devise-two-factor 3.0.0 → 4.0.0

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
- SHA1:
3
- metadata.gz: 4b3b5de2fc272e8a4cb6efed6a5767d5ebf98b7a
4
- data.tar.gz: b9f07dd6665d994fc78c3fca5baf074a2a7ecdbd
2
+ SHA256:
3
+ metadata.gz: 33ab6513476203a5a4135af19c1f3bbddeeed83fb2ed8bf3a74c2afe2e74be9b
4
+ data.tar.gz: 16068a92b6b20aa06108cb0e3cd294dc5c98c19563311d024dcfc7e0573030ad
5
5
  SHA512:
6
- metadata.gz: fdd8ddf024c2d4d336026262ea27c9660df54d6363c3aff582c500e76ae7b8bc136d1cf3f048527b5e95c899af04dc1ae4fb4c91e4323c5b47aeb6fe922df98d
7
- data.tar.gz: 11b3e866530264a4c3779fc49150df1de42658e7ead17496844d7f667d6d033c5c5fb7808d50df62a0a7afdc265e6d442f31d55ac2188d9f330662545d5c37f7
6
+ metadata.gz: 258cd2abf3bc9beb80c0f9fc596b33055efa24ca53177847fbab5a38b80a220e322a2739303b30128c356000dabd708bcc77e835dacecee2e3c9fe51b66c2b33
7
+ data.tar.gz: 940c49c9b2cbea4832ee8f66c39b26c1c6ad45d09dc054cf0c54d0be823219e18ad4ef7ca12dc388e2eec319534be0d880b5f9d3e2eb5fcace86ce6b9008e960
checksums.yaml.gz.sig CHANGED
Binary file
data.tar.gz.sig CHANGED
Binary file
data/.gitignore CHANGED
@@ -9,6 +9,8 @@
9
9
  /test/tmp/
10
10
  /test/version_tmp/
11
11
  /tmp/
