rotp 5.1.0 → 6.2.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 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