rotp 6.0.0 → 6.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|