devise-two-factor 1.0.2 → 1.1.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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.travis.yml +2 -0
- data/README.md +16 -44
- data/devise-two-factor.gemspec +3 -3
- data/lib/devise_two_factor/models/two_factor_authenticatable.rb +0 -1
- data/lib/devise_two_factor/models/two_factor_backupable.rb +2 -11
- data/lib/devise_two_factor/spec_helpers/two_factor_authenticatable_shared_examples.rb +1 -1
- data/lib/devise_two_factor/spec_helpers/two_factor_backupable_shared_examples.rb +1 -1
- data/lib/devise_two_factor/strategies/two_factor_authenticatable.rb +7 -2
- data/lib/devise_two_factor/version.rb +1 -1
- data/spec/devise/models/two_factor_authenticatable_spec.rb +1 -0
- metadata +44 -51
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f606d2579650285b6cc4377b1da513596d979e3c
|
4
|
+
data.tar.gz: 36d5f5df579e84d85db796eacdb07376abdfec47
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5539b146deb18560ac1be676606a8165d860acef7661b80d73ce000d006d9a530c06121393707a0d9d91951c415703e7ece16cdda6698bf71f081267322d149f
|
7
|
+
data.tar.gz: d0c422b2f12ffdae6d507732d71fe06684fe69c2501c1a24c920b50ddfcfb037d5f69229869fbd62f54f345a43ea2788e90fb4e8942dfb20faf564d95069fed9
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -20,7 +20,7 @@ Devise-two-factor doesn't require much to get started, but there are a few prere
|
|
20
20
|
|
21
21
|
First, you'll need a Rails application setup with Devise. Visit the Devise [homepage](https://github.com/plataformatec/devise) for instructions.
|
22
22
|
|
23
|
-
Next, since devise-two-factor encrypts its secrets before storing them in the database, you'll need to generate an encryption key, and store it in an environment variable of your choice. Set the encryption key in the model that uses devise:
|
23
|
+
Next, since devise-two-factor encrypts its secrets before storing them in the database, you'll need to generate an encryption key, and store it in an environment variable of your choice. Set the encryption key in the model that uses devise:
|
24
24
|
|
25
25
|
```
|
26
26
|
devise :two_factor_authenticatable,
|
@@ -47,6 +47,20 @@ It also adds the :two_factor_authenticatable directive to your model, and sets u
|
|
47
47
|
|
48
48
|
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.
|
49
49
|
|
50
|
+
If you're running Rails 4, you'll also need to whitelist `:otp_attempt` as a permitted parameter in Devise `:sign_in` controller. You can do this by adding the following to your `application_controller.rb`
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
before_action :configure_permitted_parameters, if: :devise_controller?
|
54
|
+
|
55
|
+
...
|
56
|
+
|
57
|
+
protected
|
58
|
+
|
59
|
+
def configure_permitted_parameters
|
60
|
+
devise_parameter_sanitizer.for(:sign_in) << :otp_attempt
|
61
|
+
end
|
62
|
+
```
|
63
|
+
|
50
64
|
**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.**
|
51
65
|
|
52
66
|
## Designing Your Workflow
|
@@ -72,49 +86,7 @@ These parameters can be submitted to the standard Devise login route, and the st
|
|
72
86
|
### Disabling Automatic Login After Password Resets
|
73
87
|
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 2FA requirement.
|
74
88
|
|
75
|
-
Because of this, you need to
|
76
|
-
|
77
|
-
```ruby
|
78
|
-
# app/controllers/passwords_controller.rb
|
79
|
-
class PasswordsController < Devise::PasswordsController
|
80
|
-
# Overrides to require a user to log in after resetting the password
|
81
|
-
|
82
|
-
def update
|
83
|
-
self.resource = resource_class.reset_password_by_token(resource_params)
|
84
|
-
yield resource if block_given?
|
85
|
-
|
86
|
-
if resource.errors.empty?
|
87
|
-
resource.unlock_access! if unlockable?(resource)
|
88
|
-
flash_message = resource.active_for_authentication? ? :updated : :updated_not_active
|
89
|
-
set_flash_message(:notice, flash_message) if is_flashing_format?
|
90
|
-
|
91
|
-
# Do not automatically login if two-factor is enabled for this user.
|
92
|
-
# Remove the following three lines entirely if you want to disable
|
93
|
-
# automatic login for all users regardless, after a password reset.
|
94
|
-
unless resource.try(:otp_required_for_login?)
|
95
|
-
sign_in(resource_name, resource)
|
96
|
-
end
|
97
|
-
|
98
|
-
respond_with resource, location: after_resetting_password_path_for(resource)
|
99
|
-
else
|
100
|
-
respond_with resource
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
protected
|
105
|
-
|
106
|
-
def after_resetting_password_path_for(resource)
|
107
|
-
new_session_path(resource)
|
108
|
-
end
|
109
|
-
end
|
110
|
-
```
|
111
|
-
|
112
|
-
And then tell Devise to use your new controller instead of the default:
|
113
|
-
|
114
|
-
```ruby
|
115
|
-
# app/config/routes.rb
|
116
|
-
devise_for :users, :controllers => {:passwords => "passwords"}
|
117
|
-
```
|
89
|
+
Because of this, you need to set `sign_in_after_reset_password` to false (either globally in your Devise initializer or via `devise_for`)
|
118
90
|
|
119
91
|
### Enabling Two-Factor Authentication
|
120
92
|
Enabling two-factor authentication for a user is easy. For example, if my user model were named User, I could do the following:
|
data/devise-two-factor.gemspec
CHANGED
@@ -26,11 +26,11 @@ Gem::Specification.new do |s|
|
|
26
26
|
|
27
27
|
s.add_runtime_dependency 'railties'
|
28
28
|
s.add_runtime_dependency 'activesupport'
|
29
|
-
s.add_runtime_dependency 'activemodel'
|
30
29
|
s.add_runtime_dependency 'attr_encrypted', '~> 1.3.2'
|
31
|
-
s.add_runtime_dependency 'devise', '
|
32
|
-
s.add_runtime_dependency 'rotp', '
|
30
|
+
s.add_runtime_dependency 'devise', '~> 3.5.0'
|
31
|
+
s.add_runtime_dependency 'rotp', '~> 2'
|
33
32
|
|
33
|
+
s.add_development_dependency 'activemodel'
|
34
34
|
s.add_development_dependency 'bundler', '> 1.0'
|
35
35
|
s.add_development_dependency 'rspec', '> 2', '< 3'
|
36
36
|
s.add_development_dependency 'simplecov'
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'active_model'
|
2
|
-
|
3
1
|
module Devise
|
4
2
|
module Models
|
5
3
|
# TwoFactorBackupable allows a user to generate backup codes which
|
@@ -25,7 +23,7 @@ module Devise
|
|
25
23
|
codes << SecureRandom.hex(code_length / 2) # Hexstring has length 2*n
|
26
24
|
end
|
27
25
|
|
28
|
-
hashed_codes = codes.map { |code| Devise.
|
26
|
+
hashed_codes = codes.map { |code| Devise::Encryptor.digest(self.class, code) }
|
29
27
|
self.otp_backup_codes = hashed_codes
|
30
28
|
|
31
29
|
codes
|
@@ -37,14 +35,7 @@ module Devise
|
|
37
35
|
codes = self.otp_backup_codes || []
|
38
36
|
|
39
37
|
codes.each do |backup_code|
|
40
|
-
|
41
|
-
# method, we'll have to adjust our comparison here to match it
|
42
|
-
# TODO Fork Devise and encapsulate this logic in a helper
|
43
|
-
bcrypt = ::BCrypt::Password.new(backup_code)
|
44
|
-
hashed_code = ::BCrypt::Engine.hash_secret("#{code}#{self.class.pepper}",
|
45
|
-
bcrypt.salt)
|
46
|
-
|
47
|
-
next unless Devise.secure_compare(hashed_code, backup_code)
|
38
|
+
next unless Devise::Encryptor.compare(self.class, backup_code, code)
|
48
39
|
|
49
40
|
codes.delete(backup_code)
|
50
41
|
self.otp_backup_codes = codes
|
@@ -70,7 +70,7 @@ shared_examples 'two_factor_authenticatable' do
|
|
70
70
|
end
|
71
71
|
|
72
72
|
it 'should return uri with issuer option' do
|
73
|
-
subject.otp_provisioning_uri(account, issuer: issuer).should match(%r{otpauth://totp/#{account}\?
|
73
|
+
subject.otp_provisioning_uri(account, issuer: issuer).should match(%r{otpauth://totp/#{account}\?secret=\w{#{otp_secret_length}}&issuer=#{issuer}$})
|
74
74
|
end
|
75
75
|
end
|
76
76
|
end
|
@@ -36,7 +36,7 @@ shared_examples 'two_factor_backupable' do
|
|
36
36
|
|
37
37
|
context 'with existing recovery codes' do
|
38
38
|
let(:old_codes) { Faker::Lorem.words }
|
39
|
-
let(:old_codes_hashed) { old_codes.map { |x| Devise.
|
39
|
+
let(:old_codes_hashed) { old_codes.map { |x| Devise::Encryptor.digest(subject.class, x) } }
|
40
40
|
|
41
41
|
before do
|
42
42
|
subject.otp_backup_codes = old_codes_hashed
|
@@ -8,8 +8,7 @@ module Devise
|
|
8
8
|
# 1. The password and the OTP are correct
|
9
9
|
# 2. The password is correct, and OTP is not required for login
|
10
10
|
# We check the OTP, then defer to DatabaseAuthenticatable
|
11
|
-
if validate(resource) {
|
12
|
-
resource.valid_otp?(params[scope]['otp_attempt']) }
|
11
|
+
if validate(resource) { validate_otp(resource) }
|
13
12
|
super
|
14
13
|
end
|
15
14
|
|
@@ -19,6 +18,12 @@ module Devise
|
|
19
18
|
# but database authenticatable automatically halts on a bad password
|
20
19
|
@halted = false if @result == :failure
|
21
20
|
end
|
21
|
+
|
22
|
+
def validate_otp(resource)
|
23
|
+
return true unless resource.otp_required_for_login
|
24
|
+
return if params[scope]['otp_attempt'].nil?
|
25
|
+
resource.valid_otp?(params[scope]['otp_attempt'])
|
26
|
+
end
|
22
27
|
end
|
23
28
|
end
|
24
29
|
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: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shane Wilton
|
@@ -84,172 +84,166 @@ cert_chain:
|
|
84
84
|
5C31v4YyRBnNCp0pN66nxYX2avEiQ8riTBP5mlkPPOhsIoYQHHe2Uj75aVpu0LZ3
|
85
85
|
cdFzuO4GC1dV0Wv+dsDm+MyF7DT5E9pUPXpnMJuPvPrFpCb+wrFlszW9hGjXbQ==
|
86
86
|
-----END CERTIFICATE-----
|
87
|
-
date: 2015-
|
87
|
+
date: 2015-07-10 00:00:00.000000000 Z
|
88
88
|
dependencies:
|
89
89
|
- !ruby/object:Gem::Dependency
|
90
90
|
name: railties
|
91
91
|
requirement: !ruby/object:Gem::Requirement
|
92
92
|
requirements:
|
93
|
-
- -
|
93
|
+
- - ">="
|
94
94
|
- !ruby/object:Gem::Version
|
95
95
|
version: '0'
|
96
96
|
type: :runtime
|
97
97
|
prerelease: false
|
98
98
|
version_requirements: !ruby/object:Gem::Requirement
|
99
99
|
requirements:
|
100
|
-
- -
|
100
|
+
- - ">="
|
101
101
|
- !ruby/object:Gem::Version
|
102
102
|
version: '0'
|
103
103
|
- !ruby/object:Gem::Dependency
|
104
104
|
name: activesupport
|
105
105
|
requirement: !ruby/object:Gem::Requirement
|
106
106
|
requirements:
|
107
|
-
- -
|
107
|
+
- - ">="
|
108
108
|
- !ruby/object:Gem::Version
|
109
109
|
version: '0'
|
110
110
|
type: :runtime
|
111
111
|
prerelease: false
|
112
112
|
version_requirements: !ruby/object:Gem::Requirement
|
113
113
|
requirements:
|
114
|
-
- -
|
114
|
+
- - ">="
|
115
115
|
- !ruby/object:Gem::Version
|
116
116
|
version: '0'
|
117
117
|
- !ruby/object:Gem::Dependency
|
118
|
-
name:
|
118
|
+
name: attr_encrypted
|
119
119
|
requirement: !ruby/object:Gem::Requirement
|
120
120
|
requirements:
|
121
|
-
- -
|
121
|
+
- - "~>"
|
122
122
|
- !ruby/object:Gem::Version
|
123
|
-
version:
|
123
|
+
version: 1.3.2
|
124
124
|
type: :runtime
|
125
125
|
prerelease: false
|
126
126
|
version_requirements: !ruby/object:Gem::Requirement
|
127
127
|
requirements:
|
128
|
-
- -
|
128
|
+
- - "~>"
|
129
129
|
- !ruby/object:Gem::Version
|
130
|
-
version:
|
130
|
+
version: 1.3.2
|
131
131
|
- !ruby/object:Gem::Dependency
|
132
|
-
name:
|
132
|
+
name: devise
|
133
133
|
requirement: !ruby/object:Gem::Requirement
|
134
134
|
requirements:
|
135
|
-
- - ~>
|
135
|
+
- - "~>"
|
136
136
|
- !ruby/object:Gem::Version
|
137
|
-
version:
|
137
|
+
version: 3.5.0
|
138
138
|
type: :runtime
|
139
139
|
prerelease: false
|
140
140
|
version_requirements: !ruby/object:Gem::Requirement
|
141
141
|
requirements:
|
142
|
-
- - ~>
|
142
|
+
- - "~>"
|
143
143
|
- !ruby/object:Gem::Version
|
144
|
-
version:
|
144
|
+
version: 3.5.0
|
145
145
|
- !ruby/object:Gem::Dependency
|
146
|
-
name:
|
146
|
+
name: rotp
|
147
147
|
requirement: !ruby/object:Gem::Requirement
|
148
148
|
requirements:
|
149
|
-
- -
|
149
|
+
- - "~>"
|
150
150
|
- !ruby/object:Gem::Version
|
151
|
-
version:
|
152
|
-
- - <
|
153
|
-
- !ruby/object:Gem::Version
|
154
|
-
version: '3.5'
|
151
|
+
version: '2'
|
155
152
|
type: :runtime
|
156
153
|
prerelease: false
|
157
154
|
version_requirements: !ruby/object:Gem::Requirement
|
158
155
|
requirements:
|
159
|
-
- -
|
160
|
-
- !ruby/object:Gem::Version
|
161
|
-
version: 3.2.4
|
162
|
-
- - <
|
156
|
+
- - "~>"
|
163
157
|
- !ruby/object:Gem::Version
|
164
|
-
version: '
|
158
|
+
version: '2'
|
165
159
|
- !ruby/object:Gem::Dependency
|
166
|
-
name:
|
160
|
+
name: activemodel
|
167
161
|
requirement: !ruby/object:Gem::Requirement
|
168
162
|
requirements:
|
169
|
-
- -
|
163
|
+
- - ">="
|
170
164
|
- !ruby/object:Gem::Version
|
171
|
-
version: '
|
172
|
-
type: :
|
165
|
+
version: '0'
|
166
|
+
type: :development
|
173
167
|
prerelease: false
|
174
168
|
version_requirements: !ruby/object:Gem::Requirement
|
175
169
|
requirements:
|
176
|
-
- -
|
170
|
+
- - ">="
|
177
171
|
- !ruby/object:Gem::Version
|
178
|
-
version: '
|
172
|
+
version: '0'
|
179
173
|
- !ruby/object:Gem::Dependency
|
180
174
|
name: bundler
|
181
175
|
requirement: !ruby/object:Gem::Requirement
|
182
176
|
requirements:
|
183
|
-
- -
|
177
|
+
- - ">"
|
184
178
|
- !ruby/object:Gem::Version
|
185
179
|
version: '1.0'
|
186
180
|
type: :development
|
187
181
|
prerelease: false
|
188
182
|
version_requirements: !ruby/object:Gem::Requirement
|
189
183
|
requirements:
|
190
|
-
- -
|
184
|
+
- - ">"
|
191
185
|
- !ruby/object:Gem::Version
|
192
186
|
version: '1.0'
|
193
187
|
- !ruby/object:Gem::Dependency
|
194
188
|
name: rspec
|
195
189
|
requirement: !ruby/object:Gem::Requirement
|
196
190
|
requirements:
|
197
|
-
- -
|
191
|
+
- - ">"
|
198
192
|
- !ruby/object:Gem::Version
|
199
193
|
version: '2'
|
200
|
-
- - <
|
194
|
+
- - "<"
|
201
195
|
- !ruby/object:Gem::Version
|
202
196
|
version: '3'
|
203
197
|
type: :development
|
204
198
|
prerelease: false
|
205
199
|
version_requirements: !ruby/object:Gem::Requirement
|
206
200
|
requirements:
|
207
|
-
- -
|
201
|
+
- - ">"
|
208
202
|
- !ruby/object:Gem::Version
|
209
203
|
version: '2'
|
210
|
-
- - <
|
204
|
+
- - "<"
|
211
205
|
- !ruby/object:Gem::Version
|
212
206
|
version: '3'
|
213
207
|
- !ruby/object:Gem::Dependency
|
214
208
|
name: simplecov
|
215
209
|
requirement: !ruby/object:Gem::Requirement
|
216
210
|
requirements:
|
217
|
-
- -
|
211
|
+
- - ">="
|
218
212
|
- !ruby/object:Gem::Version
|
219
213
|
version: '0'
|
220
214
|
type: :development
|
221
215
|
prerelease: false
|
222
216
|
version_requirements: !ruby/object:Gem::Requirement
|
223
217
|
requirements:
|
224
|
-
- -
|
218
|
+
- - ">="
|
225
219
|
- !ruby/object:Gem::Version
|
226
220
|
version: '0'
|
227
221
|
- !ruby/object:Gem::Dependency
|
228
222
|
name: faker
|
229
223
|
requirement: !ruby/object:Gem::Requirement
|
230
224
|
requirements:
|
231
|
-
- -
|
225
|
+
- - ">="
|
232
226
|
- !ruby/object:Gem::Version
|
233
227
|
version: '0'
|
234
228
|
type: :development
|
235
229
|
prerelease: false
|
236
230
|
version_requirements: !ruby/object:Gem::Requirement
|
237
231
|
requirements:
|
238
|
-
- -
|
232
|
+
- - ">="
|
239
233
|
- !ruby/object:Gem::Version
|
240
234
|
version: '0'
|
241
235
|
- !ruby/object:Gem::Dependency
|
242
236
|
name: timecop
|
243
237
|
requirement: !ruby/object:Gem::Requirement
|
244
238
|
requirements:
|
245
|
-
- -
|
239
|
+
- - ">="
|
246
240
|
- !ruby/object:Gem::Version
|
247
241
|
version: '0'
|
248
242
|
type: :development
|
249
243
|
prerelease: false
|
250
244
|
version_requirements: !ruby/object:Gem::Requirement
|
251
245
|
requirements:
|
252
|
-
- -
|
246
|
+
- - ">="
|
253
247
|
- !ruby/object:Gem::Version
|
254
248
|
version: '0'
|
255
249
|
description: Barebones two-factor authentication with Devise
|
@@ -258,9 +252,9 @@ executables: []
|
|
258
252
|
extensions: []
|
259
253
|
extra_rdoc_files: []
|
260
254
|
files:
|
261
|
-
- .gitignore
|
262
|
-
- .rspec
|
263
|
-
- .travis.yml
|
255
|
+
- ".gitignore"
|
256
|
+
- ".rspec"
|
257
|
+
- ".travis.yml"
|
264
258
|
- CONTRIBUTING.md
|
265
259
|
- Gemfile
|
266
260
|
- LICENSE
|
@@ -294,12 +288,12 @@ require_paths:
|
|
294
288
|
- lib
|
295
289
|
required_ruby_version: !ruby/object:Gem::Requirement
|
296
290
|
requirements:
|
297
|
-
- -
|
291
|
+
- - ">="
|
298
292
|
- !ruby/object:Gem::Version
|
299
293
|
version: '0'
|
300
294
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
301
295
|
requirements:
|
302
|
-
- -
|
296
|
+
- - ">="
|
303
297
|
- !ruby/object:Gem::Version
|
304
298
|
version: '0'
|
305
299
|
requirements: []
|
@@ -312,4 +306,3 @@ test_files:
|
|
312
306
|
- spec/devise/models/two_factor_authenticatable_spec.rb
|
313
307
|
- spec/devise/models/two_factor_backupable_spec.rb
|
314
308
|
- spec/spec_helper.rb
|
315
|
-
has_rdoc:
|
metadata.gz.sig
CHANGED
Binary file
|