authentication-zero 2.9.1 → 2.10.0

Sign up to get free protection for your applications and to get access to all the features.
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