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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c56412a3603b43a3371e26cfb7d9314b2469e99558ea7f58a86b3c51f7e30055
4
- data.tar.gz: 1923418367d2df594d64fb3b5810aad696a78a93ff89e4a19b655dcc4c98cff3
3
+ metadata.gz: 36079aaa87791cbc44f0772c429ebc947f88d34b0389411c13e37fc65a3bd1a1
4
+ data.tar.gz: a71126bd8dc56ca8e5db1bf5bf4fa5296c2319ac06816427400e253d0d835292
5
5
  SHA512:
6
- metadata.gz: ede75377c7c88538f4c6dc1dbe623f5ad099382a59f38245a35cde7a20f3fd17b38178ffcb0db960d7a923432dc8fe1c8eef760c58d0dcd57cb6709971fcb0e1
7
- data.tar.gz: 4bda95ccda7e78c4bb33fe57e9d79e0eda0654074e5b8b6461e9e59f25dd23bb8554e6f003f5375a1e857f126e41c6829e818c9f6bab288f583c204ed6444f49
6
+ metadata.gz: 3d90ee7ee49a029e74fe22faa8e14e14130f24a3c6551bc20742f35e7b1b529e807b56229bfb4a64f8621447920996d9a54d5fe98c1a255aae392a8dbc350d79
7
+ data.tar.gz: 12dd508477209b35bf75337934d9c780f661eebf32dc0a712acceedb7216cc2455af9781168248469c9b98c3eea27deaa961879d31464cffb793367a2aa8f529
@@ -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@google.com?secret=base32secret3232&issuer=My%20Service'
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@google.com?secret=base32secret3232&counter=0'
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
@@ -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"
@@ -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
@@ -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: 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
@@ -1,3 +1,3 @@
1
1
  module ROTP
2
- VERSION = '6.0.0'.freeze
2
+ VERSION = '6.1.0'.freeze
3
3
  end
@@ -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'
@@ -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,87 +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
-
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.0.0
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-04-23 00:00:00.000000000 Z
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.0.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
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