devise-two-factor 4.0.0 → 4.1.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of devise-two-factor might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 33ab6513476203a5a4135af19c1f3bbddeeed83fb2ed8bf3a74c2afe2e74be9b
4
- data.tar.gz: 16068a92b6b20aa06108cb0e3cd294dc5c98c19563311d024dcfc7e0573030ad
3
+ metadata.gz: 787116f2d8fd1e2ef5a6e663c0a3ed5a65ca6110a491f44d2de5eb1a985da516
4
+ data.tar.gz: f379bef9da0239c3697fe14dad33ba6c662ecfd2b6bcc3ab54fb184f75a970d4
5
5
  SHA512:
6
- metadata.gz: 258cd2abf3bc9beb80c0f9fc596b33055efa24ca53177847fbab5a38b80a220e322a2739303b30128c356000dabd708bcc77e835dacecee2e3c9fe51b66c2b33
7
- data.tar.gz: 940c49c9b2cbea4832ee8f66c39b26c1c6ad45d09dc054cf0c54d0be823219e18ad4ef7ca12dc388e2eec319534be0d880b5f9d3e2eb5fcace86ce6b9008e960
6
+ metadata.gz: 23b55432e9df42fa8723db98cd38abe641656ed05ce6f5d9e5d7ee1a1179624ba7212209c460a7ff4b16d4f14347b11c69750d795e7ff9f56513e93af651c619
7
+ data.tar.gz: ce784140d65f12c6e46a51d86ed8e97edc6b98e706f276160e67f95bccf615c308a5cf273bd54ad71788b61e5dac92f9676c4e67ebfdd81c83584fa71e0bb509
checksums.yaml.gz.sig CHANGED
Binary file
@@ -0,0 +1,51 @@
1
+ name: CI
2
+ on:
3
+ push:
4
+ branches:
5
+ - main
6
+ pull_request:
7
+
8
+ jobs:
9
+ tests:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ fail-fast: false
13
+ matrix:
14
+ # Due to https://github.com/actions/runner/issues/849, we should quote versions
15
+ ruby: ['2.3', '2.4', '2.5', '2.6', '2.7', '3.0', 'truffleruby-head']
16
+ rails: ['4.1', '4.2', '5.0', '5.1', '5.2', '6.0', '6.1', '7.0']
17
+ exclude:
18
+ - { ruby: '2.3', rails: '7.0' }
19
+ - { ruby: '2.4', rails: '7.0' }
20
+ - { ruby: '2.5', rails: '7.0' }
21
+ - { ruby: '2.6', rails: '7.0' }
22
+ - { ruby: '2.3', rails: '6.0' }
23
+ - { ruby: '2.3', rails: '6.1' }
24
+ - { ruby: '2.4', rails: '6.0' }
25
+ - { ruby: '2.4', rails: '6.1' }
26
+ - { ruby: '2.7', rails: '4.1' }
27
+ - { ruby: '2.7', rails: '4.2' }
28
+ - { ruby: '3.0', rails: '4.1' }
29
+ - { ruby: '3.0', rails: '4.2' }
30
+ - { ruby: 'truffleruby-head', rails: '4.1' }
31
+ - { ruby: 'truffleruby-head', rails: '4.2' }
32
+
33
+ name: Ruby ${{ matrix.ruby }}, Rails ${{ matrix.rails }}
34
+ env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps
35
+ BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/rails_${{ matrix.rails }}.gemfile
36
+ steps:
37
+ - uses: actions/checkout@v2
38
+ - name: Set up Ruby
39
+ uses: ruby/setup-ruby@v1
40
+ with:
41
+ ruby-version: ${{ matrix.ruby }}
42
+ bundler-cache: true
43
+ - name: Print versions
44
+ continue-on-error: true
45
+ run: |
46
+ ruby --version
47
+ bundle --version
48
+ echo "RubyGems version `gem --version`"
49
+ bundle exec rails --version
50
+ - name: Run tests
51
+ run: bundle exec rake
data/Appraisals CHANGED
@@ -1,29 +1,39 @@
1
- appraise "rails-4-1" do
1
+ appraise "rails-4.1" do
2
2
  gem 'railties', '~> 4.1'
3
3
  gem 'activesupport', '~> 4.1'
4
4
  end
5
5
 
6
- appraise "rails-4-2" do
6
+ appraise "rails-4.2" do
7
7
  gem 'railties', '~> 4.2'
8
8
  gem 'activesupport', '~> 4.2'
9
9
  end
10
10
 
11
- appraise "rails-5-0" do
11
+ appraise "rails-5.0" do
12
12
  gem 'railties', '~> 5.0'
13
13
  gem 'activesupport', '~> 5.0'
14
14
  end
15
15
 
16
- appraise "rails-5-1" do
16
+ appraise "rails-5.1" do
17
17
  gem 'railties', '~> 5.1'
18
18
  gem 'activesupport', '~> 5.1'
19
19
  end
20
20
 
21
- appraise "rails-5-2" do
21
+ appraise "rails-5.2" do
22
22
  gem 'railties', '~> 5.2'
23
23
  gem 'activesupport', '~> 5.2'
24
24
  end
25
25
 
26
- appraise "rails-6-0" do
26
+ appraise "rails-6.0" do
27
27
  gem 'railties', '~> 6.0'
28
28
  gem 'activesupport', '~> 6.0'
29
29
  end
