two_factor_authentication 1.1.4 → 1.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +13 -6
- data/CHANGELOG.md +109 -0
- data/Gemfile +8 -2
- data/README.md +182 -54
- data/app/controllers/devise/two_factor_authentication_controller.rb +1 -1
- data/config/locales/fr.yml +7 -0
- data/lib/generators/active_record/templates/migration.rb +6 -11
- data/lib/two_factor_authentication.rb +3 -0
- data/lib/two_factor_authentication/hooks/two_factor_authenticatable.rb +26 -2
- data/lib/two_factor_authentication/models/two_factor_authenticatable.rb +89 -23
- data/lib/two_factor_authentication/schema.rb +12 -4
- data/lib/two_factor_authentication/version.rb +1 -1
- data/spec/controllers/two_factor_authentication_controller_spec.rb +33 -0
- data/spec/features/two_factor_authenticatable_spec.rb +164 -28
- data/spec/generators/active_record/two_factor_authentication_generator_spec.rb +36 -0
- data/spec/lib/two_factor_authentication/models/two_factor_authenticatable_spec.rb +213 -117
- data/spec/rails_app/app/models/encrypted_user.rb +14 -0
- data/spec/rails_app/app/models/user.rb +1 -2
- data/spec/rails_app/config/environments/test.rb +3 -0
- data/spec/rails_app/config/initializers/devise.rb +3 -1
- data/spec/rails_app/db/migrate/20151224171231_add_encrypted_columns_to_user.rb +9 -0
- data/spec/rails_app/db/migrate/20151224180310_populate_otp_column.rb +19 -0
- data/spec/rails_app/db/migrate/20151228230340_remove_otp_secret_key_from_user.rb +5 -0
- data/spec/rails_app/db/schema.rb +16 -14
- data/spec/spec_helper.rb +1 -0
- data/spec/support/authenticated_model_helper.rb +26 -2
- data/spec/support/controller_helper.rb +16 -0
- data/spec/support/features_spec_helper.rb +24 -1
- data/two_factor_authentication.gemspec +1 -0
- metadata +25 -3
- data/spec/controllers/two_factor_auth_spec.rb +0 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 75e8b63e29715336ec78a4a06db141ec2fdddf68
|
4
|
+
data.tar.gz: c2b09dd1294a966ce75425efc97b6e2089e33689
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6d38f91cbb77148bd9c401f129d977f5fa22281ddb20ec12e4a12f02b1e35acabd04048bc88bad4d7e4cc80311e43f8720f45b0eeea116323c96fdee78cfb733
|
7
|
+
data.tar.gz: e7a2a0026e4558a8e0e8efbb3257b0fec162f7d45390a7521f249332e273c53568b309044000fae9ecdfcd55c3cfe13a951d2895f5327bb54b692a4fe2723fcf
|
data/.travis.yml
CHANGED
@@ -1,21 +1,28 @@
|
|
1
1
|
language: ruby
|
2
2
|
|
3
3
|
env:
|
4
|
-
- "RAILS_VERSION=3.2
|
5
|
-
- "RAILS_VERSION=4.0
|
6
|
-
- "RAILS_VERSION=4.1
|
7
|
-
- "RAILS_VERSION=4.2
|
4
|
+
- "RAILS_VERSION=3.2"
|
5
|
+
- "RAILS_VERSION=4.0"
|
6
|
+
- "RAILS_VERSION=4.1"
|
7
|
+
- "RAILS_VERSION=4.2"
|
8
8
|
- "RAILS_VERSION=master"
|
9
9
|
|
10
10
|
rvm:
|
11
|
-
- 1.9.3
|
12
11
|
- 2.0
|
13
12
|
- 2.1
|
14
|
-
- 2.2
|
13
|
+
- 2.2.2
|
15
14
|
|
16
15
|
matrix:
|
17
16
|
allow_failures:
|
18
17
|
- env: "RAILS_VERSION=master"
|
18
|
+
exclude:
|
19
|
+
- rvm: 2.0
|
20
|
+
env: RAILS_VERSION=master
|
21
|
+
- rvm: 2.1
|
22
|
+
env: RAILS_VERSION=master
|
23
|
+
|
24
|
+
before_install:
|
25
|
+
- gem update bundler
|
19
26
|
|
20
27
|
before_script:
|
21
28
|
- bundle exec rake app:db:migrate
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
# Change Log
|
2
|
+
|
3
|
+
## [Unreleased](https://github.com/houdini/two_factor_authentication/tree/HEAD)
|
4
|
+
|
5
|
+
[Full Changelog](https://github.com/houdini/two_factor_authentication/compare/v1.1.4...HEAD)
|
6
|
+
|
7
|
+
**Closed issues:**
|
8
|
+
|
9
|
+
- How should I integrate Devise two factor authentication with custom sessions controller? [\#60](https://github.com/Houdini/two_factor_authentication/issues/60)
|
10
|
+
|
11
|
+
**Merged pull requests:**
|
12
|
+
|
13
|
+
- Update bundler on Travis before installing gems [\#63](https://github.com/Houdini/two_factor_authentication/pull/63) ([monfresh](https://github.com/monfresh))
|
14
|
+
- Add support for OTP secret key encryption [\#62](https://github.com/Houdini/two_factor_authentication/pull/62) ([monfresh](https://github.com/monfresh))
|
15
|
+
- Allow executing code after sign in and before sign out [\#61](https://github.com/Houdini/two_factor_authentication/pull/61) ([monfresh](https://github.com/monfresh))
|
16
|
+
|
17
|
+
## [v1.1.4](https://github.com/houdini/two_factor_authentication/tree/v1.1.4) (2016-01-01)
|
18
|
+
[Full Changelog](https://github.com/houdini/two_factor_authentication/compare/v1.1.3...v1.1.4)
|
19
|
+
|
20
|
+
**Closed issues:**
|
21
|
+
|
22
|
+
- Old OTP can be used after a new one has been generated [\#59](https://github.com/Houdini/two_factor_authentication/issues/59)
|
23
|
+
- Do we have any two\_factor\_method like authenticate\_user! [\#58](https://github.com/Houdini/two_factor_authentication/issues/58)
|
24
|
+
- Configuration [\#57](https://github.com/Houdini/two_factor_authentication/issues/57)
|
25
|
+
|
26
|
+
**Merged pull requests:**
|
27
|
+
|
28
|
+
- Abstract logic for two factor success and fail into separate methods.… [\#56](https://github.com/Houdini/two_factor_authentication/pull/56) ([kpheasey](https://github.com/kpheasey))
|
29
|
+
- Move require rotp library to the file where it is used [\#55](https://github.com/Houdini/two_factor_authentication/pull/55) ([gkopylov](https://github.com/gkopylov))
|
30
|
+
- Add support for remembering a user's 2FA session in a cookie [\#54](https://github.com/Houdini/two_factor_authentication/pull/54) ([boffbowsh](https://github.com/boffbowsh))
|
31
|
+
- Test against Ruby 2.2 and Rails 4.2 [\#53](https://github.com/Houdini/two_factor_authentication/pull/53) ([boffbowsh](https://github.com/boffbowsh))
|
32
|
+
- Eliminates appended '?' to redirects that have no query string [\#46](https://github.com/Houdini/two_factor_authentication/pull/46) ([daveriess](https://github.com/daveriess))
|
33
|
+
|
34
|
+
## [v1.1.3](https://github.com/houdini/two_factor_authentication/tree/v1.1.3) (2014-12-14)
|
35
|
+
[Full Changelog](https://github.com/houdini/two_factor_authentication/compare/list...v1.1.3)
|
36
|
+
|
37
|
+
**Closed issues:**
|
38
|
+
|
39
|
+
- rails g two\_factor\_authentication MODEL does not append .rb to end of migration [\#40](https://github.com/Houdini/two_factor_authentication/issues/40)
|
40
|
+
|
41
|
+
**Merged pull requests:**
|
42
|
+
|
43
|
+
- Allows length of OTP to be configured [\#44](https://github.com/Houdini/two_factor_authentication/pull/44) ([amoose](https://github.com/amoose))
|
44
|
+
- Missing translation. [\#43](https://github.com/Houdini/two_factor_authentication/pull/43) ([sadfuzzy](https://github.com/sadfuzzy))
|
45
|
+
- Preserve query parameters in \_return\_to for redirect. [\#42](https://github.com/Houdini/two_factor_authentication/pull/42) ([omb-awong](https://github.com/omb-awong))
|
46
|
+
- Add file extension to ActiveRecord generator [\#41](https://github.com/Houdini/two_factor_authentication/pull/41) ([jackturnbull](https://github.com/jackturnbull))
|
47
|
+
|
48
|
+
## [list](https://github.com/houdini/two_factor_authentication/tree/list) (2014-07-14)
|
49
|
+
[Full Changelog](https://github.com/houdini/two_factor_authentication/compare/v1.1.2...list)
|
50
|
+
|
51
|
+
## [v1.1.2](https://github.com/houdini/two_factor_authentication/tree/v1.1.2) (2014-07-14)
|
52
|
+
[Full Changelog](https://github.com/houdini/two_factor_authentication/compare/v1.1.1...v1.1.2)
|
53
|
+
|
54
|
+
**Closed issues:**
|
55
|
+
|
56
|
+
- NoMethodError \(undefined method `scan' for nil:NilClass\) [\#37](https://github.com/Houdini/two_factor_authentication/issues/37)
|
57
|
+
|
58
|
+
**Merged pull requests:**
|
59
|
+
|
60
|
+
- Updated readme with rake task to update existing users with OTP secret k... [\#39](https://github.com/Houdini/two_factor_authentication/pull/39) ([Znow](https://github.com/Znow))
|
61
|
+
- Updated readme with view overriding [\#38](https://github.com/Houdini/two_factor_authentication/pull/38) ([Znow](https://github.com/Znow))
|
62
|
+
|
63
|
+
## [v1.1.1](https://github.com/houdini/two_factor_authentication/tree/v1.1.1) (2014-05-31)
|
64
|
+
[Full Changelog](https://github.com/houdini/two_factor_authentication/compare/v1.1...v1.1.1)
|
65
|
+
|
66
|
+
**Closed issues:**
|
67
|
+
|
68
|
+
- Override views [\#36](https://github.com/Houdini/two_factor_authentication/issues/36)
|
69
|
+
- NoMethodError in Devise::TwoFactorAuthenticationController\#update [\#30](https://github.com/Houdini/two_factor_authentication/issues/30)
|
70
|
+
|
71
|
+
**Merged pull requests:**
|
72
|
+
|
73
|
+
- Use Strings and not Symbols for keys when storing variable in warden session [\#35](https://github.com/Houdini/two_factor_authentication/pull/35) ([karolsarnacki](https://github.com/karolsarnacki))
|
74
|
+
- Chore/extract reused hash key [\#34](https://github.com/Houdini/two_factor_authentication/pull/34) ([rud](https://github.com/rud))
|
75
|
+
- Pad OTP codes with less than 6 digits [\#31](https://github.com/Houdini/two_factor_authentication/pull/31) ([brissmyr](https://github.com/brissmyr))
|
76
|
+
|
77
|
+
## [v1.1](https://github.com/houdini/two_factor_authentication/tree/v1.1) (2014-04-16)
|
78
|
+
**Closed issues:**
|
79
|
+
|
80
|
+
- Update [\#15](https://github.com/Houdini/two_factor_authentication/issues/15)
|
81
|
+
- Data in formats other than HTML left unprotected [\#6](https://github.com/Houdini/two_factor_authentication/issues/6)
|
82
|
+
- Wordlists [\#5](https://github.com/Houdini/two_factor_authentication/issues/5)
|
83
|
+
- devise - wrong number of arguments \(1 for 0\) [\#3](https://github.com/Houdini/two_factor_authentication/issues/3)
|
84
|
+
- gem? [\#1](https://github.com/Houdini/two_factor_authentication/issues/1)
|
85
|
+
|
86
|
+
**Merged pull requests:**
|
87
|
+
|
88
|
+
- added is\_fully\_authenticated helper for current version [\#28](https://github.com/Houdini/two_factor_authentication/pull/28) ([edg3r](https://github.com/edg3r))
|
89
|
+
- Adds integration spec to ensure authentication code is sent on sign in [\#27](https://github.com/Houdini/two_factor_authentication/pull/27) ([rossta](https://github.com/rossta))
|
90
|
+
- ensure return\_to location is properly stored [\#26](https://github.com/Houdini/two_factor_authentication/pull/26) ([rossta](https://github.com/rossta))
|
91
|
+
- travis badge in README [\#25](https://github.com/Houdini/two_factor_authentication/pull/25) ([rossta](https://github.com/rossta))
|
92
|
+
- Integration specs [\#24](https://github.com/Houdini/two_factor_authentication/pull/24) ([rossta](https://github.com/rossta))
|
93
|
+
- README updates [\#23](https://github.com/Houdini/two_factor_authentication/pull/23) ([rossta](https://github.com/rossta))
|
94
|
+
- extract method \#max\_login\_attempts [\#22](https://github.com/Houdini/two_factor_authentication/pull/22) ([rossta](https://github.com/rossta))
|
95
|
+
- extract method \#populate\_otp\_column [\#21](https://github.com/Houdini/two_factor_authentication/pull/21) ([rossta](https://github.com/rossta))
|
96
|
+
- specs for Model\#provisioning\_uri [\#20](https://github.com/Houdini/two_factor_authentication/pull/20) ([rossta](https://github.com/rossta))
|
97
|
+
- Provide options for \#provisioning\_uri [\#19](https://github.com/Houdini/two_factor_authentication/pull/19) ([rossta](https://github.com/rossta))
|
98
|
+
- Use time-based authentication codes [\#16](https://github.com/Houdini/two_factor_authentication/pull/16) ([mattmueller](https://github.com/mattmueller))
|
99
|
+
- Add ru locales and locales for max\_limit\_reached view [\#13](https://github.com/Houdini/two_factor_authentication/pull/13) ([edg3r](https://github.com/edg3r))
|
100
|
+
- Update README.md [\#11](https://github.com/Houdini/two_factor_authentication/pull/11) ([edg3r](https://github.com/edg3r))
|
101
|
+
- Changed route from user to admin\_user [\#10](https://github.com/Houdini/two_factor_authentication/pull/10) ([ilanstern](https://github.com/ilanstern))
|
102
|
+
- Changed :notice to :error when setting flash message on attempt failure. [\#9](https://github.com/Houdini/two_factor_authentication/pull/9) ([johnmichaelbradley](https://github.com/johnmichaelbradley))
|
103
|
+
- Typo and punctuation corrections. [\#8](https://github.com/Houdini/two_factor_authentication/pull/8) ([johnmichaelbradley](https://github.com/johnmichaelbradley))
|
104
|
+
- Respond with 401 for request non-HTML requests [\#7](https://github.com/Houdini/two_factor_authentication/pull/7) ([WojtekKruszewski](https://github.com/WojtekKruszewski))
|
105
|
+
- need\_two\_factor\_authentication? method should accept request param. [\#4](https://github.com/Houdini/two_factor_authentication/pull/4) ([VladimirMikhailov](https://github.com/VladimirMikhailov))
|
106
|
+
- Add generators to make it easier to install and fix deprecation warnings [\#2](https://github.com/Houdini/two_factor_authentication/pull/2) ([carvil](https://github.com/carvil))
|
107
|
+
|
108
|
+
|
109
|
+
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
|
data/Gemfile
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
source
|
1
|
+
source 'https://rubygems.org'
|
2
2
|
|
3
3
|
# Specify your gem's dependencies in devise_ip_filter.gemspec
|
4
4
|
gemspec
|
@@ -20,6 +20,12 @@ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.2.0')
|
|
20
20
|
gem "test-unit", "~> 3.0"
|
21
21
|
end
|
22
22
|
|
23
|
+
group :test, :development do
|
24
|
+
gem 'sqlite3'
|
25
|
+
end
|
26
|
+
|
23
27
|
group :test do
|
24
|
-
gem
|
28
|
+
gem 'rack_session_access'
|
29
|
+
gem 'ammeter'
|
30
|
+
gem 'pry'
|
25
31
|
end
|
data/README.md
CHANGED
@@ -5,11 +5,12 @@
|
|
5
5
|
|
6
6
|
## Features
|
7
7
|
|
8
|
-
*
|
9
|
-
*
|
10
|
-
*
|
11
|
-
*
|
8
|
+
* configurable OTP code digit length
|
9
|
+
* configurable max login attempts
|
10
|
+
* customizable logic to determine if a user needs two factor authentication
|
11
|
+
* customizable logic for sending the OTP code to the user
|
12
12
|
* configurable period where users won't be asked for 2FA again
|
13
|
+
* option to encrypt the OTP secret key in the database, with iv and salt
|
13
14
|
|
14
15
|
## Configuration
|
15
16
|
|
@@ -23,62 +24,73 @@ Once that's done, run:
|
|
23
24
|
|
24
25
|
bundle install
|
25
26
|
|
26
|
-
|
27
|
+
Note that Ruby 2.0 or greater is required.
|
27
28
|
|
28
|
-
|
29
|
+
### Installation
|
29
30
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
and create a migration in `db/migrate/`, which will add `:otp_secret_key` and `:second_factor_attempts_count` to your table.
|
34
|
-
Finally, run the migration with:
|
31
|
+
#### Automatic initial setup
|
32
|
+
To set up the model and database migration file automatically, run the
|
33
|
+
following command:
|
35
34
|
|
36
|
-
bundle exec
|
35
|
+
bundle exec rails g two_factor_authentication MODEL
|
37
36
|
|
38
|
-
|
37
|
+
Where MODEL is your model name (e.g. User or Admin). This generator will add
|
38
|
+
`:two_factor_authenticatable` to your model's Devise options and create a
|
39
|
+
migration in `db/migrate/`, which will add the following columns to your table:
|
39
40
|
|
40
|
-
|
41
|
+
- `:second_factor_attempts_count`
|
42
|
+
- `:encrypted_otp_secret_key`
|
43
|
+
- `:encrypted_otp_secret_key_iv`
|
44
|
+
- `:encrypted_otp_secret_key_salt`
|
41
45
|
|
42
|
-
|
46
|
+
#### Manual initial setup
|
47
|
+
If you prefer to set up the model and migration manually, add the
|
48
|
+
`:two_factor_authentication` option to your existing devise options, such as:
|
43
49
|
|
44
50
|
```ruby
|
45
|
-
|
46
|
-
|
47
|
-
config.otp_length = 6 # OTP code length
|
48
|
-
config.remember_otp_session_for_seconds = 30.days # Time before browser has to enter OTP code again
|
51
|
+
devise :database_authenticatable, :registerable, :recoverable, :rememberable,
|
52
|
+
:trackable, :validatable, :two_factor_authenticatable
|
49
53
|
```
|
50
54
|
|
51
|
-
|
55
|
+
Then create your migration file using the Rails generator, such as:
|
52
56
|
|
53
|
-
```
|
54
|
-
|
55
|
-
# use Model#otp_code and send via SMS, etc.
|
56
|
-
end
|
57
|
+
```
|
58
|
+
rails g migration AddTwoFactorFieldsToUsers second_factor_attempts_count:integer encrypted_otp_secret_key:string:index encrypted_otp_secret_key_iv:string encrypted_otp_secret_key_salt:string
|
57
59
|
```
|
58
60
|
|
59
|
-
|
60
|
-
|
61
|
-
|
61
|
+
Open your migration file (it will be in the `db/migrate` directory and will be
|
62
|
+
named something like `20151230163930_add_two_factor_fields_to_users.rb`), and
|
63
|
+
add `unique: true` to the `add_index` line so that it looks like this:
|
62
64
|
|
63
65
|
```ruby
|
64
|
-
|
65
|
-
:recoverable, :rememberable, :trackable, :validatable, :two_factor_authenticatable
|
66
|
+
add_index :users, :encrypted_otp_secret_key, unique: true
|
66
67
|
```
|
68
|
+
Save the file.
|
69
|
+
|
70
|
+
#### Complete the setup
|
71
|
+
Run the migration with:
|
72
|
+
|
73
|
+
bundle exec rake db:migrate
|
67
74
|
|
68
75
|
Add the following line to your model to fully enable two-factor auth:
|
69
76
|
|
70
|
-
has_one_time_password
|
77
|
+
has_one_time_password(encrypted: true)
|
71
78
|
|
72
|
-
Set config values
|
79
|
+
Set config values in `config/initializers/devise.rb`:
|
73
80
|
|
74
81
|
```ruby
|
75
|
-
config.max_login_attempts = 3 # Maximum second factor attempts count
|
76
|
-
config.allowed_otp_drift_seconds = 30 # Allowed time drift
|
82
|
+
config.max_login_attempts = 3 # Maximum second factor attempts count.
|
83
|
+
config.allowed_otp_drift_seconds = 30 # Allowed time drift between client and server.
|
77
84
|
config.otp_length = 6 # OTP code length
|
78
|
-
config.remember_otp_session_for_seconds = 30.days # Time before browser has to enter OTP code again
|
85
|
+
config.remember_otp_session_for_seconds = 30.days # Time before browser has to enter OTP code again. Default is 0.
|
86
|
+
config.otp_secret_encryption_key = ENV['OTP_SECRET_ENCRYPTION_KEY']
|
79
87
|
```
|
88
|
+
The `otp_secret_encryption_key` must be a random key that is not stored in the
|
89
|
+
DB, and is not checked in to your repo. It is recommended to store it in an
|
90
|
+
environment variable, and you can generate it with `bundle exec rake secret`.
|
80
91
|
|
81
|
-
Override the method to send one-time passwords in your model
|
92
|
+
Override the method to send one-time passwords in your model. This is
|
93
|
+
automatically called when a user logs in:
|
82
94
|
|
83
95
|
```ruby
|
84
96
|
def send_two_factor_authentication_code
|
@@ -86,10 +98,10 @@ def send_two_factor_authentication_code
|
|
86
98
|
end
|
87
99
|
```
|
88
100
|
|
89
|
-
|
90
101
|
### Customisation and Usage
|
91
102
|
|
92
|
-
By default second factor authentication
|
103
|
+
By default, second factor authentication is required for each user. You can
|
104
|
+
change that by overriding the following method in your model:
|
93
105
|
|
94
106
|
```ruby
|
95
107
|
def need_two_factor_authentication?(request)
|
@@ -97,19 +109,29 @@ def need_two_factor_authentication?(request)
|
|
97
109
|
end
|
98
110
|
```
|
99
111
|
|
100
|
-
|
112
|
+
In the example above, two factor authentication will not be required for local
|
113
|
+
users.
|
101
114
|
|
102
|
-
This gem is compatible with Google Authenticator
|
115
|
+
This gem is compatible with [Google Authenticator](https://support.google.com/accounts/answer/1066447?hl=en).
|
116
|
+
You can generate provisioning uris by invoking the following method on your model:
|
103
117
|
|
104
|
-
|
118
|
+
```ruby
|
119
|
+
user.provisioning_uri # This assumes a user model with an email attribute
|
120
|
+
```
|
105
121
|
|
106
|
-
This provisioning uri can then be turned in to a QR code if desired so that
|
122
|
+
This provisioning uri can then be turned in to a QR code if desired so that
|
123
|
+
users may add the app to Google Authenticator easily. Once this is done, they
|
124
|
+
may retrieve a one-time password directly from the Google Authenticator app as
|
125
|
+
well as through whatever method you define in
|
126
|
+
`send_two_factor_authentication_code`.
|
107
127
|
|
108
128
|
#### Overriding the view
|
109
129
|
|
110
|
-
The default view that shows the form can be overridden by
|
130
|
+
The default view that shows the form can be overridden by adding a
|
131
|
+
file named `show.html.erb` (or `show.html.haml` if you prefer HAML)
|
132
|
+
inside `app/views/devise/two_factor_authentication/` and customizing it.
|
133
|
+
Below is an example using ERB:
|
111
134
|
|
112
|
-
The full path should be "app/views/devise/two_factor_authentication/show.html.erb"
|
113
135
|
|
114
136
|
```html
|
115
137
|
<h2>Hi, you received a code by email, please enter it below, thanks!</h2>
|
@@ -125,22 +147,128 @@ The full path should be "app/views/devise/two_factor_authentication/show.html.er
|
|
125
147
|
|
126
148
|
#### Updating existing users with OTP secret key
|
127
149
|
|
128
|
-
If you have existing users that
|
150
|
+
If you have existing users that need to be provided with a OTP secret key, so
|
151
|
+
they can use two factor authentication, create a rake task. It could look like this one below:
|
129
152
|
|
130
153
|
```ruby
|
131
|
-
desc
|
154
|
+
desc 'rake task to update users with otp secret key'
|
132
155
|
task :update_users_with_otp_secret_key => :environment do
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
156
|
+
User.find_each do |user|
|
157
|
+
user.otp_secret_key = ROTP::Base32.random_base32
|
158
|
+
user.save!
|
159
|
+
puts "Rake[:update_users_with_otp_secret_key] => OTP secret key set to '#{key}' for User '#{user.email}'"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
```
|
163
|
+
Then run the task with `bundle exec rake update_users_with_otp_secret_key`
|
164
|
+
|
165
|
+
#### Adding the OTP encryption option to an existing app
|
166
|
+
|
167
|
+
If you've already been using this gem, and want to start encrypting the OTP
|
168
|
+
secret key in the database (recommended), you'll need to perform the following
|
169
|
+
steps:
|
170
|
+
|
171
|
+
1. Generate a migration to add the necessary columns to your model's table:
|
172
|
+
|
173
|
+
```
|
174
|
+
rails g migration AddEncryptionFieldsToUsers encrypted_otp_secret_key:string:index encrypted_otp_secret_key_iv:string encrypted_otp_secret_key_salt:string
|
175
|
+
```
|
176
|
+
|
177
|
+
Open your migration file (it will be in the `db/migrate` directory and will be
|
178
|
+
named something like `20151230163930_add_encryption_fields_to_users.rb`), and
|
179
|
+
add `unique: true` to the `add_index` line so that it looks like this:
|
180
|
+
|
181
|
+
```ruby
|
182
|
+
add_index :users, :encrypted_otp_secret_key, unique: true
|
183
|
+
```
|
184
|
+
Save the file.
|
185
|
+
|
186
|
+
2. Run the migration: `bundle exec rake db:migrate`
|
187
|
+
|
188
|
+
2. Update the gem: `bundle update two_factor_authentication`
|
189
|
+
|
190
|
+
3. Add `encrypted: true` to `has_one_time_password` in your model.
|
191
|
+
For example: `has_one_time_password(encrypted: true)`
|
192
|
+
|
193
|
+
4. Generate a migration to populate the new encryption fields:
|
194
|
+
```
|
195
|
+
rails g migration PopulateEncryptedOtpFields
|
196
|
+
```
|
197
|
+
|
198
|
+
Open the generated file, and replace its contents with the following:
|
199
|
+
```ruby
|
200
|
+
class PopulateEncryptedOtpFields < ActiveRecord::Migration
|
201
|
+
def up
|
202
|
+
User.reset_column_information
|
203
|
+
|
204
|
+
User.find_each do |user|
|
205
|
+
user.otp_secret_key = user.read_attribute('otp_secret_key')
|
206
|
+
user.save!
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def down
|
211
|
+
User.reset_column_information
|
212
|
+
|
213
|
+
User.find_each do |user|
|
214
|
+
user.otp_secret_key = ROTP::Base32.random_base32
|
215
|
+
user.save!
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
```
|
220
|
+
|
221
|
+
5. Generate a migration to remove the `:otp_secret_key` column:
|
222
|
+
```
|
223
|
+
rails g migration RemoveOtpSecretKeyFromUsers otp_secret_key:string
|
224
|
+
```
|
225
|
+
|
226
|
+
6. Run the migrations: `bundle exec rake db:migrate`
|
227
|
+
|
228
|
+
If, for some reason, you want to switch back to the old non-encrypted version,
|
229
|
+
use these steps:
|
230
|
+
|
231
|
+
1. Remove `(encrypted: true)` from `has_one_time_password`
|
232
|
+
|
233
|
+
2. Roll back the last 3 migrations (assuming you haven't added any new ones
|
234
|
+
after them):
|
235
|
+
```
|
236
|
+
bundle exec rake db:rollback STEP=3
|
237
|
+
```
|
238
|
+
|
239
|
+
#### Executing some code after the user signs in and before they sign out
|
240
|
+
|
241
|
+
In some cases, you might want to perform some action right after the user signs
|
242
|
+
in, but before the OTP is sent, and also right before the user signs out. One
|
243
|
+
scenario where you would need this is if you are requiring users to confirm
|
244
|
+
their phone number first before they can receive an OTP. If they enter a wrong
|
245
|
+
number, then sign out or close the browser before they confirm, they won't be
|
246
|
+
able to confirm their real number. To solve this problem, we need to be able to
|
247
|
+
reset their unconfirmed number before they sign out or sign in, and before the
|
248
|
+
OTP code is sent.
|
249
|
+
|
250
|
+
To define this action, create a `#{user.class}OtpSender` class that takes the
|
251
|
+
current user as its parameter, and defines a `#reset_otp_state` instance method.
|
252
|
+
For example, if your user's class is `User`, you would create a `UserOtpSender`
|
253
|
+
class, like this:
|
254
|
+
```ruby
|
255
|
+
class UserOtpSender
|
256
|
+
def initialize(user)
|
257
|
+
@user = user
|
258
|
+
end
|
259
|
+
|
260
|
+
def reset_otp_state
|
261
|
+
if @user.unconfirmed_mobile.present?
|
262
|
+
@user.update(unconfirmed_mobile: nil)
|
263
|
+
end
|
264
|
+
end
|
141
265
|
end
|
142
266
|
```
|
267
|
+
If you have different types of users in your app (for example, User and Admin),
|
268
|
+
and you need different logic for each type of user, create a second class for
|
269
|
+
your admin user, such as `AdminOtpSender`, with its own logic for
|
270
|
+
`#reset_otp_state`.
|
143
271
|
|
144
|
-
### Example
|
272
|
+
### Example App
|
145
273
|
|
146
274
|
[TwoFactorAuthenticationExample](https://github.com/Houdini/TwoFactorAuthenticationExample)
|