authentication-zero 2.9.1 → 2.10.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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/Gemfile.lock +1 -1
  4. data/README.md +7 -2
  5. data/lib/authentication_zero/version.rb +1 -1
  6. data/lib/generators/authentication/authentication_generator.rb +36 -67
  7. data/lib/generators/authentication/templates/controllers/api/application_controller.rb.tt +26 -0
  8. data/lib/generators/authentication/templates/controllers/api/identity/password_resets_controller.rb.tt +4 -4
  9. data/lib/generators/authentication/templates/controllers/html/application_controller.rb.tt +24 -0
  10. data/lib/generators/authentication/templates/controllers/html/identity/password_resets_controller.rb.tt +4 -4
  11. data/lib/generators/authentication/templates/controllers/html/sessions/sudos_controller.rb.tt +3 -3
  12. data/lib/generators/authentication/templates/controllers/html/sessions_controller_two_factor.rb.tt +41 -0
  13. data/lib/generators/authentication/templates/controllers/html/two_factor_authentication/challenges_controller.rb.tt +28 -0
  14. data/lib/generators/authentication/templates/controllers/html/two_factor_authentication/totps_controller.rb.tt +26 -0
  15. data/lib/generators/authentication/templates/erb/identity/password_resets/edit.html.erb.tt +3 -3
  16. data/lib/generators/authentication/templates/erb/passwords/edit.html.erb.tt +4 -4
  17. data/lib/generators/authentication/templates/erb/registrations/new.html.erb.tt +2 -2
  18. data/lib/generators/authentication/templates/erb/sessions/sudos/new.html.erb.tt +2 -2
  19. data/lib/generators/authentication/templates/erb/two_factor_authentication/challenges/new.html.erb.tt +16 -0
  20. data/lib/generators/authentication/templates/erb/two_factor_authentication/totps/new.html.erb.tt +28 -0
  21. data/lib/generators/authentication/templates/migrations/create_table_migration.rb.tt +7 -4
  22. data/lib/generators/authentication/templates/models/model.rb.tt +8 -8
  23. data/lib/generators/authentication/templates/models/session.rb.tt +2 -2
  24. data/lib/generators/authentication/templates/test_unit/application_system_test_case.rb.tt +15 -0
  25. data/lib/generators/authentication/templates/test_unit/controllers/api/identity/email_verifications_controller_test.rb.tt +0 -4
  26. data/lib/generators/authentication/templates/test_unit/controllers/api/identity/emails_controller_test.rb.tt +0 -4
  27. data/lib/generators/authentication/templates/test_unit/controllers/api/identity/password_resets_controller_test.rb.tt +2 -2
  28. data/lib/generators/authentication/templates/test_unit/controllers/api/passwords_controller_test.rb.tt +0 -4
  29. data/lib/generators/authentication/templates/test_unit/controllers/api/sessions/sudos_controller_test.rb.tt +0 -4
  30. data/lib/generators/authentication/templates/test_unit/controllers/api/sessions_controller_test.rb.tt +0 -4
  31. data/lib/generators/authentication/templates/test_unit/controllers/html/identity/email_verifications_controller_test.rb.tt +0 -4
  32. data/lib/generators/authentication/templates/test_unit/controllers/html/identity/emails_controller_test.rb.tt +0 -4
  33. data/lib/generators/authentication/templates/test_unit/controllers/html/identity/password_resets_controller_test.rb.tt +2 -2
  34. data/lib/generators/authentication/templates/test_unit/controllers/html/passwords_controller_test.rb.tt +0 -4
  35. data/lib/generators/authentication/templates/test_unit/controllers/html/sessions/sudos_controller_test.rb.tt +0 -4
  36. data/lib/generators/authentication/templates/test_unit/controllers/html/sessions_controller_test.rb.tt +0 -4
  37. data/lib/generators/authentication/templates/test_unit/system/identity/emails_test.rb.tt +0 -10
  38. data/lib/generators/authentication/templates/test_unit/system/identity/password_resets_test.rb.tt +2 -2
  39. data/lib/generators/authentication/templates/test_unit/system/passwords_test.rb.tt +0 -10
  40. data/lib/generators/authentication/templates/test_unit/system/sessions/sudos_test.rb.tt +0 -10
  41. data/lib/generators/authentication/templates/test_unit/system/sessions_test.rb.tt +4 -7
  42. data/lib/generators/authentication/templates/test_unit/test_helper.rb.tt +22 -0
  43. metadata +11 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 349df436a5358765a5f4537e5e37e79c566dfe575875f77380fe1a2eeb21096f
4
- data.tar.gz: f017571edaa6c887bcdecf4ffa023cc7d86830e0fba4ba3c7ad6dbd8626bf952
3
+ metadata.gz: c0ad4048d0c9df83173acebd1bbf770f519a7bb2a8b5b45c7e9af3c268904052
4
+ data.tar.gz: 06a897ebfc48122cfaf151a49b7412ef13b098069eb3bc7a2a4c088bcc711c90
5
5
  SHA512:
6
- metadata.gz: a70ba5553accd5f23b71ad58b58dd270f78636b28bca944decb062d16a31d83808a9dca86cd2b38f5f83317a6748c32ef25d3944613686ea215c05cdcc647dbe
7
- data.tar.gz: 2506decfeaa1e126d0160d11d787577ecff6c8afa6b4a27930a01ac65b9cfca2fa7ccdc82392dc488dafee102ebe4419ce1422450400810efcf7437fe1578454
6
+ metadata.gz: fd26a42853d553616f366e1ddc57439ce91ba039f2cf25a5d3c80be48a6b0f0e7fee6816ead70587780ebc8a358e74e53543c0b6438eb39d7baf818aacc5191c
7
+ data.tar.gz: 657bb45b7e9edfd6a036e9df9a2df41a2b2001199e65586d82548d7bd4ade28946be06ec3dbb28ba6fba100e275881c1a02dbb3de29b8546899393829b4cdc97
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## Authentication Zero 2.10.0 (March 2, 2022) ##
2
+
3
+ * Implement two-factor
4
+
1
5
  ## Authentication Zero 2.9.0 (March 2, 2022) ##
2
6
 
