devise-otp 0.2.0 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/README.md +18 -8
- data/app/controllers/devise_otp/tokens_controller.rb +6 -6
- data/app/views/devise_otp/credentials/show.html.erb +1 -1
- data/app/views/devise_otp/tokens/_trusted_devices.html.erb +10 -0
- data/app/views/devise_otp/tokens/show.html.erb +1 -13
- data/config/locales/en.yml +8 -8
- data/lib/devise-otp.rb +10 -3
- data/lib/devise-otp/version.rb +1 -1
- data/lib/devise_otp_authenticatable/controllers/helpers.rb +8 -2
- data/lib/devise_otp_authenticatable/controllers/url_helpers.rb +0 -2
- data/lib/devise_otp_authenticatable/models/otp_authenticatable.rb +18 -19
- data/lib/devise_otp_authenticatable/routes.rb +5 -3
- data/lib/generators/active_record/devise_otp_generator.rb +1 -1
- data/lib/generators/active_record/templates/migration.rb +0 -1
- data/lib/generators/devise_otp/install_generator.rb +26 -4
- data/test/integration/sign_in_test.rb +1 -1
- data/test/integration/token_test.rb +34 -0
- data/test/integration_tests_helper.rb +14 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b5936698b6e66c85c09d06f13df85acec3ef012b
|
4
|
+
data.tar.gz: 88768f71081fa8385d38aa5c035453003f7253b4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b7f176ffae7d15974ce50a6e60c9a169c6e03c5e0f9933b7642e893592d4691c3e7d561a7755ea011a246d7a02a6711aa9c3e0b0cd0a75de1c5312126d94b2ea
|
7
|
+
data.tar.gz: d1c47a4ce461d627709639a76963130b3566dda2951a2685d40551dccdad204d68ab15f0606ba31da9ba40259ceccfb21fe68bd2a3b32158292d092591a73ff7
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -4,16 +4,19 @@
|
|
4
4
|
Devise OTP implements two-factors authentication for Devise, using an rfc6238 compatible Time-Based One-Time Password Algorithm.
|
5
5
|
It uses the [rotp library](https://github.com/mdp/rotp) for generation and verification of codes.
|
6
6
|
|
7
|
+
**If you are upgrading from version 0.1.x, you will need to regenerate your views.**
|
8
|
+
|
7
9
|
It currently has the following features:
|
8
10
|
|
9
11
|
* Url based provisioning of token devices, compatible with **Google Authenticator**.
|
10
|
-
*
|
12
|
+
* Browsers can be set as 'trusted' for a limited time. During that time no OTP challenge is asked again when logging from that browser (but normal login will).
|
13
|
+
* Two factors authentication can be **optional** at user discretion, **recommended** (it nags the user on every sign-in) or **mandatory** (users must enroll OTP after signing-in next time, before they can navigate the site). The settings is global, or per-user. ( **incomplete**, see below)
|
11
14
|
* Optionally, users can obtain a list of HOTP recovery tokens to be used for emergency log-in in case the token device is lost or unavailable.
|
12
15
|
|
13
16
|
Compatible token devices are:
|
14
17
|
|
15
18
|
* [Google Authenticator](https://code.google.com/p/google-authenticator/)
|
16
|
-
*
|
19
|
+
* [FreeOTP](https://fedorahosted.org/freeotp/)
|
17
20
|
|
18
21
|
## Quick overview of Two Factors Authentication, using OTPs.
|
19
22
|
|
@@ -99,12 +102,19 @@ With this extension enabled, the following is expected behaviour:
|
|
99
102
|
|
100
103
|
The install generator adds some options to the end of your Devise config file (config/initializers/devise.rb)
|
101
104
|
|
102
|
-
* config.otp_mandatory - OTP is mandatory, users are going to be asked to enroll the next time they sign in, before they can successfully complete the session establishment.
|
103
|
-
* config.otp_authentication_timeout - how long the user has to authenticate with their token. (defaults to 3.minutes)
|
104
|
-
* config.otp_drift_window - a window which provides allowance for drift between a user's token device clock (and therefore their OTP tokens) and the authentication server's clock. (default: 3)
|
105
|
-
* config.otp_credentials_refresh - Users that have logged in longer than this time ago,
|
106
|
-
* config.
|
107
|
-
* config.
|
105
|
+
* `config.otp_mandatory` - OTP is mandatory, users are going to be asked to enroll the next time they sign in, before they can successfully complete the session establishment.
|
106
|
+
* `config.otp_authentication_timeout` - how long the user has to authenticate with their token. (defaults to `3.minutes`)
|
107
|
+
* `config.otp_drift_window` - a window which provides allowance for drift between a user's token device clock (and therefore their OTP tokens) and the authentication server's clock. Expressed in minutes centered at the current time. (default: `3`)
|
108
|
+
* `config.otp_credentials_refresh` - Users that have logged in longer than this time ago, are going to be asked their password (and an OTP challenge, if enabled) before they can see or change their otp informations. (defaults to `15.minutes`)
|
109
|
+
* `config.otp_recovery_tokens` - Whether the users are given a list of one-time recovery tokens, for emergency access (default: `10`, set to `false` to disable)
|
110
|
+
* `config.otp_trust_persistence` - The user is allowed to set his browser as "trusted", no more OTP challenges will be asked for that browser, for a limited time. (default: `1.month`, set to false to disable setting the browser as trusted)
|
111
|
+
* `config.otp_uri_application` - The name of this application, to be added to the provisioning url as '<user_email>/application_name' (defaults to the Rails application class)
|
112
|
+
|
113
|
+
## Todo
|
114
|
+
|
115
|
+
* 2D barcodes for provisioning are currently produced with the google charts api. You can, of course, use your own source in the template, but I am looking for a solution with no external dependencies (feedback welcome).
|
116
|
+
* **recommended** mode (nag the user each time) is not fully implemented. Right now you can make 2FA mandatory, or leave it to the user.
|
117
|
+
|
108
118
|
|
109
119
|
## Contributing
|
110
120
|
|
@@ -4,7 +4,7 @@ class DeviseOtp::TokensController < DeviseController
|
|
4
4
|
prepend_before_filter :ensure_credentials_refresh
|
5
5
|
prepend_before_filter :authenticate_scope!
|
6
6
|
|
7
|
-
|
7
|
+
protect_from_forgery :except => [:clear_persistence, :delete_persistence]
|
8
8
|
|
9
9
|
#
|
10
10
|
# Displays the status of OTP authentication
|
@@ -21,14 +21,13 @@ class DeviseOtp::TokensController < DeviseController
|
|
21
21
|
# Updates the status of OTP authentication
|
22
22
|
#
|
23
23
|
def update
|
24
|
-
|
25
|
-
|
24
|
+
|
25
|
+
enabled = (params[resource_name][:otp_enabled] == '1')
|
26
|
+
if (enabled ? resource.enable_otp! : resource.disable_otp!)
|
26
27
|
|
27
28
|
otp_set_flash_message :success, :successfully_updated
|
28
|
-
render :show
|
29
|
-
else
|
30
|
-
render :show
|
31
29
|
end
|
30
|
+
render :show
|
32
31
|
end
|
33
32
|
|
34
33
|
#
|
@@ -86,6 +85,7 @@ class DeviseOtp::TokensController < DeviseController
|
|
86
85
|
render :recovery
|
87
86
|
end
|
88
87
|
|
88
|
+
|
89
89
|
private
|
90
90
|
|
91
91
|
def ensure_credentials_refresh
|
@@ -17,7 +17,7 @@
|
|
17
17
|
</p>
|
18
18
|
|
19
19
|
<p><%= f.submit I18n.t('submit', {:scope => 'devise.otp.submit_token'}) %></p>
|
20
|
-
<%- if !@recovery &&
|
20
|
+
<%- if !@recovery && recovery_enabled? %>
|
21
21
|
<p><%= link_to I18n.t('recovery_link', {:scope => 'devise.otp.submit_token'}), otp_credential_path_for(resource_name, :challenge => @challenge, :recovery => true) %></p>
|
22
22
|
<% end %>
|
23
23
|
<% end %>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<h3><%= I18n.t('title', {:scope => 'devise.otp.trusted_devices'}) %></h3>
|
2
|
+
<p><%= I18n.t('explain', {:scope => 'devise.otp.trusted_devices'}) %></p>
|
3
|
+
<%- if is_otp_trusted_device_for? resource %>
|
4
|
+
<p><em><%= I18n.t('device_trusted', {:scope => 'devise.otp.trusted_devices'}) %></em></p>
|
5
|
+
<p><%= link_to I18n.t('trust_remove', {:scope => 'devise.otp.trusted_devices'}), persistence_otp_token_path_for(resource_name), :method => :post %></p>
|
6
|
+
<% else %>
|
7
|
+
<p><%= I18n.t('device_not_trusted', {:scope => 'devise.otp.trusted_devices'}) %></p>
|
8
|
+
<p><%= link_to I18n.t('trust_add', {:scope => 'devise.otp.trusted_devices'}), persistence_otp_token_path_for(resource_name) %></p>
|
9
|
+
<% end %>
|
10
|
+
<p><%= link_to I18n.t('trust_clear', {:scope => 'devise.otp.trusted_devices'}), persistence_otp_token_path_for(resource_name), :method => :delete %></p>
|
@@ -1,6 +1,4 @@
|
|
1
1
|
<h2><%= I18n.t('title', {:scope => 'devise.otp.tokens'}) %></h2>
|
2
|
-
<p><%= I18n.t('caption', {:scope => 'devise.otp.tokens'}) %></p>
|
3
|
-
|
4
2
|
<p><%= I18n.t('explain', {:scope => 'devise.otp.tokens'}) %></p>
|
5
3
|
|
6
4
|
<%= form_for(resource, :as => resource_name, :url => [resource_name, :otp_token], :html => { :method => :put }) do |f| %>
|
@@ -17,15 +15,5 @@
|
|
17
15
|
|
18
16
|
<%- if resource.otp_enabled? %>
|
19
17
|
<%= render :partial => 'token_secret' if resource.otp_enabled? %>
|
20
|
-
|
21
|
-
<h3><%= I18n.t('title', {:scope => 'devise.otp.trusted_devices'}) %></h3>
|
22
|
-
<p><%= I18n.t('explain', {:scope => 'devise.otp.trusted_devices'}) %></p>
|
23
|
-
<%- if is_otp_trusted_device_for? resource %>
|
24
|
-
<p><em><%= I18n.t('device_trusted', {:scope => 'devise.otp.trusted_devices'}) %></em></p>
|
25
|
-
<p><%= link_to I18n.t('trust_remove', {:scope => 'devise.otp.trusted_devices'}), persistence_otp_token_path_for(resource_name), :method => :post %></p>
|
26
|
-
<% else %>
|
27
|
-
<p><%= I18n.t('device_not_trusted', {:scope => 'devise.otp.trusted_devices'}) %></p>
|
28
|
-
<p><%= link_to I18n.t('trust_add', {:scope => 'devise.otp.trusted_devices'}), persistence_otp_token_path_for(resource_name) %></p>
|
29
|
-
<% end %>
|
30
|
-
<p><%= link_to I18n.t('trust_clear', {:scope => 'devise.otp.trusted_devices'}), persistence_otp_token_path_for(resource_name), :method => :delete %></p>
|
18
|
+
<%= render :partial => 'trusted_devices' if trusted_devices_enabled? %>
|
31
19
|
<% end %>
|
data/config/locales/en.yml
CHANGED
@@ -34,7 +34,7 @@ en:
|
|
34
34
|
|
35
35
|
tokens:
|
36
36
|
title: 'Two-factors Authentication:'
|
37
|
-
explain: 'Two factors
|
37
|
+
explain: 'Two factors authentication adds adds an additional layer of security to your account. When logging in you will be asked for a code that you can generate on a physical device, like your phone.'
|
38
38
|
enable_request: 'Would you like to enable Two Factors Authenticator?'
|
39
39
|
|
40
40
|
status: 'Enable Two-Factors Authentication.'
|
@@ -57,10 +57,10 @@ en:
|
|
57
57
|
|
58
58
|
|
59
59
|
trusted_devices:
|
60
|
-
title: 'Trusted
|
61
|
-
explain: '
|
62
|
-
device_trusted: 'Your
|
63
|
-
device_not_trusted: 'Your
|
64
|
-
trust_remove: 'Remove this device from the list of trusted
|
65
|
-
trust_add: 'Trust this
|
66
|
-
trust_clear: 'Clear the list of trusted
|
60
|
+
title: 'Trusted Browsers'
|
61
|
+
explain: 'If you set your browser as trusted, you will not be asked to perform a 2-factors authentication when logging in from that browser, for a time of one month.'
|
62
|
+
device_trusted: 'Your browser is trusted.'
|
63
|
+
device_not_trusted: 'Your browser is not trusted.'
|
64
|
+
trust_remove: 'Remove this device from the list of trusted browsers'
|
65
|
+
trust_add: 'Trust this browser'
|
66
|
+
trust_clear: 'Clear the list of trusted browser'
|
data/lib/devise-otp.rb
CHANGED
@@ -28,14 +28,21 @@ module Devise
|
|
28
28
|
#
|
29
29
|
#
|
30
30
|
#
|
31
|
-
mattr_accessor :
|
32
|
-
@@
|
31
|
+
mattr_accessor :otp_recovery_tokens
|
32
|
+
@@otp_recovery_tokens = 10 ## false to disable
|
33
|
+
|
34
|
+
#
|
35
|
+
# If the user is given the chance to set his browser as trusted, how long will it stay trusted.
|
36
|
+
# set to nil/false to disable the ability to set a device as trusted
|
37
|
+
#
|
38
|
+
mattr_accessor :otp_trust_persistence
|
39
|
+
@@otp_trust_persistence = 30.days
|
33
40
|
|
34
41
|
#
|
35
42
|
#
|
36
43
|
#
|
37
44
|
mattr_accessor :otp_drift_window
|
38
|
-
@@otp_drift_window = 3 # in
|
45
|
+
@@otp_drift_window = 3 # in minutes
|
39
46
|
|
40
47
|
#
|
41
48
|
# if the user wants to change Otp settings,
|
data/lib/devise-otp/version.rb
CHANGED
@@ -27,8 +27,12 @@ module DeviseOtpAuthenticatable
|
|
27
27
|
end
|
28
28
|
|
29
29
|
|
30
|
+
def trusted_devices_enabled?
|
31
|
+
resource.class.otp_trust_persistence && (resource.class.otp_trust_persistence > 0)
|
32
|
+
end
|
33
|
+
|
30
34
|
def recovery_enabled?
|
31
|
-
resource_class.
|
35
|
+
resource_class.otp_recovery_tokens && (resource_class.otp_recovery_tokens > 0)
|
32
36
|
end
|
33
37
|
|
34
38
|
#
|
@@ -67,6 +71,7 @@ module DeviseOtpAuthenticatable
|
|
67
71
|
# is the current browser trusted?
|
68
72
|
#
|
69
73
|
def is_otp_trusted_device_for?(resource)
|
74
|
+
return false unless resource.class.otp_trust_persistence
|
70
75
|
if cookies[otp_scoped_persistence_cookie].present?
|
71
76
|
cookies.signed[otp_scoped_persistence_cookie] ==
|
72
77
|
[resource.class.serialize_into_cookie(resource), resource.otp_persistence_seed].tap do
|
@@ -80,9 +85,10 @@ module DeviseOtpAuthenticatable
|
|
80
85
|
# make the current browser trusted
|
81
86
|
#
|
82
87
|
def otp_set_trusted_device_for(resource)
|
88
|
+
return unless resource.class.otp_trust_persistence
|
83
89
|
cookies.signed[otp_scoped_persistence_cookie] = {
|
84
90
|
:httponly => true,
|
85
|
-
:expires =>
|
91
|
+
:expires => Time.now + resource.class.otp_trust_persistence,
|
86
92
|
:value => [resource.class.serialize_into_cookie(resource), resource.otp_persistence_seed]
|
87
93
|
}
|
88
94
|
end
|
@@ -3,13 +3,11 @@ module DeviseOtpAuthenticatable
|
|
3
3
|
|
4
4
|
module UrlHelpers
|
5
5
|
|
6
|
-
|
7
6
|
def recovery_otp_token_for(resource_or_scope, opts = {})
|
8
7
|
scope = Devise::Mapping.find_scope!(resource_or_scope)
|
9
8
|
send("recovery_#{scope}_otp_token_path", opts)
|
10
9
|
end
|
11
10
|
|
12
|
-
|
13
11
|
def refresh_otp_credential_path_for(resource_or_scope, opts = {})
|
14
12
|
scope = Devise::Mapping.find_scope!(resource_or_scope)
|
15
13
|
send("refresh_#{scope}_otp_credential_path", opts)
|
@@ -11,8 +11,8 @@ module Devise::Models
|
|
11
11
|
end
|
12
12
|
|
13
13
|
module ClassMethods
|
14
|
-
::Devise::Models.config(self, :otp_authentication_timeout, :otp_drift_window,
|
15
|
-
:otp_mandatory, :otp_credentials_refresh, :otp_uri_application, :
|
14
|
+
::Devise::Models.config(self, :otp_authentication_timeout, :otp_drift_window, :otp_trust_persistence,
|
15
|
+
:otp_mandatory, :otp_credentials_refresh, :otp_uri_application, :otp_recovery_tokens)
|
16
16
|
|
17
17
|
def find_valid_otp_challenge(challenge)
|
18
18
|
with_valid_otp_challenge(Time.now).where(:otp_session_challenge => challenge).first
|
@@ -41,9 +41,9 @@ module Devise::Models
|
|
41
41
|
@recovery_otp = nil
|
42
42
|
generate_otp_auth_secret
|
43
43
|
reset_otp_persistence
|
44
|
-
|
45
|
-
|
46
|
-
|
44
|
+
update(:otp_enabled => false, :otp_time_drift => 0,
|
45
|
+
:otp_session_challenge => nil, :otp_challenge_expires => nil,
|
46
|
+
:otp_recovery_counter => 0)
|
47
47
|
end
|
48
48
|
|
49
49
|
def reset_otp_credentials!
|
@@ -51,7 +51,6 @@ module Devise::Models
|
|
51
51
|
save!
|
52
52
|
end
|
53
53
|
|
54
|
-
|
55
54
|
def reset_otp_persistence
|
56
55
|
generate_otp_persistence_seed
|
57
56
|
end
|
@@ -61,9 +60,17 @@ module Devise::Models
|
|
61
60
|
save!
|
62
61
|
end
|
63
62
|
|
63
|
+
def enable_otp!
|
64
|
+
update!(:otp_enabled => true, :otp_enabled_on => Time.now)
|
65
|
+
end
|
66
|
+
|
67
|
+
def disable_otp!
|
68
|
+
update!(:otp_enabled => false, :otp_enabled_on => nil, :otp_time_drift => 0)
|
69
|
+
end
|
70
|
+
|
64
71
|
def generate_otp_challenge!(expires = nil)
|
65
|
-
|
66
|
-
|
72
|
+
update!(:otp_session_challenge => SecureRandom.hex,
|
73
|
+
:otp_challenge_expires => DateTime.now + (expires || self.class.otp_authentication_timeout))
|
67
74
|
otp_session_challenge
|
68
75
|
end
|
69
76
|
|
@@ -91,7 +98,7 @@ module Devise::Models
|
|
91
98
|
end
|
92
99
|
alias_method :valid_otp_time_token?, :validate_otp_time_token
|
93
100
|
|
94
|
-
def next_otp_recovery_tokens(number =
|
101
|
+
def next_otp_recovery_tokens(number = self.class.otp_recovery_tokens)
|
95
102
|
(otp_recovery_counter..otp_recovery_counter + number).inject({}) do |h, index|
|
96
103
|
h[index] = recovery_otp.at(index)
|
97
104
|
h
|
@@ -108,21 +115,13 @@ module Devise::Models
|
|
108
115
|
|
109
116
|
|
110
117
|
|
111
|
-
|
112
|
-
|
113
118
|
private
|
114
119
|
|
115
|
-
#
|
116
|
-
# refactor me, I suck
|
117
|
-
#
|
118
120
|
def validate_otp_token_with_drift(token)
|
119
|
-
# valid_vals << ROTP::TOTP.new(otp_auth_secret).at(Time.now)
|
120
121
|
|
121
122
|
# should be centered around saved drift
|
122
|
-
(-self.class.otp_drift_window..self.class.otp_drift_window).
|
123
|
-
|
124
|
-
end
|
125
|
-
false
|
123
|
+
(-self.class.otp_drift_window..self.class.otp_drift_window).any? {|drift|
|
124
|
+
(time_based_otp.verify(token, Time.now.ago(30 * drift))) }
|
126
125
|
end
|
127
126
|
|
128
127
|
def generate_otp_persistence_seed
|
@@ -11,9 +11,11 @@ module ActionDispatch::Routing
|
|
11
11
|
resource :token, :only => [:show, :update, :destroy],
|
12
12
|
:path => mapping.path_names[:token], :controller => controllers[:otp_tokens] do
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
if Devise.otp_trust_persistence
|
15
|
+
get :persistence, :action => 'get_persistence'
|
16
|
+
post :persistence, :action => 'clear_persistence'
|
17
|
+
delete :persistence, :action => 'delete_persistence'
|
18
|
+
end
|
17
19
|
|
18
20
|
get :recovery
|
19
21
|
end
|
@@ -6,7 +6,7 @@ module ActiveRecord
|
|
6
6
|
source_root File.expand_path("../templates", __FILE__)
|
7
7
|
|
8
8
|
def copy_devise_migration
|
9
|
-
migration_template "migration.rb", "db/migrate/devise_otp_add_to_#{table_name}"
|
9
|
+
migration_template "migration.rb", "db/migrate/devise_otp_add_to_#{table_name}.rb"
|
10
10
|
end
|
11
11
|
end
|
12
12
|
end
|
@@ -6,7 +6,6 @@ class DeviseOtpAddTo<%= table_name.camelize %> < ActiveRecord::Migration
|
|
6
6
|
t.boolean :otp_enabled, :default => false, :null => false
|
7
7
|
t.boolean :otp_mandatory, :default => false, :null => false
|
8
8
|
t.datetime :otp_enabled_on
|
9
|
-
t.integer :otp_time_drift, :default => 0, :null => false
|
10
9
|
t.integer :otp_failed_attempts, :default => 0, :null => false
|
11
10
|
t.integer :otp_recovery_counter, :default => 0, :null => false
|
12
11
|
t.string :otp_persistence_seed
|
@@ -13,11 +13,33 @@ content = <<-CONTENT
|
|
13
13
|
# ==> Devise OTP Extension
|
14
14
|
# Configure OTP extension for devise
|
15
15
|
|
16
|
-
#
|
17
|
-
#
|
16
|
+
# OTP is mandatory, users are going to be asked to
|
17
|
+
# enroll OTP the next time they sign in, before they can successfully complete the session establishment.
|
18
|
+
# This is the global value, can also be set on each user.
|
19
|
+
#config.otp_mandatory = false
|
20
|
+
|
21
|
+
# Drift: a window which provides allowance for drift between a user's token device clock
|
22
|
+
# (and therefore their OTP tokens) and the authentication server's clock.
|
23
|
+
# Expressed in minutes centered at the current time. (Note: it's a number, *NOT* 3.minutes )
|
24
|
+
#config.otp_drift_window = 3
|
25
|
+
|
26
|
+
# Users that have logged in longer than this time ago, are going to be asked their password
|
27
|
+
# (and an OTP challenge, if enabled) before they can see or change their otp informations.
|
28
|
+
#config.otp_credentials_refresh = 15.minutes
|
29
|
+
|
30
|
+
# Users are given a list of one-time recovery tokens, for emergency access
|
31
|
+
# set to false to disable giving recovery tokens.
|
32
|
+
#config.recovery_tokens = 10
|
33
|
+
|
34
|
+
# The user is allowed to set his browser as "trusted", no more OTP challenges will be
|
35
|
+
# asked for that browser, for a limited time.
|
36
|
+
# set to false to disable setting the browser as trusted
|
37
|
+
#config.otp_trust_persistence = 1.month
|
38
|
+
|
39
|
+
# The name of this application, to be added to the provisioning
|
40
|
+
# url as '<user_email>/application_name' (defaults to the Rails application class)
|
41
|
+
#config.otp_uri_application = 'my_application'
|
18
42
|
|
19
|
-
# Change time drift settings for valid token values. To change the default, uncomment and change the below:
|
20
|
-
#config.otp_authentication_time_drift = 3
|
21
43
|
CONTENT
|
22
44
|
|
23
45
|
inject_into_file "config/initializers/devise.rb", content, :before => /end[ |\n|]+\Z/
|
@@ -13,7 +13,7 @@ class SignInTest < ActionDispatch::IntegrationTest
|
|
13
13
|
visit new_user_session_path
|
14
14
|
fill_in 'user_email', :with => 'user@email.invalid'
|
15
15
|
fill_in 'user_password', :with => '12345678'
|
16
|
-
click_button 'Sign in'
|
16
|
+
page.has_content?('Log in') ? click_button('Log in') : click_button('Sign in')
|
17
17
|
|
18
18
|
assert_equal root_path, current_path
|
19
19
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'integration_tests_helper'
|
3
|
+
|
4
|
+
class TokenTest < ActionDispatch::IntegrationTest
|
5
|
+
|
6
|
+
|
7
|
+
def teardown
|
8
|
+
Capybara.reset_sessions!
|
9
|
+
end
|
10
|
+
|
11
|
+
test 'disabling OTP after successfully enabling' do
|
12
|
+
|
13
|
+
# log in 1fa
|
14
|
+
user = enable_otp_and_sign_in
|
15
|
+
assert_equal user_otp_credential_path, current_path
|
16
|
+
|
17
|
+
# otp 2fa
|
18
|
+
fill_in 'user_token', :with => ROTP::TOTP.new(user.otp_auth_secret).at(Time.now)
|
19
|
+
click_button 'Submit Token'
|
20
|
+
assert_equal root_path, current_path
|
21
|
+
|
22
|
+
# disable OTP
|
23
|
+
disable_otp
|
24
|
+
|
25
|
+
# logout
|
26
|
+
sign_out
|
27
|
+
|
28
|
+
# log back in 1fa
|
29
|
+
sign_user_in(user)
|
30
|
+
|
31
|
+
assert_equal root_path, current_path
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -36,13 +36,26 @@ class ActionDispatch::IntegrationTest
|
|
36
36
|
user
|
37
37
|
end
|
38
38
|
|
39
|
+
def disable_otp
|
40
|
+
visit user_otp_token_path
|
41
|
+
uncheck 'user_otp_enabled'
|
42
|
+
click_button 'Continue...'
|
43
|
+
end
|
44
|
+
|
45
|
+
def sign_out
|
46
|
+
Capybara.reset_sessions!
|
47
|
+
end
|
48
|
+
|
39
49
|
def sign_user_in(user = nil)
|
40
50
|
user ||= create_full_user
|
41
51
|
resource_name = user.class.name.underscore
|
42
52
|
visit send("new_#{resource_name}_session_path")
|
43
53
|
fill_in "#{resource_name}_email", :with => user.email
|
44
54
|
fill_in "#{resource_name}_password", :with => user.password
|
45
|
-
|
55
|
+
|
56
|
+
page.has_content?('Log in') ? click_button('Log in') : click_button('Sign in')
|
46
57
|
user
|
47
58
|
end
|
59
|
+
|
60
|
+
|
48
61
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: devise-otp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lele Forzani
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-08-
|
11
|
+
date: 2014-08-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -96,6 +96,7 @@ files:
|
|
96
96
|
- app/views/devise_otp/credentials/refresh.html.erb
|
97
97
|
- app/views/devise_otp/credentials/show.html.erb
|
98
98
|
- app/views/devise_otp/tokens/_token_secret.html.erb
|
99
|
+
- app/views/devise_otp/tokens/_trusted_devices.html.erb
|
99
100
|
- app/views/devise_otp/tokens/recovery.html.erb
|
100
101
|
- app/views/devise_otp/tokens/show.html.erb
|
101
102
|
- config/locales/en.yml
|
@@ -161,6 +162,7 @@ files:
|
|
161
162
|
- test/dummy/script/rails
|
162
163
|
- test/integration/refresh_test.rb
|
163
164
|
- test/integration/sign_in_test.rb
|
165
|
+
- test/integration/token_test.rb
|
164
166
|
- test/integration_tests_helper.rb
|
165
167
|
- test/model_tests_helper.rb
|
166
168
|
- test/models/otp_authenticatable_test.rb
|
@@ -236,6 +238,7 @@ test_files:
|
|
236
238
|
- test/dummy/script/rails
|
237
239
|
- test/integration/refresh_test.rb
|
238
240
|
- test/integration/sign_in_test.rb
|
241
|
+
- test/integration/token_test.rb
|
239
242
|
- test/integration_tests_helper.rb
|
240
243
|
- test/model_tests_helper.rb
|
241
244
|
- test/models/otp_authenticatable_test.rb
|