devise-otp 0.8.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +11 -0
- data/.gitignore +2 -0
- data/Appraisals +36 -0
- data/Gemfile +3 -1
- data/README.md +8 -7
- data/app/assets/stylesheets/devise-otp.css +4 -0
- data/app/controllers/devise_otp/devise/otp_credentials_controller.rb +4 -6
- data/app/controllers/devise_otp/devise/otp_tokens_controller.rb +1 -2
- data/config/locales/en.yml +0 -2
- data/devise-otp.gemspec +6 -5
- data/gemfiles/rails_7.0.gemfile +25 -0
- data/gemfiles/rails_7.1.gemfile +21 -0
- data/gemfiles/rails_7.2.gemfile +17 -0
- data/gemfiles/rails_8.0.gemfile +17 -0
- data/lib/devise-otp/version.rb +1 -1
- data/lib/devise_otp_authenticatable/controllers/helpers.rb +5 -28
- data/test/dummy/app/assets/javascripts/application.js +0 -1
- data/test/dummy/app/assets/stylesheets/application.css +1 -0
- data/test/dummy/app/views/layouts/application.html.erb +7 -1
- data/test/dummy/db/migrate/20240604000001_create_admins.rb +1 -1
- data/test/integration/disable_token_test.rb +3 -0
- data/test/integration/enable_otp_form_test.rb +17 -0
- data/test/integration/persistence_test.rb +3 -0
- data/test/integration/refresh_test.rb +9 -0
- data/test/integration/reset_token_test.rb +3 -0
- data/test/integration/sign_in_test.rb +30 -0
- metadata +27 -14
- data/app/assets/javascripts/devise-otp.js +0 -1
- data/app/assets/javascripts/qrcode.js +0 -609
- data/docs/QR_CODES.md +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7a2884c76d255b2c2bb3b40859db7018c5668d49fcb6897141dacd8d7df0bc62
|
4
|
+
data.tar.gz: 3c7ee30dd96a9711b4b4194cb2b94cea6abb06113dda2ef58b70c9a96c998ec4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b099c20c5c6d76fc2e1226511615bb43f2b081620f01bbf1bcc939cec66e286aa3536f8a654495907607e42d23e7f3efd9bb3377ed3a1a8a6ef522ec5e9568e1
|
7
|
+
data.tar.gz: 731d1dc34877d0fe91bb092b33ce4ee7c302c4cb75183daa3ff67a0df7f6d242bc86b3ab4e0923683b8408c8f1e6bab8e8a1d1c5fa10b9b0e104617a9b87ded7
|
data/.github/workflows/ci.yml
CHANGED
@@ -16,6 +16,17 @@ jobs:
|
|
16
16
|
- '3.2'
|
17
17
|
- '3.1'
|
18
18
|
- 'head'
|
19
|
+
rails:
|
20
|
+
- rails_8.0
|
21
|
+
- rails_7.2
|
22
|
+
- rails_7.1
|
23
|
+
- rails_7.0
|
24
|
+
exclude:
|
25
|
+
- ruby: '3.1'
|
26
|
+
rails: 'rails_8.0'
|
27
|
+
|
28
|
+
env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps
|
29
|
+
BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.rails }}.gemfile
|
19
30
|
|
20
31
|
steps:
|
21
32
|
- name: Checkout
|
data/.gitignore
CHANGED
data/Appraisals
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
appraise 'rails_7.0' do
|
4
|
+
gem 'rails', '~> 7.0.0'
|
5
|
+
gem 'sqlite3', '~> 1.5.0'
|
6
|
+
|
7
|
+
# Fix: LoadError: cannot load such file -- base64
|
8
|
+
install_if '-> { Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.3.0") }' do
|
9
|
+
gem 'base64'
|
10
|
+
gem 'bigdecimal'
|
11
|
+
gem 'mutex_m'
|
12
|
+
gem 'drb'
|
13
|
+
gem 'logger'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
appraise 'rails_7.1' do
|
18
|
+
gem 'rails', '~> 7.1.0'
|
19
|
+
gem 'sqlite3', '~> 1.5.0'
|
20
|
+
|
21
|
+
# Fix:
|
22
|
+
# warning: logger was loaded from the standard library, but will no longer be part of the default gems since Ruby 3.5.0.
|
23
|
+
# Add logger to your Gemfile or gemspec.
|
24
|
+
install_if '-> { Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.4.0") }' do
|
25
|
+
gem 'logger'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
appraise 'rails_7.2' do
|
30
|
+
gem 'rails', '~> 7.2.0'
|
31
|
+
gem 'sqlite3', '~> 1.5.0'
|
32
|
+
end
|
33
|
+
|
34
|
+
appraise 'rails_8.0' do
|
35
|
+
gem 'rails', '~> 8.0.0'
|
36
|
+
end
|
data/Gemfile
CHANGED
@@ -3,6 +3,8 @@ source "https://rubygems.org"
|
|
3
3
|
# Specify your gem's dependencies in devise-otp.gemspec
|
4
4
|
gemspec
|
5
5
|
|
6
|
+
gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git"
|
7
|
+
|
6
8
|
gem "capybara"
|
7
9
|
gem "minitest-reporters", ">= 0.5.0"
|
8
10
|
gem "puma"
|
@@ -10,5 +12,5 @@ gem "rake"
|
|
10
12
|
gem "rdoc"
|
11
13
|
gem "shoulda"
|
12
14
|
gem "sprockets-rails"
|
13
|
-
gem "sqlite3", "~> 1
|
15
|
+
gem "sqlite3", "~> 2.1"
|
14
16
|
gem "standardrb"
|
data/README.md
CHANGED
@@ -13,7 +13,7 @@ Some of the compatible token devices are:
|
|
13
13
|
* [Google Authenticator](https://code.google.com/p/google-authenticator/)
|
14
14
|
* [FreeOTP](https://fedorahosted.org/freeotp/)
|
15
15
|
|
16
|
-
Device OTP was recently updated to work with Rails 7 and Turbo.
|
16
|
+
Device OTP was recently updated to work with Rails 7+ and Turbo.
|
17
17
|
|
18
18
|
## Sponsor
|
19
19
|
|
@@ -58,10 +58,13 @@ Don't forget to migrate:
|
|
58
58
|
|
59
59
|
rake db:migrate
|
60
60
|
|
61
|
-
|
61
|
+
### Default CSS
|
62
62
|
|
63
|
-
|
63
|
+
To use the default CSS for devise-otp, just require the devise-otp.css file as usual in your application.css file (or equivalent):
|
64
64
|
|
65
|
+
*= require devise-otp
|
66
|
+
|
67
|
+
It might be even easier to just copy the styles to your project.
|
65
68
|
|
66
69
|
### Custom views
|
67
70
|
|
@@ -77,9 +80,7 @@ The install generator also installs an english copy of a Devise OTP i18n file. T
|
|
77
80
|
|
78
81
|
### QR codes
|
79
82
|
|
80
|
-
|
81
|
-
|
82
|
-
If you need something more, have a look at [QR codes](/docs/QR_CODES.md) documentation file.
|
83
|
+
Devise OTP generates QR Codes directly as SVG's via the [rqrcode](https://github.com/whomwah/rqrcode), so there are no JavaScript (or Sprockets) dependencies.
|
83
84
|
|
84
85
|
## Configuration
|
85
86
|
|
@@ -102,7 +103,7 @@ Enforcing mandatory OTP requires adding the ensure\_mandatory\_{scope}\_otp! met
|
|
102
103
|
|
103
104
|
## Authors
|
104
105
|
|
105
|
-
The project was originally started by Lele Forzani by forking [devise_google_authenticator](https://github.com/AsteriskLabs/devise_google_authenticator) and still contains some devise_google_authenticator code. It's now maintained by [Josef Strzibny](https://github.com/strzibny/).
|
106
|
+
The project was originally started by Lele Forzani by forking [devise_google_authenticator](https://github.com/AsteriskLabs/devise_google_authenticator) and still contains some devise_google_authenticator code. It's now maintained by [Josef Strzibny](https://github.com/strzibny/) and [Laney Stroup](https://github.com/strouptl).
|
106
107
|
|
107
108
|
Contributions are welcome!
|
108
109
|
|
@@ -26,17 +26,15 @@ module DeviseOtp
|
|
26
26
|
# signs the resource in, if the OTP token is valid and the user has a valid challenge
|
27
27
|
#
|
28
28
|
def update
|
29
|
-
if @token
|
30
|
-
otp_set_flash_message(:alert, :token_blank)
|
31
|
-
redirect_to otp_credential_path_for(resource_name, challenge: @challenge, recovery: @recovery)
|
32
|
-
elsif resource.otp_challenge_valid? && resource.validate_otp_token(@token, @recovery)
|
29
|
+
if resource.otp_challenge_valid? && resource.validate_otp_token(@token, @recovery)
|
33
30
|
sign_in(resource_name, resource)
|
34
31
|
|
35
32
|
otp_set_trusted_device_for(resource) if params[:enable_persistence] == "true"
|
36
33
|
otp_refresh_credentials_for(resource)
|
37
34
|
respond_with resource, location: after_sign_in_path_for(resource)
|
38
35
|
else
|
39
|
-
|
36
|
+
kind = (@token.blank? ? :token_blank : :token_invalid)
|
37
|
+
otp_set_flash_message :alert, kind, :now => true
|
40
38
|
render :show
|
41
39
|
end
|
42
40
|
end
|
@@ -103,7 +101,7 @@ module DeviseOtp
|
|
103
101
|
end
|
104
102
|
|
105
103
|
def failed_refresh
|
106
|
-
otp_set_flash_message :alert, :invalid_refresh
|
104
|
+
otp_set_flash_message :alert, :invalid_refresh, :now => true
|
107
105
|
render :refresh
|
108
106
|
end
|
109
107
|
|
@@ -35,7 +35,7 @@ module DeviseOtp
|
|
35
35
|
otp_set_flash_message :success, :successfully_updated
|
36
36
|
redirect_to otp_token_path_for(resource)
|
37
37
|
else
|
38
|
-
otp_set_flash_message :danger, :could_not_confirm
|
38
|
+
otp_set_flash_message :danger, :could_not_confirm, :now => true
|
39
39
|
render :edit
|
40
40
|
end
|
41
41
|
end
|
@@ -109,7 +109,6 @@ module DeviseOtp
|
|
109
109
|
ensure_resource!
|
110
110
|
|
111
111
|
if needs_credentials_refresh?(resource)
|
112
|
-
otp_set_flash_message :notice, :need_to_refresh_credentials
|
113
112
|
redirect_to refresh_otp_credential_path_for(resource)
|
114
113
|
end
|
115
114
|
end
|
data/config/locales/en.yml
CHANGED
@@ -14,7 +14,6 @@ en:
|
|
14
14
|
otp_session_invalid: Session invalid. Please start again.
|
15
15
|
token_invalid: 'The token you provided was invalid.'
|
16
16
|
token_blank: 'You need to type in the token you generated with your device.'
|
17
|
-
need_to_refresh_credentials: 'We need to check your credentials before you can change these settings.'
|
18
17
|
valid_refresh: 'Thank you, your credentials were accepted.'
|
19
18
|
invalid_refresh: 'Sorry, you provided the wrong credentials.'
|
20
19
|
credentials_refresh:
|
@@ -41,7 +40,6 @@ en:
|
|
41
40
|
successfully_set_persistence: 'Your device is now trusted.'
|
42
41
|
successfully_cleared_persistence: 'Your device has been removed from the list of trusted devices.'
|
43
42
|
successfully_reset_persistence: 'Your list of trusted devices has been cleared.'
|
44
|
-
need_to_refresh_credentials: 'We need to check your credentials before you can change these settings.'
|
45
43
|
recovery:
|
46
44
|
title: 'Your Emergency Recovery Codes'
|
47
45
|
explain: 'Take note or print these recovery codes. The will allow you to log back in in case your token device is lost, stolen, or unavailable.'
|
data/devise-otp.gemspec
CHANGED
@@ -5,16 +5,17 @@ require_relative "lib/devise-otp/version"
|
|
5
5
|
Gem::Specification.new do |gem|
|
6
6
|
gem.name = "devise-otp"
|
7
7
|
gem.version = Devise::OTP::VERSION
|
8
|
-
gem.authors = ["Lele Forzani", "Josef Strzibny"]
|
9
|
-
gem.email = ["lele@windmill.it", "strzibny@strzibny.name"]
|
10
|
-
gem.description = "
|
8
|
+
gem.authors = ["Lele Forzani", "Josef Strzibny", "Laney Stroup"]
|
9
|
+
gem.email = ["lele@windmill.it", "strzibny@strzibny.name", "laney@stroupsolutions.com"]
|
10
|
+
gem.description = "OTP authentication for Devise"
|
11
11
|
gem.summary = "Time Based OTP/rfc6238 compatible authentication for Devise"
|
12
|
-
gem.homepage = "
|
12
|
+
gem.homepage = "https://github.com/wmlele/devise-otp"
|
13
13
|
|
14
14
|
gem.files = `git ls-files`.split($/)
|
15
15
|
gem.require_paths = ["lib"]
|
16
16
|
|
17
|
-
gem.add_dependency "rails", ">= 7.0"
|
17
|
+
gem.add_dependency "rails", ">= 7.0"
|
18
18
|
gem.add_dependency "devise", ">= 4.8.0", "< 5.0"
|
19
19
|
gem.add_dependency "rotp", ">= 2.0.0"
|
20
|
+
gem.add_dependency "rqrcode", "~> 2.0"
|
20
21
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git"
|
6
|
+
gem "capybara"
|
7
|
+
gem "minitest-reporters", ">= 0.5.0"
|
8
|
+
gem "puma"
|
9
|
+
gem "rake"
|
10
|
+
gem "rdoc"
|
11
|
+
gem "shoulda"
|
12
|
+
gem "sprockets-rails"
|
13
|
+
gem "sqlite3", "~> 1.5.0"
|
14
|
+
gem "standardrb"
|
15
|
+
gem "rails", "~> 7.0.0"
|
16
|
+
|
17
|
+
install_if -> { Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.3.0") } do
|
18
|
+
gem "base64"
|
19
|
+
gem "bigdecimal"
|
20
|
+
gem "mutex_m"
|
21
|
+
gem "drb"
|
22
|
+
gem "logger"
|
23
|
+
end
|
24
|
+
|
25
|
+
gemspec path: "../"
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git"
|
6
|
+
gem "capybara"
|
7
|
+
gem "minitest-reporters", ">= 0.5.0"
|
8
|
+
gem "puma"
|
9
|
+
gem "rake"
|
10
|
+
gem "rdoc"
|
11
|
+
gem "shoulda"
|
12
|
+
gem "sprockets-rails"
|
13
|
+
gem "sqlite3", "~> 1.5.0"
|
14
|
+
gem "standardrb"
|
15
|
+
gem "rails", "~> 7.1.0"
|
16
|
+
|
17
|
+
install_if -> { Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.4.0") } do
|
18
|
+
gem "logger"
|
19
|
+
end
|
20
|
+
|
21
|
+
gemspec path: "../"
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git"
|
6
|
+
gem "capybara"
|
7
|
+
gem "minitest-reporters", ">= 0.5.0"
|
8
|
+
gem "puma"
|
9
|
+
gem "rake"
|
10
|
+
gem "rdoc"
|
11
|
+
gem "shoulda"
|
12
|
+
gem "sprockets-rails"
|
13
|
+
gem "sqlite3", "~> 1.5.0"
|
14
|
+
gem "standardrb"
|
15
|
+
gem "rails", "~> 7.2.0"
|
16
|
+
|
17
|
+
gemspec path: "../"
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git"
|
6
|
+
gem "capybara"
|
7
|
+
gem "minitest-reporters", ">= 0.5.0"
|
8
|
+
gem "puma"
|
9
|
+
gem "rake"
|
10
|
+
gem "rdoc"
|
11
|
+
gem "shoulda"
|
12
|
+
gem "sprockets-rails"
|
13
|
+
gem "sqlite3", "~> 2.1"
|
14
|
+
gem "standardrb"
|
15
|
+
gem "rails", "~> 8.0.0"
|
16
|
+
|
17
|
+
gemspec path: "../"
|
data/lib/devise-otp/version.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require "rqrcode"
|
2
|
+
|
1
3
|
module DeviseOtpAuthenticatable
|
2
4
|
module Controllers
|
3
5
|
module Helpers
|
@@ -12,11 +14,8 @@ module DeviseOtpAuthenticatable
|
|
12
14
|
#
|
13
15
|
def otp_set_flash_message(key, kind, options = {})
|
14
16
|
options[:scope] ||= "devise.otp.#{controller_name}"
|
15
|
-
|
16
|
-
|
17
|
-
options = devise_i18n_options(options) if respond_to?(:devise_i18n_options, true)
|
18
|
-
message = I18n.t("#{options[:resource_name]}.#{kind}", **options)
|
19
|
-
flash[key] = message if message.present?
|
17
|
+
|
18
|
+
set_flash_message(key, kind, options)
|
20
19
|
end
|
21
20
|
|
22
21
|
def otp_t
|
@@ -122,33 +121,11 @@ module DeviseOtpAuthenticatable
|
|
122
121
|
# returns the URL for the QR Code to initialize the Authenticator device
|
123
122
|
#
|
124
123
|
def otp_authenticator_token_image(resource)
|
125
|
-
otp_authenticator_token_image_js(resource.otp_provisioning_uri)
|
126
|
-
end
|
127
|
-
|
128
|
-
private
|
129
|
-
|
130
|
-
def otp_authenticator_token_image_js(otp_url)
|
131
124
|
content_tag(:div, class: "qrcode-container") do
|
132
|
-
|
133
|
-
javascript_tag(%[
|
134
|
-
new QRCode("qrcode", {
|
135
|
-
text: "#{otp_url}",
|
136
|
-
width: 256,
|
137
|
-
height: 256,
|
138
|
-
colorDark : "#000000",
|
139
|
-
colorLight : "#ffffff",
|
140
|
-
correctLevel : QRCode.CorrectLevel.H
|
141
|
-
});
|
142
|
-
])
|
143
|
-
end
|
125
|
+
raw RQRCode::QRCode.new(resource.otp_provisioning_uri).as_svg(:module_size => 5, :viewbox => true, :use_path => true)
|
144
126
|
end
|
145
127
|
end
|
146
128
|
|
147
|
-
def otp_authenticator_token_image_google(otp_url)
|
148
|
-
otp_url = Rack::Utils.escape(otp_url)
|
149
|
-
url = "https://chart.googleapis.com/chart?chs=200x200&chld=M|0&cht=qr&chl=#{otp_url}"
|
150
|
-
image_tag(url, alt: "OTP Url QRCode")
|
151
|
-
end
|
152
129
|
end
|
153
130
|
end
|
154
131
|
end
|
@@ -20,6 +20,10 @@ class EnableOtpFormTest < ActionDispatch::IntegrationTest
|
|
20
20
|
assert_equal user_otp_token_path, current_path
|
21
21
|
assert page.has_content?("Enabled")
|
22
22
|
|
23
|
+
within "#alerts" do
|
24
|
+
assert page.has_content? 'Your Two-Factor Authentication settings have been updated.'
|
25
|
+
end
|
26
|
+
|
23
27
|
user.reload
|
24
28
|
assert user.otp_enabled?
|
25
29
|
end
|
@@ -37,6 +41,15 @@ class EnableOtpFormTest < ActionDispatch::IntegrationTest
|
|
37
41
|
|
38
42
|
user.reload
|
39
43
|
assert_not user.otp_enabled?
|
44
|
+
|
45
|
+
within "#alerts" do
|
46
|
+
assert page.has_content? 'The Confirmation Code you entered did not match the QR code shown below.'
|
47
|
+
end
|
48
|
+
|
49
|
+
visit "/"
|
50
|
+
within "#alerts" do
|
51
|
+
assert !page.has_content?('The Confirmation Code you entered did not match the QR code shown below.')
|
52
|
+
end
|
40
53
|
end
|
41
54
|
|
42
55
|
test "a user should not be able enable their OTP authentication with a blank confirmation code" do
|
@@ -50,6 +63,10 @@ class EnableOtpFormTest < ActionDispatch::IntegrationTest
|
|
50
63
|
|
51
64
|
assert page.has_content?("To Enable Two-Factor Authentication")
|
52
65
|
|
66
|
+
within "#alerts" do
|
67
|
+
assert page.has_content? 'The Confirmation Code you entered did not match the QR code shown below.'
|
68
|
+
end
|
69
|
+
|
53
70
|
user.reload
|
54
71
|
assert_not user.otp_enabled?
|
55
72
|
end
|
@@ -60,6 +60,15 @@ class RefreshTest < ActionDispatch::IntegrationTest
|
|
60
60
|
fill_in "user_refresh_password", with: "12345670"
|
61
61
|
click_button "Continue..."
|
62
62
|
assert_equal refresh_user_otp_credential_path, current_path
|
63
|
+
|
64
|
+
within "#alerts" do
|
65
|
+
assert page.has_content? 'Sorry, you provided the wrong credentials.'
|
66
|
+
end
|
67
|
+
|
68
|
+
visit "/"
|
69
|
+
within "#alerts" do
|
70
|
+
assert !page.has_content?('Sorry, you provided the wrong credentials.')
|
71
|
+
end
|
63
72
|
end
|
64
73
|
|
65
74
|
test "user should be finally be able to access their settings, and just password is enough" do
|
@@ -23,6 +23,9 @@ class ResetTokenTest < ActionDispatch::IntegrationTest
|
|
23
23
|
reset_otp
|
24
24
|
|
25
25
|
assert_equal "/users/otp/token/edit", current_path
|
26
|
+
within "#alerts" do
|
27
|
+
assert page.has_content? 'Your token secret has been reset. Please confirm your new token secret below.'
|
28
|
+
end
|
26
29
|
end
|
27
30
|
|
28
31
|
test "generates new token secrets" do
|
@@ -43,6 +43,7 @@ class SignInTest < ActionDispatch::IntegrationTest
|
|
43
43
|
click_button "Submit Token"
|
44
44
|
|
45
45
|
assert_equal user_otp_credential_path, current_path
|
46
|
+
assert page.has_content? "The token you provided was invalid."
|
46
47
|
end
|
47
48
|
|
48
49
|
test "fail blank token authentication" do
|
@@ -53,6 +54,7 @@ class SignInTest < ActionDispatch::IntegrationTest
|
|
53
54
|
click_button "Submit Token"
|
54
55
|
|
55
56
|
assert_equal user_otp_credential_path, current_path
|
57
|
+
assert page.has_content? "You need to type in the token you generated with your device."
|
56
58
|
end
|
57
59
|
|
58
60
|
test "successful token authentication" do
|
@@ -78,4 +80,32 @@ class SignInTest < ActionDispatch::IntegrationTest
|
|
78
80
|
User.otp_authentication_timeout = old_timeout
|
79
81
|
assert_equal new_user_session_path, current_path
|
80
82
|
end
|
83
|
+
|
84
|
+
test "blank token flash message does not persist to successful authentication redirect." do
|
85
|
+
user = enable_otp_and_sign_in
|
86
|
+
|
87
|
+
fill_in "token", with: "123456"
|
88
|
+
click_button "Submit Token"
|
89
|
+
|
90
|
+
assert page.has_content?("The token you provided was invalid.")
|
91
|
+
|
92
|
+
fill_in "token", with: ROTP::TOTP.new(user.otp_auth_secret).at(Time.now)
|
93
|
+
click_button "Submit Token"
|
94
|
+
|
95
|
+
assert !page.has_content?("The token you provided was invalid.")
|
96
|
+
end
|
97
|
+
|
98
|
+
test "invalid token flash message does not persist to successful authentication redirect." do
|
99
|
+
user = enable_otp_and_sign_in
|
100
|
+
|
101
|
+
fill_in "token", with: ""
|
102
|
+
click_button "Submit Token"
|
103
|
+
|
104
|
+
assert page.has_content?("You need to type in the token you generated with your device.")
|
105
|
+
|
106
|
+
fill_in "token", with: ROTP::TOTP.new(user.otp_auth_secret).at(Time.now)
|
107
|
+
click_button "Submit Token"
|
108
|
+
|
109
|
+
assert !page.has_content?("You need to type in the token you generated with your device.")
|
110
|
+
end
|
81
111
|
end
|