3
7
  * Implement trackable
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- authentication-zero (2.9.1)
4
+ authentication-zero (2.10.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -11,6 +11,7 @@ The purpose of authentication zero is to generate a pre-built authentication sys
11
11
  - Checks if a password has been found in any data breach (--pwned)
12
12
  - Authentication by cookie
13
13
  - Authentication by token (--api)
14
+ - Two factor authentication (--two-factor)
14
15
  - Social Login with OmniAuth (--omniauthable)
15
16
  - Ask password before sensitive data changes, aka: sudo
16
17
  - Reset the user password and send reset instructions
@@ -53,7 +54,7 @@ root "home#index"
53
54
  ```
54
55
 
55
56
  ```
56
- $ rails generate controller home index
57
+ rails generate controller home index
57
58
  ```
58
59
 
59
60
  Add these lines to your `app/views/home/index.html.erb`:
@@ -79,6 +80,10 @@ Add these lines to your `app/views/home/index.html.erb`:
79
80
  <%# link_to "Activity Log", authentications_events_path %>
80
81
  </div>
81
82
 
83
+ <div>
84
+ <%# link_to "Two-Factor Authentication", new_two_factor_authentication_totp_path %>
85
+ </div>
86
+
82
87
  <br>
83
88
 
84
89
  <%= button_to "Log out", Current.session, method: :delete %>
@@ -93,7 +98,7 @@ config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
93
98
  ## Usage
94
99
 
95
100
  ```
96
- $ rails generate authentication user
101
+ rails generate authentication user
97
102
  ```
98
103
 
99
104
  Then run `bundle install` again!
@@ -1,3 +1,3 @@
1
1
  module AuthenticationZero
2
- VERSION = "2.9.1"
2
+ VERSION = "2.10.0"
3
3
  end
@@ -9,6 +9,7 @@ class AuthenticationGenerator < Rails::Generators::NamedBase
9
9
  class_option :ratelimit, type: :boolean, desc: "Add request rate limiting"
10
10
  class_option :omniauthable, type: :boolean, desc: "Add social login support"
11
11
  class_option :trackable, type: :boolean, desc: "Add activity log support"
12
+ class_option :two_factor, type: :boolean, desc: "Add two factor authentication"
12
13
 
13
14
  source_root File.expand_path("templates", __dir__)
14
15
 
@@ -29,15 +30,20 @@ class AuthenticationGenerator < Rails::Generators::NamedBase
29
30
  gem "omniauth", comment: "Use OmniAuth to support multi-provider authentication [https://github.com/omniauth/omniauth]"
30
31
  gem "omniauth-rails_csrf_protection", comment: "Provides a mitigation against CVE-2015-9284 [https://github.com/cookpad/omniauth-rails_csrf_protection]"
31
32
  end
33
+
34
+ if two_factor?
35
+ gem "rotp", comment: "Use rotp for generating and validating one time passwords [https://github.com/mdp/rotp]"
36
+ gem "rqrcode", comment: "Use rqrcode for creating and rendering QR codes into various formats [https://github.com/whomwah/rqrcode]"
37
+ end
32
38
  end
33
39
 
34
40
  def create_configuration_files
35
- copy_file "config/redis/shared.yml", "config/redis/shared.yml" if options.lockable?
36
- copy_file "config/initializers/omniauth.rb", "config/initializers/omniauth.rb" if omniauthable?
41
+ copy_file "config/redis/shared.yml", "config/redis/shared.yml" if options.lockable?
42
+ copy_file "config/initializers/omniauth.rb", "config/initializers/omniauth.rb" if omniauthable?
37
43
  end
38
44
 
39
45
  def add_environment_configurations
40
- ratelimit_code = <<~CODE
46
+ ratelimit_code = <<~CODE
41
47
  # Rate limit general requests by IP address in a rate of 1000 requests per hour
42
48
  config.middleware.use(Rack::Ratelimit, name: "General", rate: [1000, 1.hour], redis: Redis.new, logger: Rails.logger) { |env| ActionDispatch::Request.new(env).ip }
43
49
  CODE
@@ -63,68 +69,19 @@ class AuthenticationGenerator < Rails::Generators::NamedBase
63
69
  template "test_unit/fixtures.yml", "test/fixtures/#{fixture_file_name}.yml"
64
70
  end
65
71
 
66
- def add_application_controller_methods
67
- api_code = <<~CODE
68
- include ActionController::HttpAuthentication::Token::ControllerMethods
69
-
70
- before_action :set_current_request_details
71
- before_action :authenticate
72
-
73
- def require_sudo
74
- if Current.session.sudo_at < 30.minutes.ago
75
- render json: { error: "Enter your password to continue" }, status: :forbidden
76
- end
77
- end
78
-
79
- private
80
- def authenticate
81
- if session = authenticate_with_http_token { |token, _| Session.find_signed(token) }
82
- Current.session = session
83
- else
84
- request_http_token_authentication
85
- end
86
- end
87
-
88
- def set_current_request_details
89
- Current.user_agent = request.user_agent
90
- Current.ip_address = request.ip
91
- end
92
- CODE
93
-
94
- html_code = <<~CODE
95
- before_action :set_current_request_details
96
- before_action :authenticate
97
-
98
- def require_sudo
99
- if Current.session.sudo_at < 30.minutes.ago
100
- redirect_to new_sessions_sudo_path(proceed_to_url: request.url)
101
- end
102
- end
103
-
104
- private
105
- def authenticate
106
- if session = Session.find_by_id(cookies.signed[:session_token])
107
- Current.session = session
108
- else
109
- redirect_to sign_in_path
110
- end
111
- end
112
-
113
- def set_current_request_details
114
- Current.user_agent = request.user_agent
115
- Current.ip_address = request.ip
116
- end
117
- CODE
72
+ def create_controllers
73
+ template "controllers/#{format_folder}/application_controller.rb", "app/controllers/application_controller.rb", force: true
118
74
 
119
- inject_code = options.api? ? api_code : html_code
120
- inject_into_class "app/controllers/application_controller.rb", "ApplicationController", optimize_indentation(inject_code, 2), verbose: false
121
- end
75
+ if two_factor?
76
+ directory "controllers/#{format_folder}/two_factor_authentication", "app/controllers/two_factor_authentication"
77
+ template "controllers/#{format_folder}/sessions_controller_two_factor.rb", "app/controllers/sessions_controller.rb"
78
+ else
79
+ template "controllers/#{format_folder}/sessions_controller.rb", "app/controllers/sessions_controller.rb"
80
+ end
122
81
 
123
- def create_controllers
124
82
  directory "controllers/#{format_folder}/identity", "app/controllers/identity"
125
83
  template "controllers/#{format_folder}/passwords_controller.rb", "app/controllers/passwords_controller.rb"
126
84
  template "controllers/#{format_folder}/registrations_controller.rb", "app/controllers/registrations_controller.rb"
127
- template "controllers/#{format_folder}/sessions_controller.rb", "app/controllers/sessions_controller.rb"
128
85
  template "controllers/#{format_folder}/sessions/sudos_controller.rb", "app/controllers/sessions/sudos_controller.rb"
129
86
  template "controllers/#{format_folder}/sessions/omniauth_controller.rb", "app/controllers/sessions/omniauth_controller.rb" if omniauthable?
130
87
  template "controllers/#{format_folder}/authentications/events_controller.rb", "app/controllers/authentications/events_controller.rb" if options.trackable?
@@ -142,6 +99,7 @@ class AuthenticationGenerator < Rails::Generators::NamedBase
142
99
  directory "erb/passwords", "app/views/passwords"
143
100
  directory "erb/registrations", "app/views/registrations"
144
101
  directory "erb/sessions", "app/views/sessions"
102
+ directory "erb/two_factor_authentication", "app/views/two_factor_authentication" if two_factor?
145
103
  directory "erb/authentications/events", "app/views/authentications/events" if options.trackable?
146
104
  end
147
105
  end
@@ -153,29 +111,36 @@ class AuthenticationGenerator < Rails::Generators::NamedBase
153
111
  def add_routes
154
112
  if omniauthable?
155
113
  route "post '/auth/:provider/callback', to: 'sessions/omniauth#create'"
156
- route "get '/auth/:provider/callback', to: 'sessions/omniauth#create'"
157
- route "get '/auth/failure', to: 'sessions/omniauth#failure'"
114
+ route "get '/auth/:provider/callback', to: 'sessions/omniauth#create'"
115
+ route "get '/auth/failure', to: 'sessions/omniauth#failure'"
116
+ end
117
+
118
+ if two_factor?
119
+ route "resource :totp, only: [:new, :create]", namespace: :two_factor_authentication
120
+ route "resource :challenge, only: [:new, :create]", namespace: :two_factor_authentication
158
121
  end
159
122
 
160
123
  if options.trackable?
161
124
  route "resources :events, only: :index", namespace: :authentications
162
125
  end
163
126
 
164
- route "resource :password_reset, only: [:new, :edit, :create, :update]", namespace: :identity
127
+ route "resource :password_reset, only: [:new, :edit, :create, :update]", namespace: :identity
165
128
  route "resource :email_verification, only: [:edit, :create]", namespace: :identity
166
- route "resource :email, only: [:edit, :update]", namespace: :identity
129
+ route "resource :email, only: [:edit, :update]", namespace: :identity
167
130
  route "resource :sudo, only: [:new, :create]", namespace: :sessions
131
+ route "resource :password, only: [:edit, :update]"
168
132
  route "resources :sessions, only: [:index, :show, :destroy]"
169
- route "resource :password, only: [:edit, :update]"
170
133
  route "post 'sign_up', to: 'registrations#create'"
171
- route "get 'sign_up', to: 'registrations#new'" unless options.api?
134
+ route "get 'sign_up', to: 'registrations#new'" unless options.api?
172
135
  route "post 'sign_in', to: 'sessions#create'"
173
- route "get 'sign_in', to: 'sessions#new'" unless options.api?
136
+ route "get 'sign_in', to: 'sessions#new'" unless options.api?
174
137
  end
175
138
 
176
139
  def create_test_files
177
140
  directory "test_unit/controllers/#{format_folder}", "test/controllers"
178
141
  directory "test_unit/system", "test/system" unless options.api?
142
+ template "test_unit/test_helper.rb", "test/test_helper.rb", force: true
143
+ template "test_unit/application_system_test_case.rb", "test/application_system_test_case.rb", force: true unless options.api?
179
144
  end
180
145
 
181
146
  private
@@ -186,4 +151,8 @@ class AuthenticationGenerator < Rails::Generators::NamedBase
186
151
  def omniauthable?
187
152
  options.omniauthable? && !options.api?
188
153
  end
154
+
155
+ def two_factor?
156
+ options.two_factor? && !options.api?
157
+ end
189
158
  end
@@ -0,0 +1,26 @@
1
+ class ApplicationController < ActionController::API
2
+ include ActionController::HttpAuthentication::Token::ControllerMethods
3
+
4
+ before_action :set_current_request_details
5
+ before_action :authenticate
6
+
7
+ def require_sudo
8
+ if Current.session.sudo_at < 30.minutes.ago
9
+ render json: { error: "Enter your password to continue" }, status: :forbidden
10
+ end
11
+ end
12
+
13
+ private
14
+ def authenticate
15
+ if session = authenticate_with_http_token { |token, _| Session.find_signed(token) }
16
+ Current.session = session
17
+ else
18
+ request_http_token_authentication
19
+ end
20
+ end
21
+
22
+ def set_current_request_details
23
+ Current.user_agent = request.user_agent
24
+ Current.ip_address = request.ip
25
+ end
26
+ end
@@ -1,9 +1,9 @@
1
1
  class Identity::PasswordResetsController < ApplicationController
2
2
  skip_before_action :authenticate
3
3
 
4
- <% if options.lockable? -%>
4
+ <%- if options.lockable? -%>
5
5
  before_action :require_locking, only: :create
6
- <% end -%>
6
+ <%- end -%>
7
7
  before_action :set_<%= singular_table_name %>, only: :update
8
8
 
9
9
  def create
@@ -32,11 +32,11 @@ class Identity::PasswordResetsController < ApplicationController
32
32
  def <%= "#{singular_table_name}_params" %>
33
33
  params.permit(:password, :password_confirmation)
34
34
  end
35
- <% if options.lockable? %>
35
+ <%- if options.lockable? %>
36
36
  def require_locking
37
37
  Locking.lock_on("password_reset_lock:#{request.remote_ip}", wait: 1.hour, attempts: 10) do
38
38
  render json: { error: "You've exceeded the maximum number of attempts" }, status: :too_many_requests
39
39
  end
40
40
  end
41
- <% end -%>
41
+ <%- end -%>
42
42
  end
@@ -0,0 +1,24 @@
1
+ class ApplicationController < ActionController::Base
2
+ before_action :set_current_request_details
3
+ before_action :authenticate
4
+
5
+ def require_sudo
6
+ if Current.session.sudo_at < 30.minutes.ago
7
+ redirect_to new_sessions_sudo_path(proceed_to_url: request.url)
8
+ end
9
+ end
10
+
11
+ private
12
+ def authenticate
13
+ if session = Session.find_by_id(cookies.signed[:session_token])
14
+ Current.session = session
15
+ else
16
+ redirect_to sign_in_path
17
+ end
18
+ end
19
+
20
+ def set_current_request_details
21
+ Current.user_agent = request.user_agent
22
+ Current.ip_address = request.ip
23
+ end
24
+ end
@@ -1,9 +1,9 @@
1
1
  class Identity::PasswordResetsController < ApplicationController
2
2
  skip_before_action :authenticate
3
3
 
4
- <% if options.lockable? -%>
4
+ <%- if options.lockable? -%>
5
5
  before_action :require_locking, only: :create
6
- <% end -%>
6
+ <%- end -%>
7
7
  before_action :set_<%= singular_table_name %>, only: %i[ edit update ]
8
8
 
9
9
  def new
@@ -39,11 +39,11 @@ class Identity::PasswordResetsController < ApplicationController
39
39
  def <%= "#{singular_table_name}_params" %>
40
40
  params.permit(:password, :password_confirmation)
41
41
  end
42
- <% if options.lockable? %>
42
+ <%- if options.lockable? %>
43
43
  def require_locking
44
44
  Locking.lock_on("password_reset_lock:#{request.remote_ip}", wait: 1.hour, attempts: 10) do
45
45
  redirect_to new_identity_password_reset_path, alert: "You've exceeded the maximum number of attempts"
46
46
  end
47
47
  end
48
- <% end -%>
48
+ <%- end -%>
49
49
  end
@@ -5,11 +5,11 @@ class Sessions::SudosController < ApplicationController
5
5
  def create
6
6
  session = Current.session
7
7
 
8
- <% if omniauthable? -%>
8
+ <%- if omniauthable? -%>
9
9
  if session.<%= singular_table_name %>.authenticate(params[:password]) || session.<%= singular_table_name %>.provider
10
- <% else -%>
10
+ <%- else -%>
11
11
  if session.<%= singular_table_name %>.authenticate(params[:password])
12
- <% end -%>
12
+ <%- end -%>
13
13
  session.update!(sudo_at: Time.current); redirect_to(params[:proceed_to_url])
14
14
  else
15
15
  redirect_to new_sessions_sudo_path(proceed_to_url: params[:proceed_to_url]), alert: "The password you entered is incorrect"
@@ -0,0 +1,41 @@
1
+ class SessionsController < ApplicationController
2
+ skip_before_action :authenticate, only: %i[ new create ]
3
+
4
+ before_action :set_session, only: :destroy
5
+
6
+ def index
7
+ @sessions = Current.<%= singular_table_name %>.sessions.order(created_at: :desc)
8
+ end
9
+
10
+ def new
11
+ @<%= singular_table_name %> = <%= class_name %>.new
12
+ end
13
+
14
+ def create
15
+ <%= singular_table_name %> = <%= class_name %>.find_by(email: params[:email])
16
+
17
+ if <%= singular_table_name %> && <%= singular_table_name %>.authenticate(params[:password])
18
+ if <%= singular_table_name %>.otp_secret
19
+ signed_id = <%= singular_table_name %>.signed_id(purpose: :authentication_challenge, expires_in: 20.minutes)
20
+
21
+ redirect_to new_two_factor_authentication_challenge_path(token: signed_id)
22
+ else
23
+ @session = <%= singular_table_name %>.sessions.create!
24
+ cookies.signed.permanent[:session_token] = { value: @session.id, httponly: true }
25
+
26
+ redirect_to root_path, notice: "Signed in successfully"
27
+ end
28
+ else
29
+ redirect_to sign_in_path(email_hint: params[:email]), alert: "That email or password is incorrect"
30
+ end
31
+ end
32
+
33
+ def destroy
34
+ @session.destroy; redirect_to(sessions_path, notice: "That session has been logged out")
35
+ end
36
+
37
+ private
38
+ def set_session
39
+ @session = Current.<%= singular_table_name %>.sessions.find(params[:id])
40
+ end
41
+ end
@@ -0,0 +1,28 @@
1
+ class TwoFactorAuthentication::ChallengesController < ApplicationController
2
+ skip_before_action :authenticate
3
+
4
+ before_action :set_<%= singular_table_name %>
5
+
6
+ def new
7
+ end
8
+
9
+ def create
10
+ @totp = ROTP::TOTP.new(@<%= singular_table_name %>.otp_secret, issuer: "YourAppName")
11
+
12
+ if @totp.verify(params[:code], drift_behind: 15)
13
+ session = @<%= singular_table_name %>.sessions.create!
14
+ cookies.signed.permanent[:session_token] = { value: session.id, httponly: true }
15
+
16
+ redirect_to root_path, notice: "Signed in successfully"
17
+ else
18
+ redirect_to new_two_factor_authentication_challenge_path(token: params[:token]), alert: "That code didn't work. Please try again"
19
+ end
20
+ end
21
+
22
+ private
23
+ def set_<%= singular_table_name %>
24
+ @<%= singular_table_name %> = <%= class_name %>.find_signed!(params[:token], purpose: :authentication_challenge)
25
+ rescue
26
+ redirect_to sign_in_path, alert: "That's taking too long. Please re-enter your password and try again"
27
+ end
28
+ end
@@ -0,0 +1,26 @@
1
+ class TwoFactorAuthentication::TotpsController < ApplicationController
2
+ before_action :require_sudo
3
+ before_action :set_<%= singular_table_name %>
4
+ before_action :set_totp
5
+
6
+ def new
7
+ @qr_code = RQRCode::QRCode.new(@totp.provisioning_uri(@<%= singular_table_name %>.email))
8
+ end
9
+
10
+ def create
11
+ if @totp.verify(params[:code], drift_behind: 15)
12
+ @<%= singular_table_name %>.update! otp_secret: params[:secret]
13
+ redirect_to root_path, notice: "2FA is enabled on your account"
14
+ else
15
+ redirect_to two_factor_authentication_totp_path, alert: "That code didn't work. Please try again"
16
+ end
17
+ end
18
+
19
+ def set_<%= singular_table_name %>
20
+ @<%= singular_table_name %> = Current.<%= singular_table_name %>
21
+ end
22
+
23
+ def set_totp
24
+ @totp = ROTP::TOTP.new(params[:secret] || ROTP::Base32.random, issuer: "YourAppName")
25
+ end
26
+ end
@@ -13,17 +13,17 @@
13
13
  </div>
14
14
  <%% end %>
15
15
 
16
- <%%= hidden_field_tag :token, params[:token] %>
16
+ <%%= form.hidden_field :token, value: params[:token] %>
17
17
 
18
18
  <div>
19
19
  <%%= form.label :password, "New password", style: "display: block" %>
20
- <%%= form.password_field :password, autofocus: true, autocomplete: "new-password" %>
20
+ <%%= form.password_field :password, required: true, autofocus: true, autocomplete: "new-password" %>
21
21
  <div>12 characters minimum.</div>
22
22
  </div>
23
23
 
24
24
  <div>
25
25
  <%%= form.label :password_confirmation, "Confirm new password", style: "display: block" %>
26
- <%%= form.password_field :password_confirmation, autocomplete: "new-password" %>
26
+ <%%= form.password_field :password_confirmation, required: true, autocomplete: "new-password" %>
27
27
  </div>
28
28
 
29
29
  <div>
@@ -16,19 +16,19 @@
16
16
  <%% end %>
17
17
 
18
18
  <div>
19
- <%%= label_tag :current_password, nil, style: "display: block" %>
20
- <%%= password_field_tag :current_password, nil, autofocus: true, autocomplete: "current-password" %>
19
+ <%%= form.label :current_password, style: "display: block" %>
20
+ <%%= form.password_field :current_password, required: true, autofocus: true, autocomplete: "current-password" %>
21
21
  </div>
22
22
 
23
23
  <div>
24
24
  <%%= form.label :password, "New password", style: "display: block" %>
25
- <%%= form.password_field :password, autocomplete: "new-password" %>
25
+ <%%= form.password_field :password, required: true, autocomplete: "new-password" %>
26
26
  <div>12 characters minimum.</div>
27
27
  </div>
28
28
 
29
29
  <div>
30
30
  <%%= form.label :password_confirmation, "Confirm new password", style: "display: block" %>
31
- <%%= form.password_field :password_confirmation, autocomplete: "new-password" %>
31
+ <%%= form.password_field :password_confirmation, required: true, autocomplete: "new-password" %>
32
32
  </div>
33
33
 
34
34
  <div>
@@ -20,13 +20,13 @@
20
20
 
21
21
  <div>
22
22
  <%%= form.label :password, style: "display: block" %>
23
- <%%= form.password_field :password, autocomplete: "new-password" %>
23
+ <%%= form.password_field :password, required: true, autocomplete: "new-password" %>
24
24
  <div>12 characters minimum.</div>
25
25
  </div>
26
26
 
27
27
  <div>
28
28
  <%%= form.label :password_confirmation, style: "display: block" %>
29
- <%%= form.password_field :password_confirmation, autocomplete: "new-password" %>
29
+ <%%= form.password_field :password_confirmation, required: true, autocomplete: "new-password" %>
30
30
  </div>
31
31
 
32
32
  <div>
@@ -4,10 +4,10 @@
4
4
 
5
5
  <%%= form_with(url: sessions_sudo_path) do |form| %>
6
6
 
7
- <%%= hidden_field_tag :proceed_to_url, params[:proceed_to_url] %>
7
+ <%%= form.hidden_field :proceed_to_url, value: params[:proceed_to_url] %>
8
8
 
9
9
  <div>
10
- <%%= password_field_tag :password, nil, autofocus: true, autocomplete: "current-password" %>
10
+ <%%= form.password_field :password, required: true, autofocus: true, autocomplete: "current-password" %>
11
11
  </div>
12
12
 
13
13
  <div>
@@ -0,0 +1,16 @@
1
+ <p style="color: red"><%%= alert %></p>
2
+
3
+ <%%= form_with(url: two_factor_authentication_challenge_path) do |form| %>
4
+ <%%= form.hidden_field :token, value: params[:token] %>
5
+
6
+ <div>
7
+ <%%= form.label :code do %>
8
+ <h1>Next, open the 2FA authenticator app on your phone and type the six digit code below:</h1>
9
+ <%% end %>
10
+ <%%= form.text_field :code, autofocus: true, required: true, autocomplete: :off %>
11
+ </div>
12
+
13
+ <div>
14
+ <%%= form.submit "Verify" %>
15
+ </div>
16
+ <%% end %>
@@ -0,0 +1,28 @@
1
+ <p style="color: red"><%%= alert %></p>
2
+
3
+ <h1>Upgrade your security with 2FA</h1>
4
+
5
+ <h2>Step 1: Get an Authenticator App</h2>
6
+ <p>First, you'll need a 2FA authenticator app on your phone. <strong>If you already have one, skip to step 2.</strong></p>
7
+ <p><strong>If you don't have one, or you aren't sure, we recommend Microsoft Authenticator</strong>. You can download it free on the Apple App Store for iPhone, or Google Play Store for Android. Please grab your phone, search the store, and install it now.</p>
8
+
9
+ <h2>Step 2: Scan + Enter the Code</h2>
10
+ <p>Next, open the authenticator app, tap "Scan QR code" or "+", and, when it asks, point your phone's camera at this QR code picture below.</p>
11
+
12
+ <figure>
13
+ <%%= image_tag @qr_code.as_png(resize_exactly_to: 200).to_data_url%>
14
+ <figcaption>Point your camera here</figcaption>
15
+ </figure>
16
+
17
+ <%%= form_with(url: two_factor_authentication_totp_path) do |form| %>
18
+ <%%= form.hidden_field :secret, value: @totp.secret %>
19
+
20
+ <div>
21
+ <%%= form.label :code, "After scanning with your camera, the app will generate a six-digit code. Enter it here:", style: "display: block" %>
22
+ <%%= form.text_field :code, autofocus: true, required: true, autocomplete: :off %>
23
+ </div>
24
+
25
+ <div>
26
+ <%%= form.submit "Verify and active" %>
27
+ </div>
28
+ <%% end %>
@@ -5,17 +5,20 @@ class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Mi
5
5
  t.string :password_digest, null: false
6
6
 
7
7
  t.boolean :verified, null: false, default: false
8
- <% if omniauthable? %>
8
+ <%- if two_factor? %>
9
+ t.string :otp_secret
10
+ <%- end -%>
11
+ <%- if omniauthable? %>
9
12
  t.string :provider
10
13
  t.string :uid
11
- <% end -%>
14
+ <%- end -%>
12
15
 
13
16
  t.timestamps
14
17
  end
15
18
 
16
19
  add_index :<%= table_name %>, :email, unique: true
17
- <% if omniauthable? -%>
20
+ <%- if omniauthable? -%>
18
21
  add_index :<%= table_name %>, [:provider, :uid], unique: true
19
- <% end -%>
22
+ <%- end -%>
20
23
  end
21
24
  end
@@ -2,18 +2,18 @@ class <%= class_name %> < ApplicationRecord
2
2
  has_secure_password
3
3
 
4
4
  has_many :sessions, dependent: :destroy
5
- <% if options.trackable? -%>
5
+ <%- if options.trackable? -%>
6
6
  has_many :events, dependent: :destroy
7
- <% end -%>
7
+ <%- end -%>
8
8
 
9
9
  validates :email, presence: true, uniqueness: true
10
10
  validates_format_of :email, with: /\A[^@\s]+@[^@\s]+\z/
11
11
 
12
- validates_length_of :password, minimum: 12, allow_blank: true
13
- validates_format_of :password, with: /(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])/, allow_blank: true, message: "might easily be guessed"
14
- <% if options.pwned? -%>
12
+ validates_length_of :password, minimum: 12, allow_nil: true
13
+ validates_format_of :password, with: /(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])/, allow_nil: true, message: "might easily be guessed"
14
+ <%- if options.pwned? -%>
15
15
  validates :password, not_pwned: { message: "might easily be guessed" }
16
- <% end -%>
16
+ <%- end -%>
17
17
 
18
18
  before_validation do
19
19
  self.email = email.downcase.strip
@@ -30,7 +30,7 @@ class <%= class_name %> < ApplicationRecord
30
30
  after_save_commit if: :email_previously_changed? do
31
31
  IdentityMailer.with(user: self).email_verify_confirmation.deliver_later
32
32
  end
33
- <% if options.trackable? %>
33
+ <%- if options.trackable? %>
34
34
  after_save_commit if: :email_previously_changed? do
35
35
  events.create! action: "email_verification_requested"
36
36
  end
@@ -42,5 +42,5 @@ class <%= class_name %> < ApplicationRecord
42
42
  after_update if: :verified_previously_changed? do
43
43
  events.create! action: "email_verified" if verified?
44
44
  end
45
- <% end -%>
45
+ <%- end -%>
46
46
  end
@@ -10,7 +10,7 @@ class Session < ApplicationRecord
10
10
  after_create_commit do
11
11
  SessionMailer.with(session: self).signed_in_notification.deliver_later
12
12
  end
13
- <% if options.trackable? %>
13
+ <%- if options.trackable? %>
14
14
  after_create do
15
15
  <%= singular_table_name %>.events.create! action: "signed_in"
16
16
  end
@@ -18,5 +18,5 @@ class Session < ApplicationRecord
18
18
  after_destroy do
19
19
  <%= singular_table_name %>.events.create! action: "signed_out"
20
20
  end
21
- <% end -%>
21
+ <%- end -%>
22
22
  end
@@ -0,0 +1,15 @@
1
+ require "test_helper"
2
+
3
+ class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
4
+ driven_by :selenium, using: :chrome, screen_size: [1400, 1400]
5
+
6
+ def sign_in_as(<%= singular_table_name %>)
7
+ visit sign_in_url
8
+ fill_in :email, with: <%= singular_table_name %>.email
9
+ fill_in :password, with: "Secret1*3*5*"
10
+ click_on "Sign in"
11
+
12
+ assert_current_path root_url
13
+ return <%= singular_table_name %>
14
+ end
15
+ end
@@ -37,8 +37,4 @@ class Identity::EmailVerificationsControllerTest < ActionDispatch::IntegrationTe
37
37
  assert_response :bad_request
38
38
  assert_equal "That email verification link is invalid", response.parsed_body["error"]
39
39
  end
40
-
41
- def sign_in_as(<%= singular_table_name %>)
42
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
43
- end
44
40
  end
@@ -18,8 +18,4 @@ class Identity::EmailsControllerTest < ActionDispatch::IntegrationTest
18
18
  assert_response :forbidden
19
19
  assert_equal "Enter your password to continue", response.parsed_body["error"]
20
20
  end
21
-
22
- def sign_in_as(<%= singular_table_name %>)
23
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
24
- end
25
21
  end
@@ -6,9 +6,9 @@ class Identity::PasswordResetsControllerTest < ActionDispatch::IntegrationTest
6
6
  @sid = @<%= singular_table_name %>.signed_id(purpose: :password_reset, expires_in: 20.minutes)
7
7
  @sid_exp = @<%= singular_table_name %>.signed_id(purpose: :password_reset, expires_in: 0.minutes)
8
8
  end
9
- <% if options.lockable? %>
9
+ <%- if options.lockable? %>
10
10
  teardown { Kredis.clear_all }
11
- <% end -%>
11
+ <%- end -%>
12
12
 
13
13
  test "should send a password reset email" do
14
14
  assert_enqueued_email_with IdentityMailer, :password_reset_provision, args: { <%= singular_table_name %>: @<%= singular_table_name %> } do
@@ -16,8 +16,4 @@ class PasswordsControllerTest < ActionDispatch::IntegrationTest
16
16
  assert_response :bad_request
17
17
  assert_equal "The current password you entered is incorrect", response.parsed_body["error"]
18
18
  end
19
-
20
- def sign_in_as(<%= singular_table_name %>)
21
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
22
- end
23
19
  end
@@ -17,8 +17,4 @@ class Sessions::SudosControllerTest < ActionDispatch::IntegrationTest
17
17
  assert_response :bad_request
18
18
  assert_equal "The password you entered is incorrect", response.parsed_body["error"]
19
19
  end
20
-
21
- def sign_in_as(<%= singular_table_name %>)
22
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
23
- end
24
20
  end
@@ -31,8 +31,4 @@ class SessionsControllerTest < ActionDispatch::IntegrationTest
31
31
  delete session_url(@<%= singular_table_name %>.sessions.last), headers: { "Authorization" => "Bearer #{@token}" }
32
32
  assert_response :no_content
33
33
  end
34
-
35
- def sign_in_as(<%= singular_table_name %>)
36
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
37
- end
38
34
  end
@@ -37,8 +37,4 @@ class Identity::EmailVerificationsControllerTest < ActionDispatch::IntegrationTe
37
37
  assert_redirected_to edit_identity_email_url
38
38
  assert_equal "That email verification link is invalid", flash[:alert]
39
39
  end
40
-
41
- def sign_in_as(<%= singular_table_name %>)
42
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); <%= singular_table_name %>
43
- end
44
40
  end
@@ -28,8 +28,4 @@ class Identity::EmailsControllerTest < ActionDispatch::IntegrationTest
28
28
  patch identity_email_url, params: { email: "new_email@hey.com" }
29
29
  assert_redirected_to new_sessions_sudo_url(proceed_to_url: identity_email_url)
30
30
  end
31
-
32
- def sign_in_as(<%= singular_table_name %>)
33
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); <%= singular_table_name %>
34
- end
35
31
  end
@@ -6,9 +6,9 @@ class Identity::PasswordResetsControllerTest < ActionDispatch::IntegrationTest
6
6
  @sid = @<%= singular_table_name %>.signed_id(purpose: :password_reset, expires_in: 20.minutes)
7
7
  @sid_exp = @<%= singular_table_name %>.signed_id(purpose: :password_reset, expires_in: 0.minutes)
8
8
  end
9
- <% if options.lockable? %>
9
+ <%- if options.lockable? %>
10
10
  teardown { Kredis.clear_all }
11
- <% end -%>
11
+ <%- end -%>
12
12
 
13
13
  test "should get new" do
14
14
  get new_identity_password_reset_url
@@ -21,8 +21,4 @@ class PasswordsControllerTest < ActionDispatch::IntegrationTest
21
21
  assert_redirected_to edit_password_url
22
22
  assert_equal "The current password you entered is incorrect", flash[:alert]
23
23
  end
24
-
25
- def sign_in_as(<%= singular_table_name %>)
26
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); <%= singular_table_name %>
27
- end
28
24
  end
@@ -19,8 +19,4 @@ class Sessions::SudosControllerTest < ActionDispatch::IntegrationTest
19
19
  post sessions_sudo_url, params: { password: "SecretWrong1*3", proceed_to_url: edit_password_url }
20
20
  assert_redirected_to new_sessions_sudo_url(proceed_to_url: edit_password_url)
21
21
  end
22
-
23
- def sign_in_as(<%= singular_table_name %>)
24
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
25
- end
26
22
  end
@@ -45,8 +45,4 @@ class SessionsControllerTest < ActionDispatch::IntegrationTest
45
45
  follow_redirect!
46
46
  assert_redirected_to sign_in_url
47
47
  end
48
-
49
- def sign_in_as(<%= singular_table_name %>)
50
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); <%= singular_table_name %>
51
- end
52
48
  end
@@ -22,14 +22,4 @@ class Identity::EmailsTest < ApplicationSystemTestCase
22
22
 
23
23
  assert_text "We sent a verification email to your email address"
24
24
  end
25
-
26
- def sign_in_as(<%= singular_table_name %>)
27
- visit sign_in_url
28
- fill_in :email, with: <%= singular_table_name %>.email
29
- fill_in :password, with: "Secret1*3*5*"
30
- click_on "Sign in"
31
-
32
- assert_current_path root_url
33
- return <%= singular_table_name %>
34
- end
35
25
  end
@@ -5,9 +5,9 @@ class Identity::PasswordResetsTest < ApplicationSystemTestCase
5
5
  @<%= singular_table_name %> = <%= table_name %>(:lazaro_nixon)
6
6
  @sid = @<%= singular_table_name %>.signed_id(purpose: :password_reset, expires_in: 20.minutes)
7
7
  end
8
- <% if options.lockable? %>
8
+ <%- if options.lockable? %>
9
9
  teardown { Kredis.clear_all }
10
- <% end -%>
10
+ <%- end -%>
11
11
 
12
12
  test "sending a password reset email" do
13
13
  visit sign_in_url
@@ -15,14 +15,4 @@ class PasswordsTest < ApplicationSystemTestCase
15
15
 
16
16
  assert_text "Your password has been changed"
17
17
  end
18
-
19
- def sign_in_as(<%= singular_table_name %>)
20
- visit sign_in_url
21
- fill_in :email, with: <%= singular_table_name %>.email
22
- fill_in :password, with: "Secret1*3*5*"
23
- click_on "Sign in"
24
-
25
- assert_current_path root_url
26
- return <%= singular_table_name %>
27
- end
28
18
  end
@@ -12,14 +12,4 @@ class Sessions::SudosTest < ApplicationSystemTestCase
12
12
 
13
13
  assert_selector "h1", text: "Change your password"
14
14
  end
15
-
16
- def sign_in_as(<%= singular_table_name %>)
17
- visit sign_in_url
18
- fill_in :email, with: <%= singular_table_name %>.email
19
- fill_in :password, with: "Secret1*3*5*"
20
- click_on "Sign in"
21
-
22
- assert_current_path root_url
23
- return <%= singular_table_name %>
24
- end
25
15
  end
@@ -21,13 +21,10 @@ class SessionsTest < ApplicationSystemTestCase
21
21
  assert_text "Signed in successfully"
22
22
  end
23
23
 
24
- def sign_in_as(<%= singular_table_name %>)
25
- visit sign_in_url
26
- fill_in :email, with: <%= singular_table_name %>.email
27
- fill_in :password, with: "Secret1*3*5*"
28
- click_on "Sign in"
24
+ test "signing out" do
25
+ sign_in_as @<%= singular_table_name %>
29
26
 
30
- assert_current_path root_url
31
- return <%= singular_table_name %>
27
+ click_on "Log out"
28
+ assert_text "That session has been logged out"
32
29
  end
33
30
  end
@@ -0,0 +1,22 @@
1
+ ENV["RAILS_ENV"] ||= "test"
2
+ require_relative "../config/environment"
3
+ require "rails/test_help"
4
+
5
+ class ActiveSupport::TestCase
6
+ # Run tests in parallel with specified workers
7
+ parallelize(workers: :number_of_processors)
8
+
9
+ # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
10
+ fixtures :all
11
+
12
+ # Add more helper methods to be used by all tests here...
13
+ <%- if options.api? -%>
14
+ def sign_in_as(<%= singular_table_name %>)
15
+ post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
16
+ end
17
+ <%- else -%>
18
+ def sign_in_as(<%= singular_table_name %>)
19
+ post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); <%= singular_table_name %>
20
+ end
21
+ <%- end -%>
22
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: authentication-zero
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.9.1
4
+ version: 2.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nixon
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-03-07 00:00:00.000000000 Z
11
+ date: 2022-03-14 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
@@ -35,6 +35,7 @@ files:
35
35
  - lib/generators/authentication/authentication_generator.rb
36
36
  - lib/generators/authentication/templates/config/initializers/omniauth.rb
37
37
  - lib/generators/authentication/templates/config/redis/shared.yml
38
+ - lib/generators/authentication/templates/controllers/api/application_controller.rb.tt
38
39
  - lib/generators/authentication/templates/controllers/api/authentications/events_controller.rb.tt
39
40
  - lib/generators/authentication/templates/controllers/api/identity/email_verifications_controller.rb.tt
40
41
  - lib/generators/authentication/templates/controllers/api/identity/emails_controller.rb.tt
@@ -43,6 +44,7 @@ files:
43
44
  - lib/generators/authentication/templates/controllers/api/registrations_controller.rb.tt
44
45
  - lib/generators/authentication/templates/controllers/api/sessions/sudos_controller.rb.tt
45
46
  - lib/generators/authentication/templates/controllers/api/sessions_controller.rb.tt
47
+ - lib/generators/authentication/templates/controllers/html/application_controller.rb.tt
46
48
  - lib/generators/authentication/templates/controllers/html/authentications/events_controller.rb.tt
47
49
  - lib/generators/authentication/templates/controllers/html/identity/email_verifications_controller.rb.tt
48
50
  - lib/generators/authentication/templates/controllers/html/identity/emails_controller.rb.tt
@@ -52,6 +54,9 @@ files:
52
54
  - lib/generators/authentication/templates/controllers/html/sessions/omniauth_controller.rb.tt
53
55
  - lib/generators/authentication/templates/controllers/html/sessions/sudos_controller.rb.tt
54
56
  - lib/generators/authentication/templates/controllers/html/sessions_controller.rb.tt
57
+ - lib/generators/authentication/templates/controllers/html/sessions_controller_two_factor.rb.tt
58
+ - lib/generators/authentication/templates/controllers/html/two_factor_authentication/challenges_controller.rb.tt
59
+ - lib/generators/authentication/templates/controllers/html/two_factor_authentication/totps_controller.rb.tt
55
60
  - lib/generators/authentication/templates/erb/authentications/events/index.html.erb
56
61
  - lib/generators/authentication/templates/erb/identity/emails/edit.html.erb.tt
57
62
  - lib/generators/authentication/templates/erb/identity/password_resets/edit.html.erb.tt
@@ -67,6 +72,8 @@ files:
67
72
  - lib/generators/authentication/templates/erb/sessions/index.html.erb.tt
68
73
  - lib/generators/authentication/templates/erb/sessions/new.html.erb.tt
69
74
  - lib/generators/authentication/templates/erb/sessions/sudos/new.html.erb.tt
75
+ - lib/generators/authentication/templates/erb/two_factor_authentication/challenges/new.html.erb.tt
76
+ - lib/generators/authentication/templates/erb/two_factor_authentication/totps/new.html.erb.tt
70
77
  - lib/generators/authentication/templates/mailers/identity_mailer.rb.tt
71
78
  - lib/generators/authentication/templates/mailers/session_mailer.rb.tt
72
79
  - lib/generators/authentication/templates/migrations/create_events_migration.rb.tt
@@ -77,6 +84,7 @@ files:
77
84
  - lib/generators/authentication/templates/models/locking.rb.tt
78
85
  - lib/generators/authentication/templates/models/model.rb.tt
79
86
  - lib/generators/authentication/templates/models/session.rb.tt
87
+ - lib/generators/authentication/templates/test_unit/application_system_test_case.rb.tt
80
88
  - lib/generators/authentication/templates/test_unit/controllers/api/identity/email_verifications_controller_test.rb.tt
81
89
  - lib/generators/authentication/templates/test_unit/controllers/api/identity/emails_controller_test.rb.tt
82
90
  - lib/generators/authentication/templates/test_unit/controllers/api/identity/password_resets_controller_test.rb.tt
@@ -98,6 +106,7 @@ files:
98
106
  - lib/generators/authentication/templates/test_unit/system/registrations_test.rb.tt
99
107
  - lib/generators/authentication/templates/test_unit/system/sessions/sudos_test.rb.tt
100
108
  - lib/generators/authentication/templates/test_unit/system/sessions_test.rb.tt
109
+ - lib/generators/authentication/templates/test_unit/test_helper.rb.tt
101
110
  homepage: https://github.com/lazaronixon/authentication-zero
102
111
  licenses:
103
112
  - MIT