30
+
31
+ appraise "rails-6.1" do
32
+ gem 'railties', '~> 6.1'
33
+ gem 'activesupport', '~> 6.1'
34
+ end
35
+
36
+ appraise "rails-7.0" do
37
+ gem 'railties', '~> 7.0'
38
+ gem 'activesupport', '~> 7.0'
39
+ end
data/CHANGELOG.md CHANGED
@@ -2,7 +2,19 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 4.0.2
6
+ - Add Rails 7.0 support
7
+ - Renew signing certificate
8
+ - Use `after` option of TOTP#verify for additional timestamp verification
9
+
10
+ ## 4.0.1
11
+ - Convert CI from Travis CI to Github Actions ([#198](https://github.com/tinfoil/devise-two-factor/pull/198))
12
+ - Fix ActiveSupport::Testing::TimeHelpers require in shared examples ([#191](https://github.com/tinfoil/devise-two-factor/pull/191))
13
+ - Accept whitespace in provided codes ([#195](https://github.com/tinfoil/devise-two-factor/pull/195))
14
+ - Add Truffleruby head to CI ([#200](https://github.com/tinfoil/devise-two-factor/pull/200))
15
+
5
16
  ## 4.0.0
17
+ - [breaking] Drop support for Ruby <= 2.2
6
18
  - Update ROTP
7
19
  - Add Rails 6.1 support
8
20
  - Remove timecop dependency
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Devise-Two-Factor Authentication
2
2
  By [Tinfoil Security](https://www.tinfoilsecurity.com/) (acq. [Synopsys](https://www.synopsys.com/) 2020). Interested in [working with us](https://www.synopsys.com/careers.html)? We're hiring!
3
3
 
4
- [![Build Status](https://travis-ci.org/tinfoil/devise-two-factor.svg?branch=master)](https://travis-ci.org/tinfoil/devise-two-factor)
4
+ ![Build Status](https://github.com/tinfoil/devise-two-factor/actions/workflows/ci.yml/badge.svg)
5
5
 
6
6
  Devise-Two-Factor is a minimalist extension to Devise which offers support for two-factor authentication, through the [TOTP](https://en.wikipedia.org/wiki/Time-based_One-Time_Password) scheme. It:
7
7
 
@@ -239,3 +239,20 @@ require 'devise_two_factor/spec_helpers'
239
239
  it_behaves_like "two_factor_authenticatable"
240
240
  it_behaves_like "two_factor_backupable"
241
241
  ```
242
+
243
+ ## Troubleshooting
244
+ If you are using Rails 4.x and Ruby >= 2.7, you may get an error like
245
+
246
+ ```
247
+ An error occurred while loading ./spec/devise/models/two_factor_authenticatable_spec.rb.
248
+ Failure/Error: require 'devise'
249
+
250
+ NoMethodError:
251
+ undefined method `new' for BigDecimal:Class
252
+ ```
253
+ see https://github.com/ruby/bigdecimal#which-version-should-you-select and https://github.com/ruby/bigdecimal/issues/127
254
+ for more details, but you should be able to solve this
255
+ by explicitly requiring an older version of bigdecimal in your gemfile like
256
+ ```
257
+ gem "bigdecimal", "~> 1.4"
258
+ ```
@@ -3,7 +3,7 @@ MIIHSjCCBTKgAwIBAgIJAK2u0LojMCNgMA0GCSqGSIb3DQEBBQUAMIGcMQswCQYD
3
3
  VQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVBhbG8gQWx0bzEfMB0GA1UE
4
4
  ChMWVGluZm9pbCBTZWN1cml0eSwgSW5jLjEfMB0GA1UEAxMWVGluZm9pbCBTZWN1
5
5
  cml0eSwgSW5jLjEqMCgGCSqGSIb3DQEJARYbc3VwcG9ydEB0aW5mb2lsc2VjdXJp
6
- dHkuY29tMB4XDTExMTIyNzA1MDc0N1oXDTIxMTIyNDA1MDc0N1owgZwxCzAJBgNV
6
+ dHkuY29tMB4XDTIxMDkwOTE4MjIwMFoXDTI1MDkwOTE4MjIwMFowgZwxCzAJBgNV
7
7
  BAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJUGFsbyBBbHRvMR8wHQYDVQQK
8
8
  ExZUaW5mb2lsIFNlY3VyaXR5LCBJbmMuMR8wHQYDVQQDExZUaW5mb2lsIFNlY3Vy
9
9
  aXR5LCBJbmMuMSowKAYJKoZIhvcNAQkBFhtzdXBwb3J0QHRpbmZvaWxzZWN1cml0
@@ -27,15 +27,15 @@ c3VwcG9ydEB0aW5mb2lsc2VjdXJpdHkuY29tggkAra7QuiMwI2AwDwYDVR0TAQH/
27
27
  BAUwAwEB/zARBglghkgBhvhCAQEEBAMCAQYwCQYDVR0SBAIwADArBglghkgBhvhC
28
28
  AQ0EHhYcVGlueUNBIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAmBgNVHREEHzAdgRtz
29
29
  dXBwb3J0QHRpbmZvaWxzZWN1cml0eS5jb20wDgYDVR0PAQH/BAQDAgEGMA0GCSqG
30
- SIb3DQEBBQUAA4ICAQAD7nsmdg1vStFTi8/P2rgSFxlXxZT0aaVVB1bFBe/m5phb
31
- MjvKQ7VAuiFZxEp3oBNdXTi4FzT1QjhRKdlYMgKZQnU+XDLLIYuoi+atxr5qGD4B
32
- m58eCGO6ZEutVs3Z7s63UOm5rG0zJ+IEWh8VHMvxgSwiX88QyJuhOtqeiKhIeSGZ
33
- 2/qGGMWgsScnPg3J/ZVOIKUn/4ljEDlC64Gh5Zz5PZUbGSXPMhdYbSD3EknDvEGA
34
- omYW4jlPMeK3GJgwAZu9yWC8hHGFpiMca/6W0W622cg7MX+CByOd+24dvWFnOHur
35
- NHBqI+kZo/7Sjdm8x7TWEOz9Rfh5RPMeVNRTj4iq0B6GzfaecT3Yn8y7HTRRiWns
36
- IYpP+iHCFYnZhDZsFi4ccKqxKtj6BGmhLf00FuNpgkvrsU3cXrhidkCaYGYj1SME
37
- 1CMfy0PPKVDpDKeFb6y0NvLf4d57vi99dZAvSJEO18rrNEHN2VUfCKRPA/mBSMLY
38
- RxKWAby1YVT/8iC9JWix9yvgsEUtTLyOFxLGtgj3PRiQSvbNe/jK4G9WAIFe6R9E
39
- 9+HUO2owcmyFXyU3rC/z/lBfDP+2pIRFdUVRGlYCMeUqR08PXpfva5+NQz21fC69
40
- FPRMZvXh70ntnFaWAq+j6NCss+AauC8ckECiQsTgbzJvJd6C3mJXYHkNCQODhg==
30
+ SIb3DQEBBQUAA4ICAQBZy4JJSmwLuO0nZbdr4tJeVS2P8bcGi6PzAcdzVfwzjp6n
31
+ 5qf8m4O8my4lnJieom0GrWSHQoPY1Yur4hEoZbugKO9DWZL3dTiGcrgw0TbQ6Gtq
32
+ TTPatW3LA21qFJwvohSvLqPdmZuM+H9g49sdl2kNTDVI6iUyMYuNpL14aPKPGBFo
33
+ o7UjciT1h7JtJl9b/fXrbPeRHBwpZXWeipiPGv/OZW5KnOsNlUkTquS7Zj4ETkIC
34
+ 6mVtmsLvq+YwFthFyMU37pXwYxcmqRmH6lX+XC6AVW5oO4GBmG+Zr/Z+h5Cih5ca
35
+ /mX88RkO+dGTjw1IdxKmxOqKL62OBATKrTDJ/scsmRptynA4TunYW+7ikOpDbPfL
36
+ l18aleLISlcgWJg/Czf2nmBqAClPLnhV8qxWsvt58MQQ/Jpoggvpl8EG1PylWiBS
37
+ Kc/4Ad/FKQFpTzXUgDg2kV07npVjYbBzA5p4ZSWSlflFu93jb9gg2+qtnRSImVCZ
38
+ nQjZdsv8hebElPAIbtJjSnoH1Kz2ucYLakdF1UMKnpp1PVREtuKPz/foU9KUHs0z
39
+ dWRALx8cWG4uKK9AIEUlVdGKfX0Wj0qFK0KGxl3f3jObud5Agwue2EPKWwUzEGUh
40
+ Iqp60gNw3vBdKHw4dh1bfcbXWnRDL+OQPuOFZeMWgu1QmeHeuggYtYtRg7V5Kg==
41
41
  -----END CERTIFICATE-----
@@ -1,9 +1,9 @@
1
1
  -----BEGIN CERTIFICATE-----
2
- MIIGADCCA+igAwIBAgIIP4wV6YA6CO0wDQYJKoZIhvcNAQENBQAwgZwxCzAJBgNV
2
+ MIIGADCCA+igAwIBAgIIHIF9ta6cW3YwDQYJKoZIhvcNAQENBQAwgZwxCzAJBgNV
3
3
  BAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJUGFsbyBBbHRvMR8wHQYDVQQK
4
4
  ExZUaW5mb2lsIFNlY3VyaXR5LCBJbmMuMR8wHQYDVQQDExZUaW5mb2lsIFNlY3Vy
5
5
  aXR5LCBJbmMuMSowKAYJKoZIhvcNAQkBFhtzdXBwb3J0QHRpbmZvaWxzZWN1cml0
6
- eS5jb20wHhcNMjEwNDA4MTUxODAwWhcNMjExMjI0MDUwNzAwWjCBiDELMAkGA1UE
6
+ eS5jb20wHhcNMjIwMzIyMjI1MzAwWhcNMjUwOTA5MTgyMjAwWjCBiDELMAkGA1UE
7
7
  BhMCVVMxCzAJBgNVBAgTAkNBMR8wHQYDVQQKExZUaW5mb2lsIFNlY3VyaXR5LCBJ
8
8
  bmMuMR0wGwYDVQQDExR0aW5mb2lsc2VjdXJpdHktZ2VtczEsMCoGCSqGSIb3DQEJ
9
9
  ARYdZW5naW5lZXJzQHRpbmZvaWxzZWN1cml0eS5jb20wggIiMA0GCSqGSIb3DQEB
@@ -20,16 +20,16 @@ WgpUq+q23PFkt1gIBi/4tGvzsLZye25QU2Y+XLzldCNm+DyRFXZ+Q+bK33IveUeU
20
20
  WEOv4T1qTXHAOypyzmgodVRG/PrlsSMOBfE515kG1mDMGjRcCpEtlskgxUbf7qM7
21
21
  hQIDAQABo1gwVjAJBgNVHRMEAjAAMEkGA1UdHwRCMEAwPqA8oDqGOGh0dHBzOi8v
22
22
  d3d3LnRpbmZvaWxzZWN1cml0eS5jb20vc2VjdXJpdHkvcmV2b2NhdGlvbl9saXN0
23
- MA0GCSqGSIb3DQEBDQUAA4ICAQB4p1yL6e/38Dmf5HdZoSJzQ7AcM+jrD0LdMC1V
24
- H0Y107JzZWvIB2aWH4tw4+SKGTr52OvyGFLpBv5jsWUUFssuAV971T1x41kWJSYt
25
- tnljNguSrH6ah/pDravLxi+JGQXMBRXhkdvQKbFOfutSe9HEuZLiWUYNDYM17XJq
26
- WmG+QhNgXliXgu4AQg+8vb1rDbu/G491GEuxbwaLyyKG8X+P5mYTBbMQbTgJkNfX
27
- elpmFtqivFaEHs3evVGEEZRQhe8i5V0Ak2c4Or1ap/pZQf3hUIkZbw7HumyZYNWi
28
- VJDMpObUyucv6++TNW8bAI5Oip8DGeYKibPsJ0IfYxMmRC3BmY1E3IIvAdsUHTcq
29
- WapfQlX732+mfx/gSBpuZhdwEqjWj0xPj6l9DjQrGuhUEijfucKqyY3F280OYM1b
30
- 2zG6cwVmh5IeR9nVv0i2KNkoc2zC8tcGpjfBBuDdXZCpow54DRJU4qQ6S0lH5ojs
31
- aQHEEIQ9/STv9TKuc4KlMUey8W6L0Zw+xFWnkLeygaMps1PhPokSbrABQsB4C10Q
32
- QSG/Dvvw438W/2sb9aR+skGh1oNAwJiFhLNaNALfkSXRtU16gLMPBJCi2Xqyco7V
33
- Wh4SFQHrAbuglSi0nYgFm2SxYf/r6JRKxhVkwo8wxRiV8rDZj7WmzQoZK4GHj1u6
34
- LXXw3g==
23
+ MA0GCSqGSIb3DQEBDQUAA4ICAQAiYF/m2ny/mxFvBVxHfdYuzybhCvsEUd+TSnoe
24
+ mqOWntY3sxCOaY0aGOMB4vyg9G+oP/kT4m63sD4uQxeuU7WOjaG2smCSS5q+PSWS
25
+ v63gILqPamjSyP/Om864EA6YlvVQ7nPXhVDEaiBt3iliefJGmb0wWSbbDCmq3aMb
26
+ WTLuax/IeY6MjJi20LutIcuz+VX8OxlA1hSpgAToMz3xrhA8fPt5UkKhkDkPFYBF
27
+ 5htKVipyijChWsXyt33YM2qGaavTEXzxza1I99PGNRKxUMvbSMas4YaLqkBpQSc+
28
+ mcrLWYPiXWsePGu+j08AypE2Ubp4AOSZJN9rBBGotC3gofipo+K/sBiOM9xXI76Q
29
+ 0HYOxXPa7D7UQQG1R9i0rcxmf9qepIVYCldmqVkKKDizcDo5UI9lRiLFjDyQhn6l
30
+ YFY9bPQ4lKTK5Jr3M6+dV7fHxLhqXyMGs1905IUb7qvB7Bq/f0qJfC0JZuY/qdn2
31
+ lL0SeFKOVsjErtobh3u8p8j2USkc8uJgIANHpXEMEExdp899CV/eVjh3TpAR7E6T
32
+ mg7Q9Hi6Hh8z+Le9iR4I49vPEWDQEvj35IT6VfwU79UfIOlX+DkW8fFkPbaut3Se
33
+ vqIDv6JBG9I16h/HhchntKfM58MI1bNZFBSdZqYOJiL8JIjP8HNIk76Y366ppG29
34
+ EhBYYg==
35
35
  -----END CERTIFICATE-----
@@ -21,9 +21,9 @@ Gem::Specification.new do |s|
21
21
  s.test_files = `git ls-files -- spec/*`.split("\n")
22
22
  s.require_paths = ['lib']
23
23
 
24
- s.add_runtime_dependency 'railties', '< 6.2'
25
- s.add_runtime_dependency 'activesupport', '< 6.2'
26
- s.add_runtime_dependency 'attr_encrypted', '>= 1.3', '< 4', '!= 2'
24
+ s.add_runtime_dependency 'railties', '~> 7.0'
25
+ s.add_runtime_dependency 'activesupport', '~> 7.0'
26
+ s.add_runtime_dependency 'attr_encrypted', '>= 1.3', '< 5', '!= 2'
27
27
  s.add_runtime_dependency 'devise', '~> 4.0'
28
28
  s.add_runtime_dependency 'rotp', '~> 6.0'
29
29
 
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "railties", "~> 6.0"
6
+ gem "activesupport", "~> 6.0"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "railties", "~> 6.1"
6
+ gem "activesupport", "~> 6.1"
7
+
8
+ gemspec path: "../"
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "railties", "~> 7.0"
6
+ gem "activesupport", "~> 7.0"
7
+
8
+ gemspec path: "../"
@@ -35,7 +35,13 @@ module Devise
35
35
  return false unless code.present? && otp_secret.present?
36
36
 
37
37
  totp = otp(otp_secret)
38
- if totp.verify(code, drift_behind: self.class.otp_allowed_drift, drift_ahead: self.class.otp_allowed_drift)
38
+
39
+ if self.consumed_timestep
40
+ # reconstruct the timestamp of the last consumed timestep
41
+ after_timestamp = self.consumed_timestep * otp.interval
42
+ end
43
+
44
+ if totp.verify(code.gsub(/\s+/, ""), drift_behind: self.class.otp_allowed_drift, drift_ahead: self.class.otp_allowed_drift, after: after_timestamp)
39
45
  return consume_otp!
40
46
  end
41
47
 
@@ -66,6 +66,42 @@ RSpec.shared_examples 'two_factor_authenticatable' do
66
66
  expect(subject.validate_and_consume_otp!(consumed_otp)).to be false
67
67
  end
68
68
  end
69
+
70
+ context 'given a valid OTP used multiple times within the allowed drift' do
71
+ let(:consumed_otp) { ROTP::TOTP.new(otp_secret).at(Time.now) }
72
+
73
+ before do
74
+ subject.validate_and_consume_otp!(consumed_otp)
75
+ end
76
+
77
+ context 'after the otp interval' do
78
+ before do
79
+ travel_to(subject.otp.interval.seconds.from_now)
80
+ end
81
+
82
+ it 'fails to validate' do
83
+ expect(subject.validate_and_consume_otp!(consumed_otp)).to be false
84
+ end
85
+ end
86
+ end
87
+
88
+ context 'given a valid OTP used multiple times within the allowed drift after a subsequent login' do
89
+ let(:consumed_otp) { ROTP::TOTP.new(otp_secret).at(Time.now - subject.class.otp_allowed_drift) }
90
+
91
+ before do
92
+ travel_to(subject.class.otp_allowed_drift.seconds.ago)
93
+ subject.validate_and_consume_otp!(consumed_otp)
94
+ end
95
+
96
+ context 'after the otp interval' do
97
+ it 'fails to validate' do
98
+ travel_to(subject.class.otp_allowed_drift.seconds.from_now)
99
+ next_otp = ROTP::TOTP.new(otp_secret).at(Time.now)
100
+ expect(subject.validate_and_consume_otp!(next_otp)).to be true
101
+ expect(subject.validate_and_consume_otp!(consumed_otp)).to be false
102
+ end
103
+ end
104
+ end
69
105
  end
70
106
 
71
107
  it 'validates a precisely correct OTP' do
@@ -73,6 +109,11 @@ RSpec.shared_examples 'two_factor_authenticatable' do
73
109
  expect(subject.validate_and_consume_otp!(otp)).to be true
74
110
  end
75
111
 
112
+ it 'validates a precisely correct OTP with whitespace' do
113
+ otp = ROTP::TOTP.new(otp_secret).at(Time.now)
114
+ expect(subject.validate_and_consume_otp!(otp.split("").join(" "))).to be true
115
+ end
116
+
76
117
  it 'fails a nil OTP value' do
77
118
  otp = nil
78
119
  expect(subject.validate_and_consume_otp!(otp)).to be false
@@ -1,2 +1,8 @@
1
+ require 'active_support/testing/time_helpers'
2
+
1
3
  require 'devise_two_factor/spec_helpers/two_factor_authenticatable_shared_examples'
2
4
  require 'devise_two_factor/spec_helpers/two_factor_backupable_shared_examples'
5
+
6
+ RSpec.configure do |config|
7
+ config.include ActiveSupport::Testing::TimeHelpers
8
+ end
@@ -1,3 +1,3 @@
1
1
  module DeviseTwoFactor
2
- VERSION = '4.0.0'.freeze
2
+ VERSION = '4.1.1'.freeze
3
3
  end
@@ -64,15 +64,31 @@ describe ::Devise::Models::TwoFactorAuthenticatable do
64
64
 
65
65
  describe 'otp_secret options' do
66
66
  it 'should be of the key' do
67
- expect(subject.encrypted_attributes[:otp_secret][:key]).to eq('test-key'*8)
67
+ if attr_encrypted_is_rails_seven_compatible?
68
+ expect(subject.attr_encrypted_encrypted_attributes[:otp_secret][:key]).to eq('test-key'*8)
69
+ else
70
+ expect(subject.encrypted_attributes[:otp_secret][:key]).to eq('test-key'*8)
71
+ end
68
72
  end
69
73
 
70
74
  it 'should be of the mode' do
71
- expect(subject.encrypted_attributes[:otp_secret][:mode]).to eq(:per_attribute_iv_and_salt)
75
+ if attr_encrypted_is_rails_seven_compatible?
76
+ expect(subject.attr_encrypted_encrypted_attributes[:otp_secret][:mode]).to eq(:per_attribute_iv_and_salt)
77
+ else
78
+ expect(subject.encrypted_attributes[:otp_secret][:mode]).to eq(:per_attribute_iv_and_salt)
79
+ end
72
80
  end
73
81
 
74
82
  it 'should be of the mode' do
75
- expect(subject.encrypted_attributes[:otp_secret][:algorithm]).to eq('aes-256-cbc')
83
+ if attr_encrypted_is_rails_seven_compatible?
84
+ expect(subject.attr_encrypted_encrypted_attributes[:otp_secret][:algorithm]).to eq('aes-256-cbc')
85
+ else
86
+ expect(subject.encrypted_attributes[:otp_secret][:algorithm]).to eq('aes-256-cbc')
87
+ end
88
+ end
89
+
90
+ def attr_encrypted_is_rails_seven_compatible?
91
+ Gem::Version.new(AttrEncrypted::Version.string) >= Gem::Version.new('4.0.0')
76
92
  end
77
93
  end
78
94
  end
data/spec/spec_helper.rb CHANGED
@@ -21,13 +21,11 @@ require 'rspec'
21
21
  require 'faker'
22
22
  require 'devise-two-factor'
23
23
  require 'devise_two_factor/spec_helpers'
24
- require 'active_support/testing/time_helpers'
25
24
 
26
25
  # Requires supporting files with custom matchers and macros, etc,
27
26
  # in ./support/ and its subdirectories.
28
27
  Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
29
28
 
30
29
  RSpec.configure do |config|
31
- config.include ActiveSupport::Testing::TimeHelpers
32
30
  config.order = 'random'
33
31
  end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: devise-two-factor
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0
4
+ version: 4.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shane Wilton
@@ -14,7 +14,7 @@ cert_chain:
14
14
  VQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVBhbG8gQWx0bzEfMB0GA1UE
15
15
  ChMWVGluZm9pbCBTZWN1cml0eSwgSW5jLjEfMB0GA1UEAxMWVGluZm9pbCBTZWN1
16
16
  cml0eSwgSW5jLjEqMCgGCSqGSIb3DQEJARYbc3VwcG9ydEB0aW5mb2lsc2VjdXJp
17
- dHkuY29tMB4XDTExMTIyNzA1MDc0N1oXDTIxMTIyNDA1MDc0N1owgZwxCzAJBgNV
17
+ dHkuY29tMB4XDTIxMDkwOTE4MjIwMFoXDTI1MDkwOTE4MjIwMFowgZwxCzAJBgNV
18
18
  BAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJUGFsbyBBbHRvMR8wHQYDVQQK
19
19
  ExZUaW5mb2lsIFNlY3VyaXR5LCBJbmMuMR8wHQYDVQQDExZUaW5mb2lsIFNlY3Vy
20
20
  aXR5LCBJbmMuMSowKAYJKoZIhvcNAQkBFhtzdXBwb3J0QHRpbmZvaWxzZWN1cml0
@@ -38,25 +38,25 @@ cert_chain:
38
38
  BAUwAwEB/zARBglghkgBhvhCAQEEBAMCAQYwCQYDVR0SBAIwADArBglghkgBhvhC
39
39
  AQ0EHhYcVGlueUNBIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAmBgNVHREEHzAdgRtz
40
40
  dXBwb3J0QHRpbmZvaWxzZWN1cml0eS5jb20wDgYDVR0PAQH/BAQDAgEGMA0GCSqG
41
- SIb3DQEBBQUAA4ICAQAD7nsmdg1vStFTi8/P2rgSFxlXxZT0aaVVB1bFBe/m5phb
42
- MjvKQ7VAuiFZxEp3oBNdXTi4FzT1QjhRKdlYMgKZQnU+XDLLIYuoi+atxr5qGD4B
43
- m58eCGO6ZEutVs3Z7s63UOm5rG0zJ+IEWh8VHMvxgSwiX88QyJuhOtqeiKhIeSGZ
44
- 2/qGGMWgsScnPg3J/ZVOIKUn/4ljEDlC64Gh5Zz5PZUbGSXPMhdYbSD3EknDvEGA
45
- omYW4jlPMeK3GJgwAZu9yWC8hHGFpiMca/6W0W622cg7MX+CByOd+24dvWFnOHur
46
- NHBqI+kZo/7Sjdm8x7TWEOz9Rfh5RPMeVNRTj4iq0B6GzfaecT3Yn8y7HTRRiWns
47
- IYpP+iHCFYnZhDZsFi4ccKqxKtj6BGmhLf00FuNpgkvrsU3cXrhidkCaYGYj1SME
48
- 1CMfy0PPKVDpDKeFb6y0NvLf4d57vi99dZAvSJEO18rrNEHN2VUfCKRPA/mBSMLY
49
- RxKWAby1YVT/8iC9JWix9yvgsEUtTLyOFxLGtgj3PRiQSvbNe/jK4G9WAIFe6R9E
50
- 9+HUO2owcmyFXyU3rC/z/lBfDP+2pIRFdUVRGlYCMeUqR08PXpfva5+NQz21fC69
51
- FPRMZvXh70ntnFaWAq+j6NCss+AauC8ckECiQsTgbzJvJd6C3mJXYHkNCQODhg==
41
+ SIb3DQEBBQUAA4ICAQBZy4JJSmwLuO0nZbdr4tJeVS2P8bcGi6PzAcdzVfwzjp6n
42
+ 5qf8m4O8my4lnJieom0GrWSHQoPY1Yur4hEoZbugKO9DWZL3dTiGcrgw0TbQ6Gtq
43
+ TTPatW3LA21qFJwvohSvLqPdmZuM+H9g49sdl2kNTDVI6iUyMYuNpL14aPKPGBFo
44
+ o7UjciT1h7JtJl9b/fXrbPeRHBwpZXWeipiPGv/OZW5KnOsNlUkTquS7Zj4ETkIC
45
+ 6mVtmsLvq+YwFthFyMU37pXwYxcmqRmH6lX+XC6AVW5oO4GBmG+Zr/Z+h5Cih5ca
46
+ /mX88RkO+dGTjw1IdxKmxOqKL62OBATKrTDJ/scsmRptynA4TunYW+7ikOpDbPfL
47
+ l18aleLISlcgWJg/Czf2nmBqAClPLnhV8qxWsvt58MQQ/Jpoggvpl8EG1PylWiBS
48
+ Kc/4Ad/FKQFpTzXUgDg2kV07npVjYbBzA5p4ZSWSlflFu93jb9gg2+qtnRSImVCZ
49
+ nQjZdsv8hebElPAIbtJjSnoH1Kz2ucYLakdF1UMKnpp1PVREtuKPz/foU9KUHs0z
50
+ dWRALx8cWG4uKK9AIEUlVdGKfX0Wj0qFK0KGxl3f3jObud5Agwue2EPKWwUzEGUh
51
+ Iqp60gNw3vBdKHw4dh1bfcbXWnRDL+OQPuOFZeMWgu1QmeHeuggYtYtRg7V5Kg==
52
52
  -----END CERTIFICATE-----
53
53
  - |
54
54
  -----BEGIN CERTIFICATE-----
55
- MIIGADCCA+igAwIBAgIIP4wV6YA6CO0wDQYJKoZIhvcNAQENBQAwgZwxCzAJBgNV
55
+ MIIGADCCA+igAwIBAgIIHIF9ta6cW3YwDQYJKoZIhvcNAQENBQAwgZwxCzAJBgNV
56
56
  BAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJUGFsbyBBbHRvMR8wHQYDVQQK
57
57
  ExZUaW5mb2lsIFNlY3VyaXR5LCBJbmMuMR8wHQYDVQQDExZUaW5mb2lsIFNlY3Vy
58
58
  aXR5LCBJbmMuMSowKAYJKoZIhvcNAQkBFhtzdXBwb3J0QHRpbmZvaWxzZWN1cml0
59
- eS5jb20wHhcNMjEwNDA4MTUxODAwWhcNMjExMjI0MDUwNzAwWjCBiDELMAkGA1UE
59
+ eS5jb20wHhcNMjIwMzIyMjI1MzAwWhcNMjUwOTA5MTgyMjAwWjCBiDELMAkGA1UE
60
60
  BhMCVVMxCzAJBgNVBAgTAkNBMR8wHQYDVQQKExZUaW5mb2lsIFNlY3VyaXR5LCBJ
61
61
  bmMuMR0wGwYDVQQDExR0aW5mb2lsc2VjdXJpdHktZ2VtczEsMCoGCSqGSIb3DQEJ
62
62
  ARYdZW5naW5lZXJzQHRpbmZvaWxzZWN1cml0eS5jb20wggIiMA0GCSqGSIb3DQEB
@@ -73,49 +73,49 @@ cert_chain:
73
73
  WEOv4T1qTXHAOypyzmgodVRG/PrlsSMOBfE515kG1mDMGjRcCpEtlskgxUbf7qM7
74
74
  hQIDAQABo1gwVjAJBgNVHRMEAjAAMEkGA1UdHwRCMEAwPqA8oDqGOGh0dHBzOi8v
75
75
  d3d3LnRpbmZvaWxzZWN1cml0eS5jb20vc2VjdXJpdHkvcmV2b2NhdGlvbl9saXN0
76
- MA0GCSqGSIb3DQEBDQUAA4ICAQB4p1yL6e/38Dmf5HdZoSJzQ7AcM+jrD0LdMC1V
77
- H0Y107JzZWvIB2aWH4tw4+SKGTr52OvyGFLpBv5jsWUUFssuAV971T1x41kWJSYt
78
- tnljNguSrH6ah/pDravLxi+JGQXMBRXhkdvQKbFOfutSe9HEuZLiWUYNDYM17XJq
79
- WmG+QhNgXliXgu4AQg+8vb1rDbu/G491GEuxbwaLyyKG8X+P5mYTBbMQbTgJkNfX
80
- elpmFtqivFaEHs3evVGEEZRQhe8i5V0Ak2c4Or1ap/pZQf3hUIkZbw7HumyZYNWi
81
- VJDMpObUyucv6++TNW8bAI5Oip8DGeYKibPsJ0IfYxMmRC3BmY1E3IIvAdsUHTcq
82
- WapfQlX732+mfx/gSBpuZhdwEqjWj0xPj6l9DjQrGuhUEijfucKqyY3F280OYM1b
83
- 2zG6cwVmh5IeR9nVv0i2KNkoc2zC8tcGpjfBBuDdXZCpow54DRJU4qQ6S0lH5ojs
84
- aQHEEIQ9/STv9TKuc4KlMUey8W6L0Zw+xFWnkLeygaMps1PhPokSbrABQsB4C10Q
85
- QSG/Dvvw438W/2sb9aR+skGh1oNAwJiFhLNaNALfkSXRtU16gLMPBJCi2Xqyco7V
86
- Wh4SFQHrAbuglSi0nYgFm2SxYf/r6JRKxhVkwo8wxRiV8rDZj7WmzQoZK4GHj1u6
87
- LXXw3g==
76
+ MA0GCSqGSIb3DQEBDQUAA4ICAQAiYF/m2ny/mxFvBVxHfdYuzybhCvsEUd+TSnoe
77
+ mqOWntY3sxCOaY0aGOMB4vyg9G+oP/kT4m63sD4uQxeuU7WOjaG2smCSS5q+PSWS
78
+ v63gILqPamjSyP/Om864EA6YlvVQ7nPXhVDEaiBt3iliefJGmb0wWSbbDCmq3aMb
79
+ WTLuax/IeY6MjJi20LutIcuz+VX8OxlA1hSpgAToMz3xrhA8fPt5UkKhkDkPFYBF
80
+ 5htKVipyijChWsXyt33YM2qGaavTEXzxza1I99PGNRKxUMvbSMas4YaLqkBpQSc+
81
+ mcrLWYPiXWsePGu+j08AypE2Ubp4AOSZJN9rBBGotC3gofipo+K/sBiOM9xXI76Q
82
+ 0HYOxXPa7D7UQQG1R9i0rcxmf9qepIVYCldmqVkKKDizcDo5UI9lRiLFjDyQhn6l
83
+ YFY9bPQ4lKTK5Jr3M6+dV7fHxLhqXyMGs1905IUb7qvB7Bq/f0qJfC0JZuY/qdn2
84
+ lL0SeFKOVsjErtobh3u8p8j2USkc8uJgIANHpXEMEExdp899CV/eVjh3TpAR7E6T
85
+ mg7Q9Hi6Hh8z+Le9iR4I49vPEWDQEvj35IT6VfwU79UfIOlX+DkW8fFkPbaut3Se
86
+ vqIDv6JBG9I16h/HhchntKfM58MI1bNZFBSdZqYOJiL8JIjP8HNIk76Y366ppG29
87
+ EhBYYg==
88
88
  -----END CERTIFICATE-----
89
- date: 2021-04-08 00:00:00.000000000 Z
89
+ date: 2023-10-12 00:00:00.000000000 Z
90
90
  dependencies:
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: railties
93
93
  requirement: !ruby/object:Gem::Requirement
94
94
  requirements:
95
- - - "<"
95
+ - - "~>"
96
96
  - !ruby/object:Gem::Version
97
- version: '6.2'
97
+ version: '7.0'
98
98
  type: :runtime
99
99
  prerelease: false
100
100
  version_requirements: !ruby/object:Gem::Requirement
101
101
  requirements:
102
- - - "<"
102
+ - - "~>"
103
103
  - !ruby/object:Gem::Version
104
- version: '6.2'
104
+ version: '7.0'
105
105
  - !ruby/object:Gem::Dependency
106
106
  name: activesupport
107
107
  requirement: !ruby/object:Gem::Requirement
108
108
  requirements:
109
- - - "<"
109
+ - - "~>"
110
110
  - !ruby/object:Gem::Version
111
- version: '6.2'
111
+ version: '7.0'
112
112
  type: :runtime
113
113
  prerelease: false
114
114
  version_requirements: !ruby/object:Gem::Requirement
115
115
  requirements:
116
- - - "<"
116
+ - - "~>"
117
117
  - !ruby/object:Gem::Version
118
- version: '6.2'
118
+ version: '7.0'
119
119
  - !ruby/object:Gem::Dependency
120
120
  name: attr_encrypted
121
121
  requirement: !ruby/object:Gem::Requirement
@@ -128,7 +128,7 @@ dependencies:
128
128
  version: '2'
129
129
  - - "<"
130
130
  - !ruby/object:Gem::Version
131
- version: '4'
131
+ version: '5'
132
132
  type: :runtime
133
133
  prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
@@ -141,7 +141,7 @@ dependencies:
141
141
  version: '2'
142
142
  - - "<"
143
143
  - !ruby/object:Gem::Version
144
- version: '4'
144
+ version: '5'
145
145
  - !ruby/object:Gem::Dependency
146
146
  name: devise
147
147
  requirement: !ruby/object:Gem::Requirement
@@ -260,9 +260,9 @@ executables: []
260
260
  extensions: []
261
261
  extra_rdoc_files: []
262
262
  files:
263
+ - ".github/workflows/ci.yml"
263
264
  - ".gitignore"
264
265
  - ".rspec"
265
- - ".travis.yml"
266
266
  - Appraisals
267
267
  - CHANGELOG.md
268
268
  - CONTRIBUTING.md
@@ -274,12 +274,14 @@ files:
274
274
  - certs/tinfoil-cacert.pem
275
275
  - certs/tinfoilsecurity-gems-cert.pem
276
276
  - devise-two-factor.gemspec
277
- - gemfiles/rails_4_1.gemfile
278
- - gemfiles/rails_4_2.gemfile
279
- - gemfiles/rails_5_0.gemfile
280
- - gemfiles/rails_5_1.gemfile
281
- - gemfiles/rails_5_2.gemfile
282
- - gemfiles/rails_6_0.gemfile
277
+ - gemfiles/rails_4.1.gemfile
278
+ - gemfiles/rails_4.2.gemfile
279
+ - gemfiles/rails_5.0.gemfile
280
+ - gemfiles/rails_5.1.gemfile
281
+ - gemfiles/rails_5.2.gemfile
282
+ - gemfiles/rails_6.0.gemfile
283
+ - gemfiles/rails_6.1.gemfile
284
+ - gemfiles/rails_7.0.gemfile
283
285
  - lib/devise-two-factor.rb
284
286
  - lib/devise_two_factor/models.rb
285
287
  - lib/devise_two_factor/models/two_factor_authenticatable.rb
@@ -314,7 +316,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
314
316
  - !ruby/object:Gem::Version
315
317
  version: '0'
316
318
  requirements: []
317
- rubygems_version: 3.0.3
319
+ rubygems_version: 3.0.3.1
318
320
  signing_key:
319
321
  specification_version: 4
320
322
  summary: Barebones two-factor authentication with Devise
metadata.gz.sig CHANGED
Binary file
data/.travis.yml DELETED
@@ -1,46 +0,0 @@
1
- sudo: false
2
- language: ruby
3
- cache: bundler
4
- before_install:
5
- - gem i rubygems-update -v '<3' && update_rubygems
6
- - gem update bundler
7
- gemfile:
8
- - Gemfile
9
- - gemfiles/rails_4_1.gemfile
10
- - gemfiles/rails_4_2.gemfile
11
- - gemfiles/rails_5_0.gemfile
12
- - gemfiles/rails_5_1.gemfile
13
- - gemfiles/rails_5_2.gemfile
14
- - gemfiles/rails_6_0.gemfile
15
- rvm:
16
- - "2.1"
17
- - "2.2"
18
- - "2.3.4"
19
- - "2.4.0"
20
- - "2.4.1"
21
- - "2.5"
22
- - "2.6"
23
- matrix:
24
- exclude:
25
- - rvm: "2.1"
26
- gemfile: gemfiles/rails_5_0.gemfile
27
- - rvm: "2.2"
28
- gemfile: gemfiles/rails_5_0.gemfile
29
- - rvm: "2.1"
30
- gemfile: gemfiles/rails_5_1.gemfile
31
- - rvm: "2.2"
32
- gemfile: gemfiles/rails_5_1.gemfile
33
- - rvm: "2.1"
34
- gemfile: gemfiles/rails_5_2.gemfile
35
- - rvm: "2.2"
36
- gemfile: gemfiles/rails_5_2.gemfile
37
- - rvm: "2.1"
38
- gemfile: gemfiles/rails_6_0.gemfile
39
- - rvm: "2.2"
40
- gemfile: gemfiles/rails_6_0.gemfile
41
- - rvm: "2.3.4"
42
- gemfile: gemfiles/rails_6_0.gemfile
43
- - rvm: "2.4.0"
44
- gemfile: gemfiles/rails_6_0.gemfile
45
- - rvm: "2.4.1"
46
- gemfile: gemfiles/rails_6_0.gemfile
@@ -1,8 +0,0 @@
1
- # This file was generated by Appraisal
2
-
3
- source "https://rubygems.org"
4
-
5
- gem "railties", "~> 6.0.0.rc2"
6
- gem "activesupport", "~> 6.0.0.rc2"
7
-
8
- gemspec path: "../"
File without changes
File without changes
File without changes
File without changes
File without changes