rotp 5.1.0 → 6.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 21758aaaffd88bc9b2042b13f269b8ea16e93aa870a02f8538d688d2290dfe6d
4
- data.tar.gz: 57e7983b8321fcf43982436b9ebc0aaad1ed5248e8a3359a6fefeafdf139527d
3
+ metadata.gz: cd976bfa6985075f5e2b76607256d0afbbdf88a82c38cd094d0eaffbb5bce4f2
4
+ data.tar.gz: 70df660f1eca3dd9efc7baa1f53061ba9af1bbb49e4bb6ead507509f6e845d38
5
5
  SHA512:
6
- metadata.gz: 58d61e11ce0ef3a2f6fc7e0e8ca577f2e93dc4c8f5bb2b880a5cb5428beccfca09723871b4d3b256d20f3c8f7246cb5b6128af9bd63ca38a039e50e10ec60314
7
- data.tar.gz: 2f47df80e1d3f4f84005183f9963f589e64a6cb1a2d35dc1cf15bb33cce707de2cf048f5569c7c778f93dbad63f619e16c530cdafdcb092bc3025fbd44e994ff
6
+ metadata.gz: 7fb326cc887a1a5614c90c492ac43b72188f75caa90fcc50c3338d129abe2efe4f67af88d018c378379806f1bef0c1d0e40fc6c683f4427f40ad411326729022
7
+ data.tar.gz: 4f913bf0693c1cead926bfe625e226fe8277323f93a552459447a792cd27b9189860ab26d3907baafff0e217430b04fb9e8b1829b0795e4c96e83062fac409fb
@@ -1,8 +1,9 @@
1
1
  language: ruby
2
2
  before_install: gem install bundler -v '<2'
3
3
  rvm:
4
+ - 2.7
4
5
  - 2.6
5
6
  - 2.5
6
- - 2.0
7
+ - 2.3
7
8
  script:
8
9
  - bundle exec rspec
@@ -1,5 +1,24 @@
1
1
  ### Changelog
2
2
 
3
+ ### 6.2.0
4
+
5
+ - Update to expand compatibility with Ruby 3. This was only a change to the
6
+ gemspec, no code changes were necessary.
7
+
8
+ ### 6.1.0
9
+
10
+ - Fixing URI encoding issues again, breaking out into it's own module
11
+ due to the complexity - closes #100 (@atcruice)
12
+ - Add docker-compose.yml to help with easier testing
13
+
14
+ ### 6.0.0
15
+
16
+ - Dropping support for Ruby <2.3 (Major version bump)
17
+ - Fix issue when using --enable-frozen-string-literal Ruby option #95 (jeremyevans)
18
+ - URI Encoding fix #94 (ksuh90)
19
+ - Update gems (rake, addressable)
20
+ - Update Travis tests to include Ruby 2.7
21
+
3
22
  ### 5.1.0
4
23
 
5
24
  - Create `random_base32` to perform `random` to avoid breaking changes
@@ -0,0 +1,10 @@
1
+ FROM ruby:2.3
2
+
3
+ RUN mkdir -p /usr/src/app
4
+ WORKDIR /usr/src/app
5
+
6
+ COPY Gemfile /usr/src/app/
7
+ COPY . /usr/src/app
8
+ RUN bundle install
9
+
10
+ CMD ["bundle", "exec", "rspec"]
@@ -1,4 +1,4 @@
1
- FROM ruby:2.0
1
+ FROM ruby:2.7
2
2
 
3
3
  RUN mkdir -p /usr/src/app
4
4
  WORKDIR /usr/src/app
