devise-otp 0.8.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|