12
+ /gemfiles/.bundle/
13
+ /gemfiles/*.gemfile.lock
12
14
 
13
15
  ## Specific to RubyMotion:
14
16
  .dat*
@@ -42,6 +44,7 @@ build-iPhoneSimulator/
42
44
  Gemfile.lock
43
45
  .ruby-version
44
46
  .ruby-gemset
47
+ .tool-versions
45
48
 
46
49
  # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
47
50
  .rvmrc
data/.travis.yml CHANGED
@@ -2,9 +2,45 @@ sudo: false
2
2
  language: ruby
3
3
  cache: bundler
4
4
  before_install:
5
- - gem update --system
5
+ - gem i rubygems-update -v '<3' && update_rubygems
6
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
7
15
  rvm:
8
16
  - "2.1"
9
17
  - "2.2"
10
- - "2.3.0"
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
data/Appraisals ADDED
@@ -0,0 +1,29 @@
1
+ appraise "rails-4-1" do
2
+ gem 'railties', '~> 4.1'
3
+ gem 'activesupport', '~> 4.1'
4
+ end
5
+
6
+ appraise "rails-4-2" do
7
+ gem 'railties', '~> 4.2'
8
+ gem 'activesupport', '~> 4.2'
9
+ end
10
+
11
+ appraise "rails-5-0" do
12
+ gem 'railties', '~> 5.0'
13
+ gem 'activesupport', '~> 5.0'
14
+ end
15
+
16
+ appraise "rails-5-1" do
17
+ gem 'railties', '~> 5.1'
18
+ gem 'activesupport', '~> 5.1'
19
+ end
20
+
21
+ appraise "rails-5-2" do
22
+ gem 'railties', '~> 5.2'
23
+ gem 'activesupport', '~> 5.2'
24
+ end
25
+
26
+ appraise "rails-6-0" do
27
+ gem 'railties', '~> 6.0'
28
+ gem 'activesupport', '~> 6.0'
29
+ end
data/CHANGELOG.md CHANGED
@@ -2,6 +2,27 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 4.0.0
6
+ - Update ROTP
7
+ - Add Rails 6.1 support
8
+ - Remove timecop dependency
9
+ - Clarify changes in project ownership
10
+ - Bugfixes & cleanup
11
+
12
+ ## 3.1.0
13
+ - Add Rails 6.0 support
14
+ - New gem signing certificate
15
+ - Fix paranoid-mode being ignored
16
+
17
+ ## 3.0.3
18
+ - Add Rails 5.2 support
19
+
20
+ ## 3.0.2
21
+ - Add Rails 5.1 support
22
+
23
+ ## 3.0.1
24
+ - Qualify call to rspec shared_examples
25
+
5
26
  ## 3.0.0
6
27
  See `UPGRADING.md` for specific help with breaking changes from 2.x to 3.0.0.
7
28
 
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,15 +1,18 @@
1
1
  # Devise-Two-Factor Authentication
2
- By [Tinfoil Security](http://tinfoilsecurity.com/)
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
4
  [![Build Status](https://travis-ci.org/tinfoil/devise-two-factor.svg?branch=master)](https://travis-ci.org/tinfoil/devise-two-factor)
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
10
- * Integrates easily with two-factor applications like Google Authenticator and Authy
10
+ * Integrates easily with two-factor applications like [Google Authenticator](https://support.google.com/accounts/answer/1066447?hl=en) and [Authy](https://authy.com/)
11
11
  * Is extensible, and includes two-factor backup codes as an example of how plugins can be structured
12
12
 
13
+ ## Contributing
14
+ We welcome pull requests, bug reports, and other contributions. We're especially looking for help getting this gem fully compatible with Rails 5+ and squashing any deprecation messages.
15
+
13
16
  ## Example App
14
17
  An example Rails 4 application is provided in the `demo` directory. It showcases a minimal example of Devise-Two-Factor in action, and can act as a reference for integrating the gem into your own application.
15
18
 
@@ -50,7 +53,13 @@ This generator will add a few columns to the specified model:
50
53
  * consumed_timestep
51
54
  * otp_required_for_login
52
55
 
53
- 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.
54
63
 
55
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.
56
65
 
@@ -82,7 +91,7 @@ def configure_permitted_parameters
82
91
  end
83
92
  ```
84
93
 
85
- **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.**
86
95
 
87
96
  ## Designing Your Workflow
88
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.
@@ -92,7 +101,7 @@ There are two key workflows you'll have to think about:
92
101
  1. Logging in with two-factor authentication
93
102
  2. Enabling two-factor authentication for a given user
94
103
 
95
- 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).
96
105
 
97
106
 
98
107
  ### Logging In
@@ -105,7 +114,7 @@ Logging in with two-factor authentication works extremely similarly to regular d
105
114
  These parameters can be submitted to the standard Devise login route, and the strategy will handle the authentication of the user for you.
106
115
 
107
116
  ### Disabling Automatic Login After Password Resets
108
- 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.
109
118
 
110
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`).
111
120
 
@@ -122,13 +131,30 @@ Before you can do this however, you need to decide how you're going to transmit
122
131
 
123
132
  At Tinfoil Security, we opted to use the excellent [rqrcode-rails3](https://github.com/samvincent/rqrcode-rails3) gem to generate a QR-code representing the user's secret key, which can then be scanned by any mobile two-factor authentication client.
124
133
 
134
+ If you decide to do this you'll need to generate a URI to act as the source for the QR code. This can be done using the `User#otp_provisioning_uri` method.
135
+
136
+ ```ruby
137
+ issuer = 'Your App'
138
+ label = "#{issuer}:#{current_user.email}"
139
+
140
+ current_user.otp_provisioning_uri(label, issuer: issuer)
141
+
142
+ # > "otpauth://totp/Your%20App:user@example.com?secret=[otp_secret]&issuer=Your+App"
143
+ ```
144
+
125
145
  If you instead to decide to send the one-time password to the user directly, such as via SMS, you'll need a mechanism for generating the one-time password on the server:
126
146
 
127
147
  ```ruby
128
148
  current_user.current_otp
129
149
  ```
130
150
 
131
- 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
+ ```
132
158
 
133
159
  However you decide to handle enrollment, there are a few important considerations to be made:
134
160
 
@@ -1,33 +1,35 @@
1
1
  -----BEGIN CERTIFICATE-----
2
- MIIFyjCCA7KgAwIBAgIJAK2u0LojMCNtMA0GCSqGSIb3DQEBDQUAMIGcMQswCQYD
3
- VQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVBhbG8gQWx0bzEfMB0GA1UE
4
- ChMWVGluZm9pbCBTZWN1cml0eSwgSW5jLjEfMB0GA1UEAxMWVGluZm9pbCBTZWN1
5
- cml0eSwgSW5jLjEqMCgGCSqGSIb3DQEJARYbc3VwcG9ydEB0aW5mb2lsc2VjdXJp
6
- dHkuY29tMB4XDTE0MDUyMDIxMTAwMFoXDTE2MDUyMDIxMTAwMFowgZwxCzAJBgNV
2
+ MIIGADCCA+igAwIBAgIIP4wV6YA6CO0wDQYJKoZIhvcNAQENBQAwgZwxCzAJBgNV
7
3
  BAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJUGFsbyBBbHRvMR8wHQYDVQQK
8
- ExZUaW5mb2lsIFNlY3VyaXR5LCBJbmMuMR0wGwYDVQQDExR0aW5mb2lsc2VjdXJp
9
- dHktZ2VtczEsMCoGCSqGSIb3DQEJARYdZW5naW5lZXJzQHRpbmZvaWxzZWN1cml0
10
- eS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDNJYNH8D+8lACL
11
- t3KzjEIPs3XVBCPaMm2eD/Xk9OOTuDV/NqgMK0icD9MRxMUtS3SCrC9QcPocXT76
12
- f2LQ3yVJuK+rBUasymEES47PIx2czC4n4Hga0xPPuBpioO26oaRFsobyzh9RPOIb
13
- nYfpjyqtdrbm+YyM3sPR4XzFirv9xomT4E9T4RCLgOQHTcLKL9K9m+EN7PeVdVUX
14
- V0Pa7cVs2vJUKedsd7vnr6Lzbn8ToPk/7J/4W931PbaeI5yg9ZuaRa9K2IaY1TkP
15
- I67NW4qKitBVepRlXw6Sb7TYcUncWEQ/eC5CpnOmqUrG5tfGD8cc5aGZOkitW/VX
16
- ZgVj81xgCv1hk4HjErrqq4FBNAaCSNyBfwR0TUYqg1lN1nbNjOKwfb6YRn06R2ov
17
- cFJG0tmGhsQULCr6fW8u2TfSM+U9WFSIJx2griureY7EZPwg/MgsUiWUWMFemz3G
18
- VYXWJR3dN2pW9Uqr3rkjKZbA0bstGWahJO9HuFdDakQxoaTPYPtTQDC+kskkO6lK
19
- G1KLIoZ1iLZzB1Ks1vEeyE7lp1imWgpUq+q23PFkt1gIBi/4tGvzsLZye25QU2Y+
20
- XLzldCNm+DyRFXZ+Q+bK33IveUeUWEOv4T1qTXHAOypyzmgodVRG/PrlsSMOBfE5
21
- 15kG1mDMGjRcCpEtlskgxUbf7qM7hQIDAQABow0wCzAJBgNVHRMEAjAAMA0GCSqG
22
- SIb3DQEBDQUAA4ICAQCLXzGJOr0isGHscTvm73ReEAOv4g0IOSXjfHfHAOWKhzdM
23
- g+D8nrzhy8wnARqAt2Ob4I+R9scEIfI5MPp/C5HHqWed4m0W0Uygx3V3qyixavkc
24
- nVUJMZ4TPS6W48IHdGGVD45hopx7ycFy+iPm7QUFk4sg044dO53mkScTetm2AvIE
25
- xDTotsFNpn/hrAnzXlH4MQQ7LKXAedPmFi8Jv5nJwv9BnEGJQJhvzQtdx7le4S8J
26
- a78vwAq429N2gVnfSdeU9v9QqdHOF1215lC6qaDI4bk8hVE4RyMxSBZmWKhnPLAm
27
- jFXGc6Wj2yF7HQ2YtEGAAEyjH+tAwF35STv+J3eweUFhyGZPH8Sf+b+UKZc9UF1D
28
- J/VmzQd9D9RaB+pOclDYR3Uiji5EBq1La2FPg48hA0uCd4KJ9dBGwNrxvjDNSBW0
29
- FiM1vjR6AVxIAhOwICeWG6QTvH5Jrnq/UDBnnK/KALCbFw3YbbhOyy+295jta7xf
30
- r32d4cJAvbDF1C6t2JRjNwi0ANgPw0cytl+8yvCyXpXMZpT0YpDk76XICo97SOHI
31
- 5C31v4YyRBnNCp0pN66nxYX2avEiQ8riTBP5mlkPPOhsIoYQHHe2Uj75aVpu0LZ3
32
- cdFzuO4GC1dV0Wv+dsDm+MyF7DT5E9pUPXpnMJuPvPrFpCb+wrFlszW9hGjXbQ==
4
+ ExZUaW5mb2lsIFNlY3VyaXR5LCBJbmMuMR8wHQYDVQQDExZUaW5mb2lsIFNlY3Vy
5
+ aXR5LCBJbmMuMSowKAYJKoZIhvcNAQkBFhtzdXBwb3J0QHRpbmZvaWxzZWN1cml0
6
+ eS5jb20wHhcNMjEwNDA4MTUxODAwWhcNMjExMjI0MDUwNzAwWjCBiDELMAkGA1UE
7
+ BhMCVVMxCzAJBgNVBAgTAkNBMR8wHQYDVQQKExZUaW5mb2lsIFNlY3VyaXR5LCBJ
8
+ bmMuMR0wGwYDVQQDExR0aW5mb2lsc2VjdXJpdHktZ2VtczEsMCoGCSqGSIb3DQEJ
9
+ ARYdZW5naW5lZXJzQHRpbmZvaWxzZWN1cml0eS5jb20wggIiMA0GCSqGSIb3DQEB
10
+ AQUAA4ICDwAwggIKAoICAQDNJYNH8D+8lACLt3KzjEIPs3XVBCPaMm2eD/Xk9OOT
11
+ uDV/NqgMK0icD9MRxMUtS3SCrC9QcPocXT76f2LQ3yVJuK+rBUasymEES47PIx2c
12
+ zC4n4Hga0xPPuBpioO26oaRFsobyzh9RPOIbnYfpjyqtdrbm+YyM3sPR4XzFirv9
13
+ xomT4E9T4RCLgOQHTcLKL9K9m+EN7PeVdVUXV0Pa7cVs2vJUKedsd7vnr6Lzbn8T
14
+ oPk/7J/4W931PbaeI5yg9ZuaRa9K2IaY1TkPI67NW4qKitBVepRlXw6Sb7TYcUnc
15
+ WEQ/eC5CpnOmqUrG5tfGD8cc5aGZOkitW/VXZgVj81xgCv1hk4HjErrqq4FBNAaC
16
+ SNyBfwR0TUYqg1lN1nbNjOKwfb6YRn06R2ovcFJG0tmGhsQULCr6fW8u2TfSM+U9
17
+ WFSIJx2griureY7EZPwg/MgsUiWUWMFemz3GVYXWJR3dN2pW9Uqr3rkjKZbA0bst
18
+ GWahJO9HuFdDakQxoaTPYPtTQDC+kskkO6lKG1KLIoZ1iLZzB1Ks1vEeyE7lp1im
19
+ WgpUq+q23PFkt1gIBi/4tGvzsLZye25QU2Y+XLzldCNm+DyRFXZ+Q+bK33IveUeU
20
+ WEOv4T1qTXHAOypyzmgodVRG/PrlsSMOBfE515kG1mDMGjRcCpEtlskgxUbf7qM7
21
+ hQIDAQABo1gwVjAJBgNVHRMEAjAAMEkGA1UdHwRCMEAwPqA8oDqGOGh0dHBzOi8v
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==
33
35
  -----END CERTIFICATE-----
@@ -17,23 +17,20 @@ 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'
28
- s.add_runtime_dependency 'activesupport'
24
+ s.add_runtime_dependency 'railties', '< 6.2'
25
+ s.add_runtime_dependency 'activesupport', '< 6.2'
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'
31
+ s.add_development_dependency 'appraisal'
34
32
  s.add_development_dependency 'bundler', '> 1.0'
35
33
  s.add_development_dependency 'rspec', '> 3'
36
34
  s.add_development_dependency 'simplecov'
37
35
  s.add_development_dependency 'faker'
38
- s.add_development_dependency 'timecop'
39
36
  end
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "railties", "~> 4.1"
6
+ gem "activesupport", "~> 4.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", "~> 4.2"
6
+ gem "activesupport", "~> 4.2"
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", "~> 5.0"
6
+ gem "activesupport", "~> 5.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", "~> 5.1"
6
+ gem "activesupport", "~> 5.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", "~> 5.2"
6
+ gem "activesupport", "~> 5.2"
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.0.0.rc2"
6
+ gem "activesupport", "~> 6.0.0.rc2"
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,10 @@ 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
+ if totp.verify(code, drift_behind: self.class.otp_allowed_drift, drift_ahead: self.class.otp_allowed_drift)
39
+ return consume_otp!
40
+ end
36
41
 
37
42
  false
38
43
  end
@@ -56,6 +61,7 @@ module Devise
56
61
  end
57
62
 
58
63
  def clean_up_passwords
64
+ super
59
65
  self.otp_attempt = nil
60
66
  end
61
67
 
@@ -1,4 +1,6 @@
1
- shared_examples 'two_factor_authenticatable' do
1
+ require 'cgi'
2
+
3
+ RSpec.shared_examples 'two_factor_authenticatable' do
2
4
  before :each do
3
5
  subject.otp_secret = subject.class.generate_otp_secret
4
6
  subject.consumed_timestep = nil
@@ -32,12 +34,12 @@ 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 @@ 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)
@@ -77,17 +79,17 @@ shared_examples 'two_factor_authenticatable' do
77
79
  end
78
80
 
79
81
  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)
82
+ otp = ROTP::TOTP.new(otp_secret).at(Time.now + subject.class.otp_allowed_drift)
81
83
  expect(subject.validate_and_consume_otp!(otp)).to be true
82
84
  end
83
85
 
84
86
  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)
87
+ otp = ROTP::TOTP.new(otp_secret).at(Time.now + subject.class.otp_allowed_drift * 2)
86
88
  expect(subject.validate_and_consume_otp!(otp)).to be false
87
89
  end
88
90
 
89
91
  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)
92
+ otp = ROTP::TOTP.new(otp_secret).at(Time.now - subject.class.otp_allowed_drift * 2)
91
93
  expect(subject.validate_and_consume_otp!(otp)).to be false
92
94
  end
93
95
  end
@@ -95,15 +97,15 @@ shared_examples 'two_factor_authenticatable' do
95
97
  describe '#otp_provisioning_uri' do
96
98
  let(:otp_secret_length) { subject.class.otp_secret_length }
97
99
  let(:account) { Faker::Internet.email }
98
- let(:issuer) { "Tinfoil" }
100
+ let(:issuer) { 'Tinfoil' }
99
101
 
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}}})
102
+ it 'should return uri with specified account' do
103
+ expect(subject.otp_provisioning_uri(account)).to match(%r{otpauth://totp/#{CGI.escape(account)}\?secret=\w{#{otp_secret_length}}})
102
104
  end
103
105
 
104
106
  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}(&|$)})
107
+ expect(subject.otp_provisioning_uri(account, issuer: issuer)).to match(%r{otpauth://totp/#{issuer}:#{CGI.escape(account)}\?.*secret=\w{#{otp_secret_length}}(&|$)})
108
+ expect(subject.otp_provisioning_uri(account, issuer: issuer)).to match(%r{otpauth://totp/#{issuer}:#{CGI.escape(account)}\?.*issuer=#{issuer}(&|$)})
107
109
  end
108
110
  end
109
111
  end
@@ -1,4 +1,4 @@
1
- shared_examples 'two_factor_backupable' do
1
+ RSpec.shared_examples 'two_factor_backupable' do
2
2
  describe 'required_fields' do
3
3
  it 'has the attr_encrypted fields for otp_backup_codes' do
4
4
  expect(Devise::Models::TwoFactorBackupable.required_fields(subject.class)).to contain_exactly(:otp_backup_codes)
@@ -12,7 +12,7 @@ module Devise
12
12
  super
13
13
  end
14
14
 
15
- fail(:not_found_in_database) unless resource
15
+ fail(Devise.paranoid ? :invalid : :not_found_in_database) unless resource
16
16
 
17
17
  # We want to cascade to the next strategy if this one fails,
18
18
  # but database authenticatable automatically halts on a bad password
@@ -12,7 +12,7 @@ module Devise
12
12
  super
13
13
  end
14
14
 
15
- fail(:not_found_in_database) unless resource
15
+ fail(Devise.paranoid ? :invalid : :not_found_in_database) unless resource
16
16
 
17
17
  # We want to cascade to the next strategy if this one fails,
18
18
  # but database authenticatable automatically halts on a bad password
@@ -1,3 +1,3 @@
1
1
  module DeviseTwoFactor
2
- VERSION = '3.0.0'.freeze
2
+ VERSION = '4.0.0'.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,14 +19,15 @@ $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'
24
+ require 'active_support/testing/time_helpers'
25
25
 
26
26
  # Requires supporting files with custom matchers and macros, etc,
27
27
  # in ./support/ and its subdirectories.
28
28
  Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
29
29
 
30
30
  RSpec.configure do |config|
31
+ config.include ActiveSupport::Testing::TimeHelpers
31
32
  config.order = 'random'
32
33
  end
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: 3.0.0
4
+ version: 4.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shane Wilton
@@ -52,68 +52,70 @@ cert_chain:
52
52
  -----END CERTIFICATE-----
53
53
  - |
54
54
  -----BEGIN CERTIFICATE-----
55
- MIIFyjCCA7KgAwIBAgIJAK2u0LojMCNtMA0GCSqGSIb3DQEBDQUAMIGcMQswCQYD
56
- VQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVBhbG8gQWx0bzEfMB0GA1UE
57
- ChMWVGluZm9pbCBTZWN1cml0eSwgSW5jLjEfMB0GA1UEAxMWVGluZm9pbCBTZWN1
58
- cml0eSwgSW5jLjEqMCgGCSqGSIb3DQEJARYbc3VwcG9ydEB0aW5mb2lsc2VjdXJp
59
- dHkuY29tMB4XDTE0MDUyMDIxMTAwMFoXDTE2MDUyMDIxMTAwMFowgZwxCzAJBgNV
55
+ MIIGADCCA+igAwIBAgIIP4wV6YA6CO0wDQYJKoZIhvcNAQENBQAwgZwxCzAJBgNV
60
56
  BAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJUGFsbyBBbHRvMR8wHQYDVQQK
61
- ExZUaW5mb2lsIFNlY3VyaXR5LCBJbmMuMR0wGwYDVQQDExR0aW5mb2lsc2VjdXJp
62
- dHktZ2VtczEsMCoGCSqGSIb3DQEJARYdZW5naW5lZXJzQHRpbmZvaWxzZWN1cml0
63
- eS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDNJYNH8D+8lACL
64
- t3KzjEIPs3XVBCPaMm2eD/Xk9OOTuDV/NqgMK0icD9MRxMUtS3SCrC9QcPocXT76
65
- f2LQ3yVJuK+rBUasymEES47PIx2czC4n4Hga0xPPuBpioO26oaRFsobyzh9RPOIb
66
- nYfpjyqtdrbm+YyM3sPR4XzFirv9xomT4E9T4RCLgOQHTcLKL9K9m+EN7PeVdVUX
67
- V0Pa7cVs2vJUKedsd7vnr6Lzbn8ToPk/7J/4W931PbaeI5yg9ZuaRa9K2IaY1TkP
68
- I67NW4qKitBVepRlXw6Sb7TYcUncWEQ/eC5CpnOmqUrG5tfGD8cc5aGZOkitW/VX
69
- ZgVj81xgCv1hk4HjErrqq4FBNAaCSNyBfwR0TUYqg1lN1nbNjOKwfb6YRn06R2ov
70
- cFJG0tmGhsQULCr6fW8u2TfSM+U9WFSIJx2griureY7EZPwg/MgsUiWUWMFemz3G
71
- VYXWJR3dN2pW9Uqr3rkjKZbA0bstGWahJO9HuFdDakQxoaTPYPtTQDC+kskkO6lK
72
- G1KLIoZ1iLZzB1Ks1vEeyE7lp1imWgpUq+q23PFkt1gIBi/4tGvzsLZye25QU2Y+
73
- XLzldCNm+DyRFXZ+Q+bK33IveUeUWEOv4T1qTXHAOypyzmgodVRG/PrlsSMOBfE5
74
- 15kG1mDMGjRcCpEtlskgxUbf7qM7hQIDAQABow0wCzAJBgNVHRMEAjAAMA0GCSqG
75
- SIb3DQEBDQUAA4ICAQCLXzGJOr0isGHscTvm73ReEAOv4g0IOSXjfHfHAOWKhzdM
76
- g+D8nrzhy8wnARqAt2Ob4I+R9scEIfI5MPp/C5HHqWed4m0W0Uygx3V3qyixavkc
77
- nVUJMZ4TPS6W48IHdGGVD45hopx7ycFy+iPm7QUFk4sg044dO53mkScTetm2AvIE
78
- xDTotsFNpn/hrAnzXlH4MQQ7LKXAedPmFi8Jv5nJwv9BnEGJQJhvzQtdx7le4S8J
79
- a78vwAq429N2gVnfSdeU9v9QqdHOF1215lC6qaDI4bk8hVE4RyMxSBZmWKhnPLAm
80
- jFXGc6Wj2yF7HQ2YtEGAAEyjH+tAwF35STv+J3eweUFhyGZPH8Sf+b+UKZc9UF1D
81
- J/VmzQd9D9RaB+pOclDYR3Uiji5EBq1La2FPg48hA0uCd4KJ9dBGwNrxvjDNSBW0
82
- FiM1vjR6AVxIAhOwICeWG6QTvH5Jrnq/UDBnnK/KALCbFw3YbbhOyy+295jta7xf
83
- r32d4cJAvbDF1C6t2JRjNwi0ANgPw0cytl+8yvCyXpXMZpT0YpDk76XICo97SOHI
84
- 5C31v4YyRBnNCp0pN66nxYX2avEiQ8riTBP5mlkPPOhsIoYQHHe2Uj75aVpu0LZ3
85
- cdFzuO4GC1dV0Wv+dsDm+MyF7DT5E9pUPXpnMJuPvPrFpCb+wrFlszW9hGjXbQ==
57
+ ExZUaW5mb2lsIFNlY3VyaXR5LCBJbmMuMR8wHQYDVQQDExZUaW5mb2lsIFNlY3Vy
58
+ aXR5LCBJbmMuMSowKAYJKoZIhvcNAQkBFhtzdXBwb3J0QHRpbmZvaWxzZWN1cml0
59
+ eS5jb20wHhcNMjEwNDA4MTUxODAwWhcNMjExMjI0MDUwNzAwWjCBiDELMAkGA1UE
60
+ BhMCVVMxCzAJBgNVBAgTAkNBMR8wHQYDVQQKExZUaW5mb2lsIFNlY3VyaXR5LCBJ
61
+ bmMuMR0wGwYDVQQDExR0aW5mb2lsc2VjdXJpdHktZ2VtczEsMCoGCSqGSIb3DQEJ
62
+ ARYdZW5naW5lZXJzQHRpbmZvaWxzZWN1cml0eS5jb20wggIiMA0GCSqGSIb3DQEB
63
+ AQUAA4ICDwAwggIKAoICAQDNJYNH8D+8lACLt3KzjEIPs3XVBCPaMm2eD/Xk9OOT
64
+ uDV/NqgMK0icD9MRxMUtS3SCrC9QcPocXT76f2LQ3yVJuK+rBUasymEES47PIx2c
65
+ zC4n4Hga0xPPuBpioO26oaRFsobyzh9RPOIbnYfpjyqtdrbm+YyM3sPR4XzFirv9
66
+ xomT4E9T4RCLgOQHTcLKL9K9m+EN7PeVdVUXV0Pa7cVs2vJUKedsd7vnr6Lzbn8T
67
+ oPk/7J/4W931PbaeI5yg9ZuaRa9K2IaY1TkPI67NW4qKitBVepRlXw6Sb7TYcUnc
68
+ WEQ/eC5CpnOmqUrG5tfGD8cc5aGZOkitW/VXZgVj81xgCv1hk4HjErrqq4FBNAaC
69
+ SNyBfwR0TUYqg1lN1nbNjOKwfb6YRn06R2ovcFJG0tmGhsQULCr6fW8u2TfSM+U9
70
+ WFSIJx2griureY7EZPwg/MgsUiWUWMFemz3GVYXWJR3dN2pW9Uqr3rkjKZbA0bst
71
+ GWahJO9HuFdDakQxoaTPYPtTQDC+kskkO6lKG1KLIoZ1iLZzB1Ks1vEeyE7lp1im
72
+ WgpUq+q23PFkt1gIBi/4tGvzsLZye25QU2Y+XLzldCNm+DyRFXZ+Q+bK33IveUeU
73
+ WEOv4T1qTXHAOypyzmgodVRG/PrlsSMOBfE515kG1mDMGjRcCpEtlskgxUbf7qM7
74
+ hQIDAQABo1gwVjAJBgNVHRMEAjAAMEkGA1UdHwRCMEAwPqA8oDqGOGh0dHBzOi8v
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==
86
88
  -----END CERTIFICATE-----
87
- date: 2016-05-19 00:00:00.000000000 Z
89
+ date: 2021-04-08 00:00:00.000000000 Z
88
90
  dependencies:
89
91
  - !ruby/object:Gem::Dependency
90
92
  name: railties
91
93
  requirement: !ruby/object:Gem::Requirement
92
94
  requirements:
93
- - - ">="
95
+ - - "<"
94
96
  - !ruby/object:Gem::Version
95
- version: '0'
97
+ version: '6.2'
96
98
  type: :runtime
97
99
  prerelease: false
98
100
  version_requirements: !ruby/object:Gem::Requirement
99
101
  requirements:
100
- - - ">="
102
+ - - "<"
101
103
  - !ruby/object:Gem::Version
102
- version: '0'
104
+ version: '6.2'
103
105
  - !ruby/object:Gem::Dependency
104
106
  name: activesupport
105
107
  requirement: !ruby/object:Gem::Requirement
106
108
  requirements:
107
- - - ">="
109
+ - - "<"
108
110
  - !ruby/object:Gem::Version
109
- version: '0'
111
+ version: '6.2'
110
112
  type: :runtime
111
113
  prerelease: false
112
114
  version_requirements: !ruby/object:Gem::Requirement
113
115
  requirements:
114
- - - ">="
116
+ - - "<"
115
117
  - !ruby/object:Gem::Version
116
- version: '0'
118
+ version: '6.2'
117
119
  - !ruby/object:Gem::Dependency
118
120
  name: attr_encrypted
119
121
  requirement: !ruby/object:Gem::Requirement
@@ -121,12 +123,12 @@ dependencies:
121
123
  - - ">="
122
124
  - !ruby/object:Gem::Version
123
125
  version: '1.3'
124
- - - "<"
125
- - !ruby/object:Gem::Version
126
- version: '4'
127
126
  - - "!="
128
127
  - !ruby/object:Gem::Version
129
128
  version: '2'
129
+ - - "<"
130
+ - !ruby/object:Gem::Version
131
+ version: '4'
130
132
  type: :runtime
131
133
  prerelease: false
132
134
  version_requirements: !ruby/object:Gem::Requirement
@@ -134,12 +136,12 @@ dependencies:
134
136
  - - ">="
135
137
  - !ruby/object:Gem::Version
136
138
  version: '1.3'
137
- - - "<"
138
- - !ruby/object:Gem::Version
139
- version: '4'
140
139
  - - "!="
141
140
  - !ruby/object:Gem::Version
142
141
  version: '2'
142
+ - - "<"
143
+ - !ruby/object:Gem::Version
144
+ version: '4'
143
145
  - !ruby/object:Gem::Dependency
144
146
  name: devise
145
147
  requirement: !ruby/object:Gem::Requirement
@@ -160,14 +162,14 @@ dependencies:
160
162
  requirements:
161
163
  - - "~>"
162
164
  - !ruby/object:Gem::Version
163
- version: '2.0'
165
+ version: '6.0'
164
166
  type: :runtime
165
167
  prerelease: false
166
168
  version_requirements: !ruby/object:Gem::Requirement
167
169
  requirements:
168
170
  - - "~>"
169
171
  - !ruby/object:Gem::Version
170
- version: '2.0'
172
+ version: '6.0'
171
173
  - !ruby/object:Gem::Dependency
172
174
  name: activemodel
173
175
  requirement: !ruby/object:Gem::Requirement
@@ -182,6 +184,20 @@ dependencies:
182
184
  - - ">="
183
185
  - !ruby/object:Gem::Version
184
186
  version: '0'
187
+ - !ruby/object:Gem::Dependency
188
+ name: appraisal
189
+ requirement: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - ">="
192
+ - !ruby/object:Gem::Version
193
+ version: '0'
194
+ type: :development
195
+ prerelease: false
196
+ version_requirements: !ruby/object:Gem::Requirement
197
+ requirements:
198
+ - - ">="
199
+ - !ruby/object:Gem::Version
200
+ version: '0'
185
201
  - !ruby/object:Gem::Dependency
186
202
  name: bundler
187
203
  requirement: !ruby/object:Gem::Requirement
@@ -238,20 +254,6 @@ dependencies:
238
254
  - - ">="
239
255
  - !ruby/object:Gem::Version
240
256
  version: '0'
241
- - !ruby/object:Gem::Dependency
242
- name: timecop
243
- requirement: !ruby/object:Gem::Requirement
244
- requirements:
245
- - - ">="
246
- - !ruby/object:Gem::Version
247
- version: '0'
248
- type: :development
249
- prerelease: false
250
- version_requirements: !ruby/object:Gem::Requirement
251
- requirements:
252
- - - ">="
253
- - !ruby/object:Gem::Version
254
- version: '0'
255
257
  description: Barebones two-factor authentication with Devise
256
258
  email: engineers@tinfoilsecurity.com
257
259
  executables: []
@@ -261,6 +263,7 @@ files:
261
263
  - ".gitignore"
262
264
  - ".rspec"
263
265
  - ".travis.yml"
266
+ - Appraisals
264
267
  - CHANGELOG.md
265
268
  - CONTRIBUTING.md
266
269
  - Gemfile
@@ -271,6 +274,12 @@ files:
271
274
  - certs/tinfoil-cacert.pem
272
275
  - certs/tinfoilsecurity-gems-cert.pem
273
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
274
283
  - lib/devise-two-factor.rb
275
284
  - lib/devise_two_factor/models.rb
276
285
  - lib/devise_two_factor/models/two_factor_authenticatable.rb
@@ -305,8 +314,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
305
314
  - !ruby/object:Gem::Version
306
315
  version: '0'
307
316
  requirements: []
308
- rubyforge_project: devise-two-factor
309
- rubygems_version: 2.6.3
317
+ rubygems_version: 3.0.3
310
318
  signing_key:
311
319
  specification_version: 4
312
320
  summary: Barebones two-factor authentication with Devise
metadata.gz.sig CHANGED
Binary file