devise-two-factor 3.1.0 → 4.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 12a32c4d2067b65a19376f7d98480e89975091a9b57eddd01b7bd8a036eae7bb
4
- data.tar.gz: 46c843c0e7aeaa0e2432a667195bef8f92eabfc1e7940c9f9e067a3c08fe8797
3
+ metadata.gz: 976317c79975d3526dea8a30d6b7ded62de0d218bc89add6db0cac627b75e057
4
+ data.tar.gz: 758d0b728bdda8132be4c2f3459cce4bd61a42e548b36b34c5ee07fc8c38f4d8
5
5
  SHA512:
6
- metadata.gz: 6de2e060514bfcea44bde7fde4c63bc8ab5f0ad934f4d69e3224fdf9534e9c452abc7bb6994d964cd0de61a244ba57e7c400979e07b81c3c2a93d1745aef997d
7
- data.tar.gz: 867896645165eca78879f02ace0acffedd86f1b32cd38e221630ac9e3bf7f47fec71d6cfa90f5fb16b7a2241b63ca004a33ac598bb60df98bd21601bfda5e16f
6
+ metadata.gz: 510c99d55da2f9c66533c968c4d7c4681a14bb81a3ba4624fb3c5f61c542ff5bf68683e5234d618a12aeb5398a2016144b8b5222b01a812e8ab6e48332b75264
7
+ data.tar.gz: 7e68943b8d191f0b0b27f05f51a55b1a47ae85d487b44db22e4d74810eab29faffac01f6b9f6a8afa9c158eede9286611d2e8eafcf138f0ce0292a3cb2d4895a
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,6 +2,28 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 4.0.1
6
+ - Convert CI from Travis CI to Github Actions ([#198](https://github.com/tinfoil/devise-two-factor/pull/198))
7
+ - Fix ActiveSupport::Testing::TimeHelpers require in shared examples ([#191](https://github.com/tinfoil/devise-two-factor/pull/191))
8
+ - Accept whitespace in provided codes ([#195](https://github.com/tinfoil/devise-two-factor/pull/195))
9
+ - Add Truffleruby head to CI ([#200](https://github.com/tinfoil/devise-two-factor/pull/200))
10
+
11
+ ## 4.0.0
12
+ - [breaking] Drop support for Ruby <= 2.2
13
+ - Update ROTP
14
+ - Add Rails 6.1 support
15
+ - Remove timecop dependency
16
+ - Clarify changes in project ownership
17
+ - Bugfixes & cleanup
18
+
19
+ ## 3.1.0
20
+ - Add Rails 6.0 support
21
+ - New gem signing certificate
22
+ - Fix paranoid-mode being ignored
23
+
24
+ ## 3.0.3
25
+ - Add Rails 5.2 support
26
+
5
27
  ## 3.0.2
6
28
  - Add Rails 5.1 support
7
29
 
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2014 Tinfoil Security, Inc.
3
+ Copyright (c) 2014 Synopsys, Inc.
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  # Devise-Two-Factor Authentication
2
- By [Tinfoil Security](https://www.tinfoilsecurity.com/). Interested in [working with us](https://www.tinfoilsecurity.com/jobs)? We're hiring!
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
- 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_Algorithm) scheme. It:
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
 
8
8
  * Allows you to incorporate two-factor authentication into your existing models
9
9
  * Is opinionated about security, so you don't have to be
@@ -53,7 +53,13 @@ This generator will add a few columns to the specified model:
53
53
  * consumed_timestep
54
54
  * otp_required_for_login
55
55
 
56
- It also adds the :two_factor_authenticatable directive to your model, and sets up your encryption key. If present, it will remove :database_authenticatable from the model, as the two strategies are incompatible. Lastly, the generator will add a Warden config block to your Devise initializer, which enables the strategies required for two-factor authentication.
56
+ Remember to apply the new migration.
57
+
58
+ ```ruby
59
+ bundle exec rake db:migrate
60
+ ```
61
+
62
+ It also adds the `:two_factor_authenticatable` directive to your model, and sets up your encryption key. If present, it will remove `:database_authenticatable` from the model, as the two strategies are incompatible. Lastly, the generator will add a Warden config block to your Devise initializer, which enables the strategies required for two-factor authentication.
57
63
 
58
64
  If you're running Rails 3, or do not have strong parameters enabled, the generator will also setup the required mass-assignment security options in your model.
59
65
 
@@ -85,7 +91,7 @@ def configure_permitted_parameters
85
91
  end
86
92
  ```
87
93
 
88
- **After running the generator, verify that :database_authenticatable is not being loaded by your model. The generator will try to remove it, but if you have a non-standard Devise setup, this step may fail. Loading both :database_authenticatable and `:two_factor_authenticatable` in a model will allow users to bypass two-factor authenticatable due to the way Warden handles cascading strategies.**
94
+ **After running the generator, verify that `:database_authenticatable` is not being loaded by your model. The generator will try to remove it, but if you have a non-standard Devise setup, this step may fail. Loading both `:database_authenticatable` and `:two_factor_authenticatable` in a model will allow users to bypass two-factor authenticatable due to the way Warden handles cascading strategies.**
89
95
 
90
96
  ## Designing Your Workflow
91
97
  Devise-Two-Factor only worries about the backend, leaving the details of the integration up to you. This means that you're responsible for building the UI that drives the gem. While there is an example Rails application included in the gem, it is important to remember that this gem is intentionally very open-ended, and you should build a user experience which fits your individual application.
@@ -95,7 +101,7 @@ There are two key workflows you'll have to think about:
95
101
  1. Logging in with two-factor authentication
96
102
  2. Enabling two-factor authentication for a given user
97
103
 
98
- We chose to keep things as simple as possible, and our implementation can be found by registering at [Tinfoil Security](https://tinfoilsecurity.com/), and enabling two-factor authentication from the [security settings page](https://www.tinfoilsecurity.com/account/security).
104
+ We chose to keep things as simple as possible, and our implementation can be found by registering at [Tinfoil Security](https://www.tinfoilsecurity.com/), and enabling two-factor authentication from the [security settings page](https://www.tinfoilsecurity.com/account/security).
99
105
 
100
106
 
101
107
  ### Logging In
@@ -108,7 +114,7 @@ Logging in with two-factor authentication works extremely similarly to regular d
108
114
  These parameters can be submitted to the standard Devise login route, and the strategy will handle the authentication of the user for you.
109
115
 
110
116
  ### Disabling Automatic Login After Password Resets
111
- If you use the Devise ```recoverable``` strategy, the default behavior after a password reset is to automatically authenticate the user and log them in. This is obviously a problem if a user has two-factor authentication enabled, as resetting the password would get around the two-factor requirement.
117
+ If you use the Devise `recoverable` strategy, the default behavior after a password reset is to automatically authenticate the user and log them in. This is obviously a problem if a user has two-factor authentication enabled, as resetting the password would get around the two-factor requirement.
112
118
 
113
119
  Because of this, you need to set `sign_in_after_reset_password` to `false` (either globally in your Devise initializer or via `devise_for`).
114
120
 
@@ -142,7 +148,13 @@ If you instead to decide to send the one-time password to the user directly, suc
142
148
  current_user.current_otp
143
149
  ```
144
150
 
145
- The generated code will be valid for the duration specified by `otp_allowed_drift`.
151
+ The generated code will be valid for the duration specified by `otp_allowed_drift`. This value can be modified by adding a config in `config/initializers/devise.rb`.
152
+ ```ruby
153
+ Devise.otp_allowed_drift = 240 # value in seconds
154
+ Devise.setup do |config|
155
+ ...
156
+ end
157
+ ```
146
158
 
147
159
  However you decide to handle enrollment, there are a few important considerations to be made:
148
160
 
@@ -227,3 +239,20 @@ require 'devise_two_factor/spec_helpers'
227
239
  it_behaves_like "two_factor_authenticatable"
228
240
  it_behaves_like "two_factor_backupable"
229
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+igAwIBAgIIBr+qGSyQe7MwDQYJKoZIhvcNAQENBQAwgZwxCzAJBgNV
2
+ MIIGADCCA+igAwIBAgIIHIF9ta6cW3YwDQYJKoZIhvcNAQENBQAwgZwxCzAJBgNV
3
3
  BAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJUGFsbyBBbHRvMR8wHQYDVQQK
4
4
  ExZUaW5mb2lsIFNlY3VyaXR5LCBJbmMuMR8wHQYDVQQDExZUaW5mb2lsIFNlY3Vy
5
5
  aXR5LCBJbmMuMSowKAYJKoZIhvcNAQkBFhtzdXBwb3J0QHRpbmZvaWxzZWN1cml0
6
- eS5jb20wHhcNMTkwODA3MTkwMjAwWhcNMjAwODA3MTkwMjAwWjCBiDELMAkGA1UE
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
- MA0GCSqGSIb3DQEBDQUAA4ICAQAiW9gcR0TTaxsK1DaHZ9U9VxP0dhtWKyTcjDnE
24
- BHlaayjrEmmQHmEtDEFOkLYOo20LpMFaogxcSHBYto5Z3XYfU9ZPuaY6rGVFhXeq
25
- mrQn6O0iOxzNOHLSZTgEjQTc2gJg1p1CYSxapla53ivW5v01dNcS1XJE/HFHQttI
26
- gXdNb53g7XW/c1Be5MvtQKZHsYGNANE+NUUOIFMp29PI1iXBVZVm2PSYL4XlGgQi
27
- 2cg3tU2TK6+wLul5ZMMvNrOgjaWcbqgW976hUAE03OgR5GDy4M3LfYOs8OJNsktG
28
- dtpfft6WroUoiqzDCNz85fbLqhY0vwPNJxK/wFHvxTJ2MKL/BdOha8gIV2o5HPTC
29
- cBpZx2C8FyUKAZfuXwTVkMtPbhFowjPNQN4TNl5N6DRtLnb1PDn9LOXHCPd+0V2b
30
- ssxu8B1+kf9IyrKBhlqKWSoLdaPboz5FunBUj6nkbXz2VlCkqbRIAFaTtmMJjjWR
31
- I0Fgx1CAbDjnDvf/hYrjPAvwLOcQ117Tm3Wk/3MP78CwmZoFHRrBw68Ir5mcLrQy
32
- L71AbBqGd3i+azsNXMmm11ZZetYzbIy6kc9g9MFHd+3xooJpU7eRpm+tyRLKX/qu
33
- /9iBhU0HVz2daNTk8LdQtzGqff8Dj0qB4sBndVsTW1JxKRP8E3jeLBT4EBGsy9OL
34
- 5aBXTA==
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-----
@@ -17,18 +17,15 @@ Gem::Specification.new do |s|
17
17
  'certs/tinfoilsecurity-gems-cert.pem'
18
18
  ]
19
19
  s.signing_key = File.expand_path("~/.ssh/tinfoilsecurity-gems-key.pem") if $0 =~ /gem\z/
20
-
21
- s.rubyforge_project = 'devise-two-factor'
22
-
23
20
  s.files = `git ls-files`.split("\n").delete_if { |x| x.match('demo/*') }
24
21
  s.test_files = `git ls-files -- spec/*`.split("\n")
25
22
  s.require_paths = ['lib']
26
23
 
27
- s.add_runtime_dependency 'railties', '< 6.1'
28
- s.add_runtime_dependency 'activesupport', '< 6.1'
24
+ s.add_runtime_dependency 'railties', '< 7.1'
25
+ s.add_runtime_dependency 'activesupport', '< 7.1'
29
26
  s.add_runtime_dependency 'attr_encrypted', '>= 1.3', '< 4', '!= 2'
30
27
  s.add_runtime_dependency 'devise', '~> 4.0'
31
- s.add_runtime_dependency 'rotp', '~> 2.0'
28
+ s.add_runtime_dependency 'rotp', '~> 6.0'
32
29
 
33
30
  s.add_development_dependency 'activemodel'
34
31
  s.add_development_dependency 'appraisal'
@@ -36,5 +33,4 @@ Gem::Specification.new do |s|
36
33
  s.add_development_dependency 'rspec', '> 3'
37
34
  s.add_development_dependency 'simplecov'
38
35
  s.add_development_dependency 'faker'
39
- s.add_development_dependency 'timecop'
40
36
  end
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -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: "../"
@@ -1,4 +1,3 @@
1
- require 'attr_encrypted'
2
1
  require 'rotp'
3
2
 
4
3
  module Devise
@@ -8,14 +7,18 @@ module Devise
8
7
  include Devise::Models::DatabaseAuthenticatable
9
8
 
10
9
  included do
11
- unless singleton_class.ancestors.include?(AttrEncrypted)
12
- extend AttrEncrypted
13
- end
14
-
15
- unless attr_encrypted?(:otp_secret)
16
- attr_encrypted :otp_secret,
17
- :key => self.otp_secret_encryption_key,
18
- :mode => :per_attribute_iv_and_salt unless self.attr_encrypted?(:otp_secret)
10
+ unless %i[otp_secret otp_secret=].all? { |attr| method_defined?(attr) }
11
+ require 'attr_encrypted'
12
+
13
+ unless singleton_class.ancestors.include?(AttrEncrypted)
14
+ extend AttrEncrypted
15
+ end
16
+
17
+ unless attr_encrypted?(:otp_secret)
18
+ attr_encrypted :otp_secret,
19
+ :key => self.otp_secret_encryption_key,
20
+ :mode => :per_attribute_iv_and_salt unless self.attr_encrypted?(:otp_secret)
21
+ end
19
22
  end
20
23
 
21
24
  attr_accessor :otp_attempt
@@ -31,8 +34,16 @@ module Devise
31
34
  otp_secret = options[:otp_secret] || self.otp_secret
32
35
  return false unless code.present? && otp_secret.present?
33
36
 
34
- totp = self.otp(otp_secret)
35
- return consume_otp! if totp.verify_with_drift(code, self.class.otp_allowed_drift)
37
+ totp = otp(otp_secret)
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)
45
+ return consume_otp!
46
+ end
36
47
 
37
48
  false
38
49
  end
@@ -56,6 +67,7 @@ module Devise
56
67
  end
57
68
 
58
69
  def clean_up_passwords
70
+ super
59
71
  self.otp_attempt = nil
60
72
  end
61
73
 
@@ -1,3 +1,5 @@
1
+ require 'cgi'
2
+
1
3
  RSpec.shared_examples 'two_factor_authenticatable' do
2
4
  before :each do
3
5
  subject.otp_secret = subject.class.generate_otp_secret
@@ -32,12 +34,12 @@ RSpec.shared_examples 'two_factor_authenticatable' do
32
34
  let(:otp_secret) { '2z6hxkdwi3uvrnpn' }
33
35
 
34
36
  before :each do
35
- Timecop.freeze(Time.current)
37
+ travel_to(Time.now)
36
38
  subject.otp_secret = otp_secret
37
39
  end
38
40
 
39
41
  after :each do
40
- Timecop.return
42
+ travel_back
41
43
  end
42
44
 
43
45
  context 'with a stored consumed_timestep' do
@@ -54,7 +56,7 @@ RSpec.shared_examples 'two_factor_authenticatable' do
54
56
  end
55
57
 
56
58
  context 'given a previously valid OTP within the allowed drift' do
57
- let(:consumed_otp) { ROTP::TOTP.new(otp_secret).at(Time.now + subject.class.otp_allowed_drift, true) }
59
+ let(:consumed_otp) { ROTP::TOTP.new(otp_secret).at(Time.now + subject.class.otp_allowed_drift) }
58
60
 
59
61
  before do
60
62
  subject.validate_and_consume_otp!(consumed_otp)
@@ -64,6 +66,42 @@ RSpec.shared_examples 'two_factor_authenticatable' do
64
66
  expect(subject.validate_and_consume_otp!(consumed_otp)).to be false
65
67
  end
66
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
67
105
  end
68
106
 
69
107
  it 'validates a precisely correct OTP' do
@@ -71,23 +109,28 @@ RSpec.shared_examples 'two_factor_authenticatable' do
71
109
  expect(subject.validate_and_consume_otp!(otp)).to be true
72
110
  end
73
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
+
74
117
  it 'fails a nil OTP value' do
75
118
  otp = nil
76
119
  expect(subject.validate_and_consume_otp!(otp)).to be false
77
120
  end
78
121
 
79
122
  it 'validates an OTP within the allowed drift' do
80
- otp = ROTP::TOTP.new(otp_secret).at(Time.now + subject.class.otp_allowed_drift, true)
123
+ otp = ROTP::TOTP.new(otp_secret).at(Time.now + subject.class.otp_allowed_drift)
81
124
  expect(subject.validate_and_consume_otp!(otp)).to be true
82
125
  end
83
126
 
84
127
  it 'does not validate an OTP above the allowed drift' do
85
- otp = ROTP::TOTP.new(otp_secret).at(Time.now + subject.class.otp_allowed_drift * 2, true)
128
+ otp = ROTP::TOTP.new(otp_secret).at(Time.now + subject.class.otp_allowed_drift * 2)
86
129
  expect(subject.validate_and_consume_otp!(otp)).to be false
87
130
  end
88
131
 
89
132
  it 'does not validate an OTP below the allowed drift' do
90
- otp = ROTP::TOTP.new(otp_secret).at(Time.now - subject.class.otp_allowed_drift * 2, true)
133
+ otp = ROTP::TOTP.new(otp_secret).at(Time.now - subject.class.otp_allowed_drift * 2)
91
134
  expect(subject.validate_and_consume_otp!(otp)).to be false
92
135
  end
93
136
  end
@@ -95,15 +138,15 @@ RSpec.shared_examples 'two_factor_authenticatable' do
95
138
  describe '#otp_provisioning_uri' do
96
139
  let(:otp_secret_length) { subject.class.otp_secret_length }
97
140
  let(:account) { Faker::Internet.email }
98
- let(:issuer) { "Tinfoil" }
141
+ let(:issuer) { 'Tinfoil' }
99
142
 
100
- it "should return uri with specified account" do
101
- expect(subject.otp_provisioning_uri(account)).to match(%r{otpauth://totp/#{account}\?secret=\w{#{otp_secret_length}}})
143
+ it 'should return uri with specified account' do
144
+ expect(subject.otp_provisioning_uri(account)).to match(%r{otpauth://totp/#{CGI.escape(account)}\?secret=\w{#{otp_secret_length}}})
102
145
  end
103
146
 
104
147
  it 'should return uri with issuer option' do
105
- expect(subject.otp_provisioning_uri(account, issuer: issuer)).to match(%r{otpauth://totp/#{account}\?.*secret=\w{#{otp_secret_length}}(&|$)})
106
- expect(subject.otp_provisioning_uri(account, issuer: issuer)).to match(%r{otpauth://totp/#{account}\?.*issuer=#{issuer}(&|$)})
148
+ expect(subject.otp_provisioning_uri(account, issuer: issuer)).to match(%r{otpauth://totp/#{issuer}:#{CGI.escape(account)}\?.*secret=\w{#{otp_secret_length}}(&|$)})
149
+ expect(subject.otp_provisioning_uri(account, issuer: issuer)).to match(%r{otpauth://totp/#{issuer}:#{CGI.escape(account)}\?.*issuer=#{issuer}(&|$)})
107
150
  end
108
151
  end
109
152
  end
@@ -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 = '3.1.0'.freeze
2
+ VERSION = '4.0.2'.freeze
3
3
  end
@@ -77,3 +77,23 @@ describe ::Devise::Models::TwoFactorAuthenticatable do
77
77
  end
78
78
  end
79
79
  end
80
+
81
+ describe ::Devise::Models::TwoFactorAuthenticatable do
82
+ context 'When clean_up_passwords is called ' do
83
+ subject { TwoFactorAuthenticatableDouble.new }
84
+ before :each do
85
+ subject.otp_attempt = 'foo'
86
+ subject.password_confirmation = 'foo'
87
+ end
88
+ it 'otp_attempt should be nill' do
89
+ subject.clean_up_passwords
90
+ expect(subject.otp_attempt).to be_nil
91
+ end
92
+ it 'password_confirmation should be nill' do
93
+ subject.clean_up_passwords
94
+ expect(subject.password_confirmation).to be_nil
95
+ end
96
+ end
97
+ end
98
+
99
+
data/spec/spec_helper.rb CHANGED
@@ -19,7 +19,6 @@ $LOAD_PATH.unshift(File.dirname(__FILE__))
19
19
 
20
20
  require 'rspec'
21
21
  require 'faker'
22
- require 'timecop'
23
22
  require 'devise-two-factor'
24
23
  require 'devise_two_factor/spec_helpers'
25
24
 
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,11 +1,11 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: devise-two-factor
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.0
4
+ version: 4.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shane Wilton
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain:
11
11
  - |
@@ -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+igAwIBAgIIBr+qGSyQe7MwDQYJKoZIhvcNAQENBQAwgZwxCzAJBgNV
55
+ MIIGADCCA+igAwIBAgIIHIF9ta6cW3YwDQYJKoZIhvcNAQENBQAwgZwxCzAJBgNV
56
56
  BAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJUGFsbyBBbHRvMR8wHQYDVQQK
57
57
  ExZUaW5mb2lsIFNlY3VyaXR5LCBJbmMuMR8wHQYDVQQDExZUaW5mb2lsIFNlY3Vy
58
58
  aXR5LCBJbmMuMSowKAYJKoZIhvcNAQkBFhtzdXBwb3J0QHRpbmZvaWxzZWN1cml0
59
- eS5jb20wHhcNMTkwODA3MTkwMjAwWhcNMjAwODA3MTkwMjAwWjCBiDELMAkGA1UE
59
+ eS5jb20wHhcNMjIwMzIyMjI1MzAwWhcNMjUwOTA5MTgyMjAwWjCBiDELMAkGA1UE
60
60
  BhMCVVMxCzAJBgNVBAgTAkNBMR8wHQYDVQQKExZUaW5mb2lsIFNlY3VyaXR5LCBJ
61
61
  bmMuMR0wGwYDVQQDExR0aW5mb2lsc2VjdXJpdHktZ2VtczEsMCoGCSqGSIb3DQEJ
62
62
  ARYdZW5naW5lZXJzQHRpbmZvaWxzZWN1cml0eS5jb20wggIiMA0GCSqGSIb3DQEB
@@ -73,20 +73,20 @@ cert_chain:
73
73
  WEOv4T1qTXHAOypyzmgodVRG/PrlsSMOBfE515kG1mDMGjRcCpEtlskgxUbf7qM7
74
74
  hQIDAQABo1gwVjAJBgNVHRMEAjAAMEkGA1UdHwRCMEAwPqA8oDqGOGh0dHBzOi8v
75
75
  d3d3LnRpbmZvaWxzZWN1cml0eS5jb20vc2VjdXJpdHkvcmV2b2NhdGlvbl9saXN0
76
- MA0GCSqGSIb3DQEBDQUAA4ICAQAiW9gcR0TTaxsK1DaHZ9U9VxP0dhtWKyTcjDnE
77
- BHlaayjrEmmQHmEtDEFOkLYOo20LpMFaogxcSHBYto5Z3XYfU9ZPuaY6rGVFhXeq
78
- mrQn6O0iOxzNOHLSZTgEjQTc2gJg1p1CYSxapla53ivW5v01dNcS1XJE/HFHQttI
79
- gXdNb53g7XW/c1Be5MvtQKZHsYGNANE+NUUOIFMp29PI1iXBVZVm2PSYL4XlGgQi
80
- 2cg3tU2TK6+wLul5ZMMvNrOgjaWcbqgW976hUAE03OgR5GDy4M3LfYOs8OJNsktG
81
- dtpfft6WroUoiqzDCNz85fbLqhY0vwPNJxK/wFHvxTJ2MKL/BdOha8gIV2o5HPTC
82
- cBpZx2C8FyUKAZfuXwTVkMtPbhFowjPNQN4TNl5N6DRtLnb1PDn9LOXHCPd+0V2b
83
- ssxu8B1+kf9IyrKBhlqKWSoLdaPboz5FunBUj6nkbXz2VlCkqbRIAFaTtmMJjjWR
84
- I0Fgx1CAbDjnDvf/hYrjPAvwLOcQ117Tm3Wk/3MP78CwmZoFHRrBw68Ir5mcLrQy
85
- L71AbBqGd3i+azsNXMmm11ZZetYzbIy6kc9g9MFHd+3xooJpU7eRpm+tyRLKX/qu
86
- /9iBhU0HVz2daNTk8LdQtzGqff8Dj0qB4sBndVsTW1JxKRP8E3jeLBT4EBGsy9OL
87
- 5aBXTA==
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: 2019-08-07 00:00:00.000000000 Z
89
+ date: 2022-03-24 00:00:00.000000000 Z
90
90
  dependencies:
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: railties
@@ -94,28 +94,28 @@ dependencies:
94
94
  requirements:
95
95
  - - "<"
96
96
  - !ruby/object:Gem::Version
97
- version: '6.1'
97
+ version: '7.1'
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.1'
104
+ version: '7.1'
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.1'
111
+ version: '7.1'
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.1'
118
+ version: '7.1'
119
119
  - !ruby/object:Gem::Dependency
120
120
  name: attr_encrypted
121
121
  requirement: !ruby/object:Gem::Requirement
@@ -162,14 +162,14 @@ dependencies:
162
162
  requirements:
163
163
  - - "~>"
164
164
  - !ruby/object:Gem::Version
165
- version: '2.0'
165
+ version: '6.0'
166
166
  type: :runtime
167
167
  prerelease: false
168
168
  version_requirements: !ruby/object:Gem::Requirement
169
169
  requirements:
170
170
  - - "~>"
171
171
  - !ruby/object:Gem::Version
172
- version: '2.0'
172
+ version: '6.0'
173
173
  - !ruby/object:Gem::Dependency
174
174
  name: activemodel
175
175
  requirement: !ruby/object:Gem::Requirement
@@ -254,29 +254,15 @@ dependencies:
254
254
  - - ">="
255
255
  - !ruby/object:Gem::Version
256
256
  version: '0'
257
- - !ruby/object:Gem::Dependency
258
- name: timecop
259
- requirement: !ruby/object:Gem::Requirement
260
- requirements:
261
- - - ">="
262
- - !ruby/object:Gem::Version
263
- version: '0'
264
- type: :development
265
- prerelease: false
266
- version_requirements: !ruby/object:Gem::Requirement
267
- requirements:
268
- - - ">="
269
- - !ruby/object:Gem::Version
270
- version: '0'
271
257
  description: Barebones two-factor authentication with Devise
272
258
  email: engineers@tinfoilsecurity.com
273
259
  executables: []
274
260
  extensions: []
275
261
  extra_rdoc_files: []
276
262
  files:
263
+ - ".github/workflows/ci.yml"
277
264
  - ".gitignore"
278
265
  - ".rspec"
279
- - ".travis.yml"
280
266
  - Appraisals
281
267
  - CHANGELOG.md
282
268
  - CONTRIBUTING.md
@@ -288,12 +274,14 @@ files:
288
274
  - certs/tinfoil-cacert.pem
289
275
  - certs/tinfoilsecurity-gems-cert.pem
290
276
  - devise-two-factor.gemspec
291
- - gemfiles/rails_4_1.gemfile
292
- - gemfiles/rails_4_2.gemfile
293
- - gemfiles/rails_5_0.gemfile
294
- - gemfiles/rails_5_1.gemfile
295
- - gemfiles/rails_5_2.gemfile
296
- - 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
297
285
  - lib/devise-two-factor.rb
298
286
  - lib/devise_two_factor/models.rb
299
287
  - lib/devise_two_factor/models/two_factor_authenticatable.rb
@@ -313,7 +301,7 @@ homepage: https://github.com/tinfoil/devise-two-factor
313
301
  licenses:
314
302
  - MIT
315
303
  metadata: {}
316
- post_install_message:
304
+ post_install_message:
317
305
  rdoc_options: []
318
306
  require_paths:
319
307
  - lib
@@ -328,9 +316,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
328
316
  - !ruby/object:Gem::Version
329
317
  version: '0'
330
318
  requirements: []
331
- rubyforge_project: devise-two-factor
332
- rubygems_version: 2.7.6.2
333
- signing_key:
319
+ rubygems_version: 3.2.32
320
+ signing_key:
334
321
  specification_version: 4
335
322
  summary: Barebones two-factor authentication with Devise
336
323
  test_files:
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: "../"