devise-two-factor 4.0.0 → 4.1.1

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.

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