rotp 6.0.0 → 6.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +8 -2
- data/docker-compose.yml +30 -0
- data/lib/rotp.rb +2 -3
- data/lib/rotp/base32.rb +2 -0
- data/lib/rotp/hotp.rb +1 -6
- data/lib/rotp/otp.rb +0 -10
- data/lib/rotp/otp/uri.rb +79 -0
- data/lib/rotp/totp.rb +1 -13
- data/lib/rotp/version.rb +1 -1
- data/rotp.gemspec +0 -2
- data/spec/lib/rotp/hotp_spec.rb +6 -21
- data/spec/lib/rotp/otp/uri_spec.rb +99 -0
- data/spec/lib/rotp/totp_spec.rb +3 -81
- metadata +6 -18
- data/Rakefile +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 36079aaa87791cbc44f0772c429ebc947f88d34b0389411c13e37fc65a3bd1a1
|
4
|
+
data.tar.gz: a71126bd8dc56ca8e5db1bf5bf4fa5296c2319ac06816427400e253d0d835292
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3d90ee7ee49a029e74fe22faa8e14e14130f24a3c6551bc20742f35e7b1b529e807b56229bfb4a64f8621447920996d9a54d5fe98c1a255aae392a8dbc350d79
|
7
|
+
data.tar.gz: 12dd508477209b35bf75337934d9c780f661eebf32dc0a712acceedb7216cc2455af9781168248469c9b98c3eea27deaa961879d31464cffb793367a2aa8f529
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
### Changelog
|
2
2
|
|
3
|
+
### 6.1.0
|
4
|
+
|
5
|
+
- Fixing URI encoding issues again, breaking out into it's own module
|
6
|
+
due to the complexity - closes #100 (@atcruice)
|
7
|
+
- Add docker-compose.yml to help with easier testing
|
8
|
+
|
3
9
|
### 6.0.0
|
4
10
|
|
5
11
|
- Dropping support for Ruby <2.3 (Major version bump)
|
data/README.md
CHANGED
@@ -133,10 +133,10 @@ Google Authenticator.
|
|
133
133
|
|
134
134
|
```ruby
|
135
135
|
totp = ROTP::TOTP.new("base32secret3232", issuer: "My Service")
|
136
|
-
totp.provisioning_uri("alice@google.com") # => 'otpauth://totp/My%20Service:alice
|
136
|
+
totp.provisioning_uri("alice@google.com") # => 'otpauth://totp/My%20Service:alice%40google.com?secret=base32secret3232&issuer=My%20Service'
|
137
137
|
|
138
138
|
hotp = ROTP::HOTP.new("base32secret3232", issuer: "My Service")
|
139
|
-
hotp.provisioning_uri("alice@google.com", 0) # => 'otpauth://hotp/alice
|
139
|
+
hotp.provisioning_uri("alice@google.com", 0) # => 'otpauth://hotp/alice%40google.com?secret=base32secret3232&counter=0'
|
140
140
|
```
|
141
141
|
|
142
142
|
This can then be rendered as a QR Code which the user can scan using their mobile phone and the appropriate application.
|
@@ -173,6 +173,12 @@ docker build -f Dockerfile-2.6 -t rotp_2.6 .
|
|
173
173
|
docker run --rm -v $(pwd):/usr/src/app rotp_2.6
|
174
174
|
```
|
175
175
|
|
176
|
+
Alternately, you may use docker-compose to run all the tests:
|
177
|
+
|
178
|
+
```
|
179
|
+
docker-compose up
|
180
|
+
```
|
181
|
+
|
176
182
|
## Executable Usage
|
177
183
|
|
178
184
|
The rotp rubygem includes CLI version to help with testing and debugging
|
data/docker-compose.yml
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
version: "3.8"
|
2
|
+
services:
|
3
|
+
ruby_2_3:
|
4
|
+
build:
|
5
|
+
context: .
|
6
|
+
dockerfile: Dockerfile-2.3
|
7
|
+
volumes:
|
8
|
+
- "./lib:/usr/src/app/lib"
|
9
|
+
- "./spec:/usr/src/app/spec"
|
10
|
+
ruby_2_5:
|
11
|
+
build:
|
12
|
+
context: .
|
13
|
+
dockerfile: Dockerfile-2.5
|
14
|
+
volumes:
|
15
|
+
- "./lib:/usr/src/app/lib"
|
16
|
+
- "./spec:/usr/src/app/spec"
|
17
|
+
ruby_2_6:
|
18
|
+
build:
|
19
|
+
context: .
|
20
|
+
dockerfile: Dockerfile-2.6
|
21
|
+
volumes:
|
22
|
+
- "./lib:/usr/src/app/lib"
|
23
|
+
- "./spec:/usr/src/app/spec"
|
24
|
+
ruby_2_7:
|
25
|
+
build:
|
26
|
+
context: .
|
27
|
+
dockerfile: Dockerfile-2.7
|
28
|
+
volumes:
|
29
|
+
- "./lib:/usr/src/app/lib"
|
30
|
+
- "./spec:/usr/src/app/spec"
|
data/lib/rotp.rb
CHANGED
data/lib/rotp/base32.rb
CHANGED
data/lib/rotp/hotp.rb
CHANGED
@@ -25,12 +25,7 @@ module ROTP
|
|
25
25
|
# @param [Integer] initial_count starting counter value, defaults to 0
|
26
26
|
# @return [String] provisioning uri
|
27
27
|
def provisioning_uri(name, initial_count = 0)
|
28
|
-
|
29
|
-
secret: secret,
|
30
|
-
counter: initial_count,
|
31
|
-
digits: digits == DEFAULT_DIGITS ? nil : digits
|
32
|
-
}
|
33
|
-
encode_params("otpauth://hotp/#{Addressable::URI.escape(name)}", params)
|
28
|
+
OTP::URI.new(self, account_name: name, counter: initial_count).to_s
|
34
29
|
end
|
35
30
|
end
|
36
31
|
end
|
data/lib/rotp/otp.rb
CHANGED
@@ -66,16 +66,6 @@ module ROTP
|
|
66
66
|
result.reverse.join.rjust(padding, 0.chr)
|
67
67
|
end
|
68
68
|
|
69
|
-
# A very simple param encoder
|
70
|
-
def encode_params(uri, params)
|
71
|
-
params_str = String.new('?')
|
72
|
-
params.each do |k, v|
|
73
|
-
params_str << "#{k}=#{CGI.escape(v.to_s)}&" if v
|
74
|
-
end
|
75
|
-
params_str.chop!
|
76
|
-
uri + params_str
|
77
|
-
end
|
78
|
-
|
79
69
|
# constant-time compare the strings
|
80
70
|
def time_constant_compare(a, b)
|
81
71
|
return false if a.empty? || b.empty? || a.bytesize != b.bytesize
|
data/lib/rotp/otp/uri.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
module ROTP
|
2
|
+
class OTP
|
3
|
+
# https://github.com/google/google-authenticator/wiki/Key-Uri-Format
|
4
|
+
class URI
|
5
|
+
def initialize(otp, account_name:, counter: nil)
|
6
|
+
@otp = otp
|
7
|
+
@account_name = account_name
|
8
|
+
@counter = counter
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
"otpauth://#{type}/#{label}?#{parameters}"
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def algorithm
|
18
|
+
return unless %w[sha256 sha512].include?(@otp.digest)
|
19
|
+
|
20
|
+
@otp.digest.upcase
|
21
|
+
end
|
22
|
+
|
23
|
+
def counter
|
24
|
+
return if @otp.is_a?(TOTP)
|
25
|
+
fail if @counter.nil?
|
26
|
+
|
27
|
+
@counter
|
28
|
+
end
|
29
|
+
|
30
|
+
def digits
|
31
|
+
return if @otp.digits == DEFAULT_DIGITS
|
32
|
+
|
33
|
+
@otp.digits
|
34
|
+
end
|
35
|
+
|
36
|
+
def issuer
|
37
|
+
return if @otp.is_a?(HOTP)
|
38
|
+
|
39
|
+
@otp.issuer&.strip&.tr(':', '_')
|
40
|
+
end
|
41
|
+
|
42
|
+
def label
|
43
|
+
[issuer, @account_name.rstrip]
|
44
|
+
.compact
|
45
|
+
.map { |s| s.tr(':', '_') }
|
46
|
+
.map { |s| ERB::Util.url_encode(s) }
|
47
|
+
.join(':')
|
48
|
+
end
|
49
|
+
|
50
|
+
def parameters
|
51
|
+
{
|
52
|
+
secret: @otp.secret,
|
53
|
+
issuer: issuer,
|
54
|
+
algorithm: algorithm,
|
55
|
+
digits: digits,
|
56
|
+
period: period,
|
57
|
+
counter: counter,
|
58
|
+
}
|
59
|
+
.reject { |_, v| v.nil? }
|
60
|
+
.map { |k, v| "#{k}=#{ERB::Util.url_encode(v)}" }
|
61
|
+
.join('&')
|
62
|
+
end
|
63
|
+
|
64
|
+
def period
|
65
|
+
return if @otp.is_a?(HOTP)
|
66
|
+
return if @otp.interval == DEFAULT_INTERVAL
|
67
|
+
|
68
|
+
@otp.interval
|
69
|
+
end
|
70
|
+
|
71
|
+
def type
|
72
|
+
case @otp
|
73
|
+
when TOTP then 'totp'
|
74
|
+
when HOTP then 'hotp'
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/lib/rotp/totp.rb
CHANGED
@@ -54,19 +54,7 @@ module ROTP
|
|
54
54
|
# @param [String] name of the account
|
55
55
|
# @return [String] provisioning URI
|
56
56
|
def provisioning_uri(name)
|
57
|
-
|
58
|
-
# https://github.com/google/google-authenticator/wiki/Key-Uri-Format
|
59
|
-
# For compatibility the issuer appears both before that account name and also in the
|
60
|
-
# query string.
|
61
|
-
issuer_string = issuer.nil? ? '' : "#{Addressable::URI.escape(issuer)}:"
|
62
|
-
params = {
|
63
|
-
secret: secret,
|
64
|
-
period: interval == 30 ? nil : interval,
|
65
|
-
issuer: Addressable::URI.encode(issuer),
|
66
|
-
digits: digits == DEFAULT_DIGITS ? nil : digits,
|
67
|
-
algorithm: digest.casecmp('SHA1').zero? ? nil : digest.upcase
|
68
|
-
}
|
69
|
-
encode_params("otpauth://totp/#{issuer_string}#{Addressable::URI.escape(name)}", params)
|
57
|
+
OTP::URI.new(self, account_name: name).to_s
|
70
58
|
end
|
71
59
|
|
72
60
|
private
|
data/lib/rotp/version.rb
CHANGED
data/rotp.gemspec
CHANGED
@@ -17,8 +17,6 @@ Gem::Specification.new do |s|
|
|
17
17
|
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
18
18
|
s.require_paths = ['lib']
|
19
19
|
|
20
|
-
s.add_runtime_dependency 'addressable', '~> 2.7'
|
21
|
-
|
22
20
|
s.add_development_dependency "rake", "~> 13.0"
|
23
21
|
s.add_development_dependency 'rspec', '~> 3.5'
|
24
22
|
s.add_development_dependency 'simplecov', '~> 0.12'
|
data/spec/lib/rotp/hotp_spec.rb
CHANGED
@@ -108,29 +108,14 @@ RSpec.describe ROTP::HOTP do
|
|
108
108
|
end
|
109
109
|
|
110
110
|
describe '#provisioning_uri' do
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
it 'has the correct format' do
|
115
|
-
expect(uri).to match %r{\Aotpauth:\/\/hotp.+}
|
116
|
-
end
|
117
|
-
|
118
|
-
it 'includes the secret as parameter' do
|
119
|
-
expect(params['secret'].first).to eq 'a' * 32
|
120
|
-
end
|
121
|
-
|
122
|
-
context 'with default digits' do
|
123
|
-
it 'does not include digits parameter with default digits' do
|
124
|
-
expect(params['digits'].first).to be_nil
|
125
|
-
end
|
111
|
+
it 'accepts the account name' do
|
112
|
+
expect(hotp.provisioning_uri('mark@percival'))
|
113
|
+
.to eq 'otpauth://hotp/mark%40percival?secret=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&counter=0'
|
126
114
|
end
|
127
115
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
it 'includes digits parameter' do
|
132
|
-
expect(params['digits'].first).to eq '8'
|
133
|
-
end
|
116
|
+
it 'also accepts a custom counter value' do
|
117
|
+
expect(hotp.provisioning_uri('mark@percival', 17))
|
118
|
+
.to eq 'otpauth://hotp/mark%40percival?secret=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&counter=17'
|
134
119
|
end
|
135
120
|
end
|
136
121
|
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe ROTP::OTP::URI do
|
4
|
+
it 'meets basic functionality' do
|
5
|
+
otp = ROTP::TOTP.new('JBSWY3DPEHPK3PXP')
|
6
|
+
uri = described_class.new(otp, account_name: 'alice@google.com')
|
7
|
+
expect(uri.to_s).to eq 'otpauth://totp/alice%40google.com?secret=JBSWY3DPEHPK3PXP'
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'includes issuer' do
|
11
|
+
otp = ROTP::TOTP.new('JBSWY3DPEHPK3PXP', issuer: 'Example')
|
12
|
+
uri = described_class.new(otp, account_name: 'alice@google.com')
|
13
|
+
expect(uri.to_s).to eq 'otpauth://totp/Example:alice%40google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example'
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'encodes the account name' do
|
17
|
+
otp = ROTP::TOTP.new('JBSWY3DPEHPK3PXP', issuer: 'Provider1')
|
18
|
+
uri = described_class.new(otp, account_name: 'Alice Smith')
|
19
|
+
expect(uri.to_s).to eq 'otpauth://totp/Provider1:Alice%20Smith?secret=JBSWY3DPEHPK3PXP&issuer=Provider1'
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'encodes the issuer' do
|
23
|
+
otp = ROTP::TOTP.new('JBSWY3DPEHPK3PXP', issuer: 'Big Corporation')
|
24
|
+
uri = described_class.new(otp, account_name: ' alice@bigco.com')
|
25
|
+
expect(uri.to_s).to eq 'otpauth://totp/Big%20Corporation:%20alice%40bigco.com?secret=JBSWY3DPEHPK3PXP&issuer=Big%20Corporation'
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'includes non-default SHA256 algorithm' do
|
29
|
+
otp = ROTP::TOTP.new('JBSWY3DPEHPK3PXP', digest: 'sha256')
|
30
|
+
uri = described_class.new(otp, account_name: 'alice@google.com')
|
31
|
+
expect(uri.to_s).to eq 'otpauth://totp/alice%40google.com?secret=JBSWY3DPEHPK3PXP&algorithm=SHA256'
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'includes non-default SHA512 algorithm' do
|
35
|
+
otp = ROTP::TOTP.new('JBSWY3DPEHPK3PXP', digest: 'sha512')
|
36
|
+
uri = described_class.new(otp, account_name: 'alice@google.com')
|
37
|
+
expect(uri.to_s).to eq 'otpauth://totp/alice%40google.com?secret=JBSWY3DPEHPK3PXP&algorithm=SHA512'
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'includes non-default 8 digits' do
|
41
|
+
otp = ROTP::TOTP.new('JBSWY3DPEHPK3PXP', digits: 8)
|
42
|
+
uri = described_class.new(otp, account_name: 'alice@google.com')
|
43
|
+
expect(uri.to_s).to eq 'otpauth://totp/alice%40google.com?secret=JBSWY3DPEHPK3PXP&digits=8'
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'includes non-default period for TOTP' do
|
47
|
+
otp = ROTP::TOTP.new('JBSWY3DPEHPK3PXP', interval: 35)
|
48
|
+
uri = described_class.new(otp, account_name: 'alice@google.com')
|
49
|
+
expect(uri.to_s).to eq 'otpauth://totp/alice%40google.com?secret=JBSWY3DPEHPK3PXP&period=35'
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'includes non-default counter for HOTP' do
|
53
|
+
otp = ROTP::HOTP.new('JBSWY3DPEHPK3PXP')
|
54
|
+
uri = described_class.new(otp, account_name: 'alice@google.com', counter: 17)
|
55
|
+
expect(uri.to_s).to eq 'otpauth://hotp/alice%40google.com?secret=JBSWY3DPEHPK3PXP&counter=17'
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'can include all parameters' do
|
59
|
+
otp = ROTP::TOTP.new(
|
60
|
+
'HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ',
|
61
|
+
digest: 'sha512',
|
62
|
+
digits: 8,
|
63
|
+
interval: 60,
|
64
|
+
issuer: 'ACME Co',
|
65
|
+
)
|
66
|
+
uri = described_class.new(otp, account_name: 'john.doe@email.com')
|
67
|
+
expect(uri.to_s).to eq'otpauth://totp/ACME%20Co:john.doe%40email.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co&algorithm=SHA512&digits=8&period=60'
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'strips leading and trailing whitespace from the issuer' do
|
71
|
+
otp = ROTP::TOTP.new('JBSWY3DPEHPK3PXP', issuer: ' Big Corporation ')
|
72
|
+
uri = described_class.new(otp, account_name: ' alice@bigco.com')
|
73
|
+
expect(uri.to_s).to eq 'otpauth://totp/Big%20Corporation:%20alice%40bigco.com?secret=JBSWY3DPEHPK3PXP&issuer=Big%20Corporation'
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'strips trailing whitespace from the account name' do
|
77
|
+
otp = ROTP::TOTP.new('JBSWY3DPEHPK3PXP')
|
78
|
+
uri = described_class.new(otp, account_name: ' alice@google.com ')
|
79
|
+
expect(uri.to_s).to eq 'otpauth://totp/%20%20alice%40google.com?secret=JBSWY3DPEHPK3PXP'
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'replaces colons in the issuer with underscores' do
|
83
|
+
otp = ROTP::TOTP.new('JBSWY3DPEHPK3PXP', issuer: 'Big:Corporation')
|
84
|
+
uri = described_class.new(otp, account_name: 'alice@bigco.com')
|
85
|
+
expect(uri.to_s).to eq 'otpauth://totp/Big_Corporation:alice%40bigco.com?secret=JBSWY3DPEHPK3PXP&issuer=Big_Corporation'
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'replaces colons in the account name with underscores' do
|
89
|
+
otp = ROTP::TOTP.new('JBSWY3DPEHPK3PXP')
|
90
|
+
uri = described_class.new(otp, account_name: 'Alice:Smith')
|
91
|
+
expect(uri.to_s).to eq 'otpauth://totp/Alice_Smith?secret=JBSWY3DPEHPK3PXP'
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'handles email account names with sub-addressing' do
|
95
|
+
otp = ROTP::TOTP.new('JBSWY3DPEHPK3PXP')
|
96
|
+
uri = described_class.new(otp, account_name: 'alice+1234@google.com')
|
97
|
+
expect(uri.to_s).to eq 'otpauth://totp/alice%2B1234%40google.com?secret=JBSWY3DPEHPK3PXP'
|
98
|
+
end
|
99
|
+
end
|
data/spec/lib/rotp/totp_spec.rb
CHANGED
@@ -221,87 +221,9 @@ RSpec.describe ROTP::TOTP do
|
|
221
221
|
end
|
222
222
|
|
223
223
|
describe '#provisioning_uri' do
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
context 'without issuer' do
|
228
|
-
it 'has the correct format' do
|
229
|
-
expect(uri).to match %r{\Aotpauth:\/\/totp.+}
|
230
|
-
end
|
231
|
-
|
232
|
-
it 'includes the secret as parameter' do
|
233
|
-
expect(params['secret'].first).to eq 'JBSWY3DPEHPK3PXP'
|
234
|
-
end
|
235
|
-
end
|
236
|
-
|
237
|
-
context 'with default digits' do
|
238
|
-
it 'does does not include digits parameter' do
|
239
|
-
expect(params['digits'].first).to be_nil
|
240
|
-
end
|
241
|
-
end
|
242
|
-
|
243
|
-
context 'with non-default digits' do
|
244
|
-
let(:totp) { ROTP::TOTP.new 'JBSWY3DPEHPK3PXP', digits: 8 }
|
245
|
-
|
246
|
-
it 'does does not include digits parameter' do
|
247
|
-
expect(params['digits'].first).to eq '8'
|
248
|
-
end
|
249
|
-
end
|
250
|
-
|
251
|
-
context 'with issuer' do
|
252
|
-
let(:totp) { ROTP::TOTP.new 'JBSWY3DPEHPK3PXP', issuer: 'FooCo' }
|
253
|
-
|
254
|
-
it 'has the correct format' do
|
255
|
-
expect(uri).to match %r{\Aotpauth:\/\/totp/FooCo:.+}
|
256
|
-
end
|
257
|
-
|
258
|
-
it 'includes the secret as parameter' do
|
259
|
-
expect(params['secret'].first).to eq 'JBSWY3DPEHPK3PXP'
|
260
|
-
end
|
261
|
-
|
262
|
-
it 'includes the issuer as parameter' do
|
263
|
-
expect(params['issuer'].first).to eq 'FooCo'
|
264
|
-
end
|
265
|
-
|
266
|
-
context 'with spaces in issuer' do
|
267
|
-
let(:totp) { ROTP::TOTP.new 'JBSWY3DPEHPK3PXP', issuer: 'Foo Co' }
|
268
|
-
|
269
|
-
it 'includes the uri encoded issuer as parameter' do
|
270
|
-
expect(params['issuer'].first).to eq 'Foo%20Co'
|
271
|
-
end
|
272
|
-
end
|
273
|
-
end
|
274
|
-
|
275
|
-
context 'with custom interval' do
|
276
|
-
let(:totp) { ROTP::TOTP.new 'JBSWY3DPEHPK3PXP', interval: 60 }
|
277
|
-
|
278
|
-
it 'has the correct format' do
|
279
|
-
expect(uri).to match %r{\Aotpauth:\/\/totp.+}
|
280
|
-
end
|
281
|
-
|
282
|
-
it 'includes the secret as parameter' do
|
283
|
-
expect(params['secret'].first).to eq 'JBSWY3DPEHPK3PXP'
|
284
|
-
end
|
285
|
-
|
286
|
-
it 'includes the interval as period parameter' do
|
287
|
-
expect(params['period'].first).to eq '60'
|
288
|
-
end
|
289
|
-
end
|
290
|
-
|
291
|
-
context 'with custom digest' do
|
292
|
-
let(:totp) { ROTP::TOTP.new 'JBSWY3DPEHPK3PXP', digest: 'sha256' }
|
293
|
-
|
294
|
-
it 'has the correct format' do
|
295
|
-
expect(uri).to match %r{\Aotpauth:\/\/totp.+}
|
296
|
-
end
|
297
|
-
|
298
|
-
it 'includes the secret as parameter' do
|
299
|
-
expect(params['secret'].first).to eq 'JBSWY3DPEHPK3PXP'
|
300
|
-
end
|
301
|
-
|
302
|
-
it 'includes the digest as algorithm parameter' do
|
303
|
-
expect(params['algorithm'].first).to eq 'SHA256'
|
304
|
-
end
|
224
|
+
it 'accepts the account name' do
|
225
|
+
expect(totp.provisioning_uri('mark@percival'))
|
226
|
+
.to eq 'otpauth://totp/mark%40percival?secret=JBSWY3DPEHPK3PXP'
|
305
227
|
end
|
306
228
|
end
|
307
229
|
|
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rotp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 6.
|
4
|
+
version: 6.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mark Percival
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-08-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: addressable
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '2.7'
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '2.7'
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: rake
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -100,7 +86,6 @@ files:
|
|
100
86
|
- Guardfile
|
101
87
|
- LICENSE
|
102
88
|
- README.md
|
103
|
-
- Rakefile
|
104
89
|
- bin/rotp
|
105
90
|
- doc/ROTP/HOTP.html
|
106
91
|
- doc/ROTP/OTP.html
|
@@ -120,12 +105,14 @@ files:
|
|
120
105
|
- doc/js/jquery.js
|
121
106
|
- doc/method_list.html
|
122
107
|
- doc/top-level-namespace.html
|
108
|
+
- docker-compose.yml
|
123
109
|
- lib/rotp.rb
|
124
110
|
- lib/rotp/arguments.rb
|
125
111
|
- lib/rotp/base32.rb
|
126
112
|
- lib/rotp/cli.rb
|
127
113
|
- lib/rotp/hotp.rb
|
128
114
|
- lib/rotp/otp.rb
|
115
|
+
- lib/rotp/otp/uri.rb
|
129
116
|
- lib/rotp/totp.rb
|
130
117
|
- lib/rotp/version.rb
|
131
118
|
- rotp.gemspec
|
@@ -133,6 +120,7 @@ files:
|
|
133
120
|
- spec/lib/rotp/base32_spec.rb
|
134
121
|
- spec/lib/rotp/cli_spec.rb
|
135
122
|
- spec/lib/rotp/hotp_spec.rb
|
123
|
+
- spec/lib/rotp/otp/uri_spec.rb
|
136
124
|
- spec/lib/rotp/totp_spec.rb
|
137
125
|
- spec/spec_helper.rb
|
138
126
|
homepage: http://github.com/mdp/rotp
|
@@ -154,7 +142,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
154
142
|
- !ruby/object:Gem::Version
|
155
143
|
version: '0'
|
156
144
|
requirements: []
|
157
|
-
rubygems_version: 3.
|
145
|
+
rubygems_version: 3.1.2
|
158
146
|
signing_key:
|
159
147
|
specification_version: 4
|
160
148
|
summary: A Ruby library for generating and verifying one time passwords
|