@@ -0,0 +1,12 @@
1
+ FROM ruby:3.0-rc
2
+
3
+ RUN mkdir -p /usr/src/app
4
+ WORKDIR /usr/src/app
5
+
6
+ COPY Gemfile /usr/src/app/
7
+ COPY . /usr/src/app
8
+ RUN gem install bundler
9
+ RUN bundle install
10
+
11
+ CMD ["bundle", "exec", "rspec"]
12
+
data/README.md CHANGED
@@ -18,12 +18,16 @@ Many websites use this for [multi-factor authentication](https://www.youtube.com
18
18
 
19
19
  ## Breaking changes
20
20
 
21
+ ### Breaking changes in >= 6.0
22
+
23
+ - Dropping support for Ruby <2.3
24
+
21
25
  ### Breaking changes in >= 5.0
22
26
 
23
27
  - `ROTP::Base32.random_base32` is now `ROTP::Base32.random` and the argument
24
28
  has changed from secret string length to byte length to allow for more
25
- precision
26
- - Cleaned up the Base32 implementation to better match Google Authenticator's version
29
+ precision. There is an alias to allow for `random_base32` for the time being.
30
+ - Cleaned up the Base32 implementation to match Google Authenticator's version.
27
31
 
28
32
  ### Breaking changes in >= 4.0
29
33
 
@@ -66,8 +70,8 @@ hotp.at(1) # => "595254"
66
70
  hotp.at(1401) # => "259769"
67
71
 
68
72
  # OTP verified with a counter
69
- hotp.verify("316439", 1401) # => 1401
70
- hotp.verify("316439", 1402) # => nil
73
+ hotp.verify("259769", 1401) # => 1401
74
+ hotp.verify("259769", 1402) # => nil
71
75
  ```
72
76
 
73
77
  ### Preventing reuse of Time based OTP's
@@ -78,7 +82,7 @@ the interval window (default 30 seconds)
78
82
  The following is an example of this in action:
79
83
 
80
84
  ```ruby
81
- User.find(someUserID)
85
+ user = User.find(someUserID)
82
86
  totp = ROTP::TOTP.new(user.otp_secret)
83
87
  totp.now # => "492039"
84
88
 
@@ -129,10 +133,10 @@ Google Authenticator.
129
133
 
130
134
  ```ruby
131
135
  totp = ROTP::TOTP.new("base32secret3232", issuer: "My Service")
132
- totp.provisioning_uri("alice@google.com") # => 'otpauth://totp/My%20Service:alice@google.com?secret=base32secret3232&issuer=My+Service'
136
+ totp.provisioning_uri("alice@google.com") # => 'otpauth://totp/My%20Service:alice%40google.com?secret=base32secret3232&issuer=My%20Service'
133
137
 
134
138
  hotp = ROTP::HOTP.new("base32secret3232", issuer: "My Service")
135
- hotp.provisioning_uri("alice@google.com", 0) # => 'otpauth://hotp/alice@google.com?secret=base32secret3232&counter=0'
139
+ hotp.provisioning_uri("alice@google.com", 0) # => 'otpauth://hotp/alice%40google.com?secret=base32secret3232&counter=0'
136
140
  ```
137
141
 
138
142
  This can then be rendered as a QR Code which the user can scan using their mobile phone and the appropriate application.
@@ -169,6 +173,12 @@ docker build -f Dockerfile-2.6 -t rotp_2.6 .
169
173
  docker run --rm -v $(pwd):/usr/src/app rotp_2.6
170
174
  ```
171
175
 
176
+ Alternately, you may use docker-compose to run all the tests:
177
+
178
+ ```
179
+ docker-compose up
180
+ ```
181
+
172
182
  ## Executable Usage
173
183
 
174
184
  The rotp rubygem includes CLI version to help with testing and debugging
@@ -0,0 +1,37 @@
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"
31
+ ruby_3_0_rc:
32
+ build:
33
+ context: .
34
+ dockerfile: Dockerfile-3.0-rc
35
+ volumes:
36
+ - "./lib:/usr/src/app/lib"
37
+ - "./spec:/usr/src/app/spec"
@@ -1,9 +1,8 @@
1
- require 'cgi'
2
- require 'addressable'
3
- require 'securerandom'
4
1
  require 'openssl'
2
+ require 'erb'
5
3
  require 'rotp/base32'
6
4
  require 'rotp/otp'
5
+ require 'rotp/otp/uri'
7
6
  require 'rotp/hotp'
8
7
  require 'rotp/totp'
9
8
 
@@ -1,3 +1,5 @@
1
+ require 'securerandom'
2
+
1
3
  module ROTP
2
4
  class Base32
3
5
  class Base32Error < RuntimeError; end
@@ -28,7 +30,7 @@ module ROTP
28
30
 
29
31
  def encode(b)
30
32
  data = b.unpack('c*')
31
- out = ''
33
+ out = String.new
32
34
  buffer = data[0]
33
35
  idx = 1
34
36
  bits_left = 8
@@ -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
- params = {
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
@@ -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
@@ -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
@@ -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
- # The format of this URI is documented at:
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: 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
@@ -1,3 +1,3 @@
1
1
  module ROTP
2
- VERSION = '5.1.0'.freeze
2
+ VERSION = '6.2.0'.freeze
3
3
  end
@@ -4,23 +4,20 @@ Gem::Specification.new do |s|
4
4
  s.name = 'rotp'
5
5
  s.version = ROTP::VERSION
6
6
  s.platform = Gem::Platform::RUBY
7
+ s.required_ruby_version = '>= 2.3'
7
8
  s.license = 'MIT'
8
9
  s.authors = ['Mark Percival']
9
10
  s.email = ['mark@markpercival.us']
10
- s.homepage = 'http://github.com/mdp/rotp'
11
+ s.homepage = 'https://github.com/mdp/rotp'
11
12
  s.summary = 'A Ruby library for generating and verifying one time passwords'
12
13
  s.description = 'Works for both HOTP and TOTP, and includes QR Code provisioning'
13
14
 
14
- s.rubyforge_project = 'rotp'
15
-
16
15
  s.files = `git ls-files`.split("\n")
17
16
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
17
  s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
19
18
  s.require_paths = ['lib']
20
19
 
21
- s.add_runtime_dependency 'addressable', '~> 2.5'
22
-
23
- s.add_development_dependency 'rake', '~> 10.5'
20
+ s.add_development_dependency "rake", "~> 13.0"
24
21
  s.add_development_dependency 'rspec', '~> 3.5'
25
22
  s.add_development_dependency 'simplecov', '~> 0.12'
26
23
  s.add_development_dependency 'timecop', '~> 0.8'
@@ -108,29 +108,14 @@ RSpec.describe ROTP::HOTP do
108
108
  end
109
109
 
110
110
  describe '#provisioning_uri' do
111
- let(:uri) { hotp.provisioning_uri('mark@percival') }
112
- let(:params) { CGI.parse URI.parse(uri).query }
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
- context 'with non-default digits' do
129
- let(:hotp) { ROTP::HOTP.new('a' * 32, digits: 8) }
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
@@ -221,79 +221,9 @@ RSpec.describe ROTP::TOTP do
221
221
  end
222
222
 
223
223
  describe '#provisioning_uri' do
224
- let(:uri) { totp.provisioning_uri('mark@percival') }
225
- let(:params) { CGI.parse URI.parse(uri).query }
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
- end
266
-
267
- context 'with custom interval' do
268
- let(:totp) { ROTP::TOTP.new 'JBSWY3DPEHPK3PXP', interval: 60 }
269
-
270
- it 'has the correct format' do
271
- expect(uri).to match %r{\Aotpauth:\/\/totp.+}
272
- end
273
-
274
- it 'includes the secret as parameter' do
275
- expect(params['secret'].first).to eq 'JBSWY3DPEHPK3PXP'
276
- end
277
-
278
- it 'includes the interval as period parameter' do
279
- expect(params['period'].first).to eq '60'
280
- end
281
- end
282
-
283
- context 'with custom digest' do
284
- let(:totp) { ROTP::TOTP.new 'JBSWY3DPEHPK3PXP', digest: 'sha256' }
285
-
286
- it 'has the correct format' do
287
- expect(uri).to match %r{\Aotpauth:\/\/totp.+}
288
- end
289
-
290
- it 'includes the secret as parameter' do
291
- expect(params['secret'].first).to eq 'JBSWY3DPEHPK3PXP'
292
- end
293
-
294
- it 'includes the digest as algorithm parameter' do
295
- expect(params['algorithm'].first).to eq 'SHA256'
296
- end
224
+ it 'accepts the account name' do
225
+ expect(totp.provisioning_uri('mark@percival'))
226
+ .to eq 'otpauth://totp/mark%40percival?secret=JBSWY3DPEHPK3PXP'
297
227
  end
298
228
  end
299
229
 
metadata CHANGED
@@ -1,43 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rotp
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.1.0
4
+ version: 6.2.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: 2019-06-27 00:00:00.000000000 Z
11
+ date: 2020-09-30 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.5'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '2.5'
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: rake
29
15
  requirement: !ruby/object:Gem::Requirement
30
16
  requirements:
31
17
  - - "~>"
32
18
  - !ruby/object:Gem::Version
33
- version: '10.5'
19
+ version: '13.0'
34
20
  type: :development
35
21
  prerelease: false
36
22
  version_requirements: !ruby/object:Gem::Requirement
37
23
  requirements:
38
24
  - - "~>"
39
25
  - !ruby/object:Gem::Version
40
- version: '10.5'
26
+ version: '13.0'
41
27
  - !ruby/object:Gem::Dependency
42
28
  name: rspec
43
29
  requirement: !ruby/object:Gem::Requirement
@@ -92,14 +78,15 @@ files:
92
78
  - ".gitignore"
93
79
  - ".travis.yml"
94
80
  - CHANGELOG.md
95
- - Dockerfile-2.0
81
+ - Dockerfile-2.3
96
82
  - Dockerfile-2.5
97
83
  - Dockerfile-2.6
84
+ - Dockerfile-2.7
85
+ - Dockerfile-3.0-rc
98
86
  - Gemfile
99
87
  - Guardfile
100
88
  - LICENSE
101
89
  - README.md
102
- - Rakefile
103
90
  - bin/rotp
104
91
  - doc/ROTP/HOTP.html
105
92
  - doc/ROTP/OTP.html
@@ -119,12 +106,14 @@ files:
119
106
  - doc/js/jquery.js
120
107
  - doc/method_list.html
121
108
  - doc/top-level-namespace.html
109
+ - docker-compose.yml
122
110
  - lib/rotp.rb
123
111
  - lib/rotp/arguments.rb
124
112
  - lib/rotp/base32.rb
125
113
  - lib/rotp/cli.rb
126
114
  - lib/rotp/hotp.rb
127
115
  - lib/rotp/otp.rb
116
+ - lib/rotp/otp/uri.rb
128
117
  - lib/rotp/totp.rb
129
118
  - lib/rotp/version.rb
130
119
  - rotp.gemspec
@@ -132,9 +121,10 @@ files:
132
121
  - spec/lib/rotp/base32_spec.rb
133
122
  - spec/lib/rotp/cli_spec.rb
134
123
  - spec/lib/rotp/hotp_spec.rb
124
+ - spec/lib/rotp/otp/uri_spec.rb
135
125
  - spec/lib/rotp/totp_spec.rb
136
126
  - spec/spec_helper.rb
137
- homepage: http://github.com/mdp/rotp
127
+ homepage: https://github.com/mdp/rotp
138
128
  licenses:
139
129
  - MIT
140
130
  metadata: {}
@@ -146,15 +136,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
146
136
  requirements:
147
137
  - - ">="
148
138
  - !ruby/object:Gem::Version
149
- version: '0'
139
+ version: '2.3'
150
140
  required_rubygems_version: !ruby/object:Gem::Requirement
151
141
  requirements:
152
142
  - - ">="
153
143
  - !ruby/object:Gem::Version
154
144
  version: '0'
155
145
  requirements: []
156
- rubyforge_project: rotp
157
- rubygems_version: 2.7.6
146
+ rubygems_version: 3.1.2
158
147
  signing_key:
159
148
  specification_version: 4
160
149
  summary: A Ruby library for generating and verifying one time passwords
data/Rakefile DELETED
@@ -1,9 +0,0 @@
1
- require 'bundler'
2
- Bundler::GemHelper.install_tasks
3
- require 'rspec/core/rake_task'
4
-
5
- RSpec::Core::RakeTask.new(:rspec) do |spec|
6
- spec.pattern = 'spec/**/*_spec.rb'
7
- end
8
-
9
- task default: :rspec