authentication-zero 2.16.15 → 2.16.17

Sign up to get free protection for your applications and to get access to all the features.
Files changed (29) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/Gemfile.lock +1 -1
  4. data/README.md +1 -2
  5. data/lib/authentication_zero/version.rb +1 -1
  6. data/lib/generators/authentication/authentication_generator.rb +31 -24
  7. data/lib/generators/authentication/templates/controllers/api/identity/email_verifications_controller.rb.tt +1 -11
  8. data/lib/generators/authentication/templates/controllers/api/identity/password_resets_controller.rb.tt +5 -1
  9. data/lib/generators/authentication/templates/controllers/html/identity/email_verifications_controller.rb.tt +1 -1
  10. data/lib/generators/authentication/templates/controllers/html/identity/password_resets_controller.rb.tt +1 -1
  11. data/lib/generators/authentication/templates/controllers/html/sessions/passwordlesses_controller.rb.tt +1 -1
  12. data/lib/generators/authentication/templates/controllers/html/sessions_controller.rb.tt +1 -1
  13. data/lib/generators/authentication/templates/controllers/html/two_factor_authentication/challenges_controller.rb.tt +33 -9
  14. data/lib/generators/authentication/templates/controllers/html/two_factor_authentication/recovery_codes_controller.rb.tt +31 -0
  15. data/lib/generators/authentication/templates/controllers/html/two_factor_authentication/totps_controller.rb.tt +8 -7
  16. data/lib/generators/authentication/templates/erb/home/index.html.erb.tt +4 -0
  17. data/lib/generators/authentication/templates/erb/two_factor_authentication/challenges/_recovery_code_form.html.erb.tt +19 -0
  18. data/lib/generators/authentication/templates/erb/two_factor_authentication/challenges/_totp_form.html.erb.tt +20 -0
  19. data/lib/generators/authentication/templates/erb/two_factor_authentication/challenges/new.html.erb.tt +4 -13
  20. data/lib/generators/authentication/templates/erb/two_factor_authentication/recovery_codes/_recovery_code.html.erb.tt +5 -0
  21. data/lib/generators/authentication/templates/erb/two_factor_authentication/recovery_codes/index.html.erb.tt +16 -0
  22. data/lib/generators/authentication/templates/erb/two_factor_authentication/totps/new.html.erb.tt +2 -2
  23. data/lib/generators/authentication/templates/erb/user_mailer/email_verification.html.erb.tt +1 -5
  24. data/lib/generators/authentication/templates/mailers/user_mailer.rb.tt +0 -4
  25. data/lib/generators/authentication/templates/migrations/create_recovery_codes_migration.rb.tt +11 -0
  26. data/lib/generators/authentication/templates/models/recovery_code.rb.tt +3 -0
  27. data/lib/generators/authentication/templates/models/user.rb.tt +4 -4
  28. data/lib/generators/authentication/templates/test_unit/controllers/api/identity/password_resets_controller_test.rb.tt +7 -0
  29. metadata +9 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 85fbe83273139a893939d4f20c3a06de269e0cea2b5fe3a7994f8d66919c3532
4
- data.tar.gz: 63396f6a2ceb39fc3a49be717a6ffc7097c29e6782b7a351ca0d6f3bb7f2cae6
3
+ metadata.gz: 54633f078d0da7d8832e1e8067b1a6462ddcf567351b5f15e548f62ab0307d86
4
+ data.tar.gz: ca024f87f0c49e1534679fedadc7d3944209be7c955a3bc46e254e3d4c790f73
5
5
  SHA512:
6
- metadata.gz: '0899627eceb12252ec843541a19ec9da8de2e0d4ab12e76e43f34e1117597be485f58a090a66f825f2a1fdb1affe5c83cb55502e2deb7a681e2d70e7578ede31'
7
- data.tar.gz: 8ee00bb247a6989e0c930b33ffefc5c546f6e2c4161d77297344343ec5585b6c80f5b11dd18cd06937b6264d4abb1d89b9f47d1ab80494201bf5d523cb289f86
6
+ metadata.gz: 2646e6b0e95f86ab931fa4338cf587894265078e346a3cb5a3b919bc3b5aba7a0eab9bb8a72e16dcce96efbec43764f4d47df08d6c2011d57994e958a6b295cc
7
+ data.tar.gz: 449db5cde90d0ba2bdbfc4df356c2eca24eab9464f88f149229b4cddaf63ce03cb1bcdf211af60af443db1a411ecfd800e98f871b6c717747da8863620ce33a7
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## Authentication Zero 2.16.16 ##
2
+
3
+ * Add recovery codes to two factor auth
4
+ * Removed code-verifiable strategy
5
+ * Respond password reset edit api with no_content
6
+
1
7
  ## Authentication Zero 2.16.15 ##
2
8
 
3
9
  * Add sign-in as button functionallity (--masqueradable)
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- authentication-zero (2.16.15)
4
+ authentication-zero (2.16.17)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -28,12 +28,11 @@ Since Authentication Zero generates this code into your application instead of b
28
28
  - Authentication by cookie
29
29
  - Authentication by token (--api)
30
30
  - Passwordless authentication (--passwordless)
31
- - Two factor authentication (--two-factor)
31
+ - Two factor authentication + recovery codes (--two-factor)
32
32
  - Social Login with OmniAuth (--omniauthable)
33
33
  - Send invitations (--invitable)
34
34
  - Sign-in as button functionallity (--masqueradable)
35
35
  - Verify email using a link with token
36
- - Verify email using a six random digits code for api (--code-verifiable)
37
36
  - Ask password before sensitive data changes, aka: sudo (--sudoable)
38
37
  - Reset the user password and send reset instructions
39
38
  - Reset the user password only from verified emails
@@ -1,3 +1,3 @@
1
1
  module AuthenticationZero
2
- VERSION = "2.16.15"
2
+ VERSION = "2.16.17"
3
3
  end
@@ -3,18 +3,17 @@ require "rails/generators/active_record"
3
3
  class AuthenticationGenerator < Rails::Generators::Base
4
4
  include ActiveRecord::Generators::Migration
5
5
 
6
- class_option :api, type: :boolean, desc: "Generates API authentication"
7
- class_option :pwned, type: :boolean, desc: "Add pwned password validation"
8
- class_option :code_verifiable, type: :boolean, desc: "Add email verification using a code for api"
9
- class_option :sudoable, type: :boolean, desc: "Add password request before sensitive data changes"
10
- class_option :lockable, type: :boolean, desc: "Add password reset locking"
11
- class_option :ratelimit, type: :boolean, desc: "Add request rate limiting"
12
- class_option :passwordless, type: :boolean, desc: "Add passwordless sign"
13
- class_option :omniauthable, type: :boolean, desc: "Add social login support"
14
- class_option :trackable, type: :boolean, desc: "Add activity log support"
15
- class_option :two_factor, type: :boolean, desc: "Add two factor authentication"
16
- class_option :invitable, type: :boolean, desc: "Add sending invitations"
17
- class_option :masqueradable, type: :boolean, desc: "Add sign-in as button functionallity"
6
+ class_option :api, type: :boolean, desc: "Generates API authentication"
7
+ class_option :pwned, type: :boolean, desc: "Add pwned password validation"
8
+ class_option :sudoable, type: :boolean, desc: "Add password request before sensitive data changes"
9
+ class_option :lockable, type: :boolean, desc: "Add password reset locking"
10
+ class_option :ratelimit, type: :boolean, desc: "Add request rate limiting"
11
+ class_option :passwordless, type: :boolean, desc: "Add passwordless sign"
12
+ class_option :omniauthable, type: :boolean, desc: "Add social login support"
13
+ class_option :trackable, type: :boolean, desc: "Add activity log support"
14
+ class_option :two_factor, type: :boolean, desc: "Add two factor authentication"
15
+ class_option :invitable, type: :boolean, desc: "Add sending invitations"
16
+ class_option :masqueradable, type: :boolean, desc: "Add sign-in as button functionallity"
18
17
 
19
18
  source_root File.expand_path("templates", __dir__)
20
19
 
@@ -63,6 +62,7 @@ class AuthenticationGenerator < Rails::Generators::Base
63
62
  migration_template "migrations/create_password_reset_tokens_migration.rb", "#{db_migrate_path}/create_password_reset_tokens.rb"
64
63
  migration_template "migrations/create_sign_in_tokens_migration.rb", "#{db_migrate_path}/create_sign_in_tokens_migration.rb" if passwordless?
65
64
  migration_template "migrations/create_events_migration.rb", "#{db_migrate_path}/create_events.rb" if options.trackable?
65
+ migration_template "migrations/create_recovery_codes_migration.rb", "#{db_migrate_path}/create_recovery_codes.rb" if two_factor?
66
66
  end
67
67
 
68
68
  def create_models
@@ -73,6 +73,7 @@ class AuthenticationGenerator < Rails::Generators::Base
73
73
  template "models/sign_in_token.rb", "app/models/sign_in_token.rb" if passwordless?
74
74
  template "models/current.rb", "app/models/current.rb"
75
75
  template "models/event.rb", "app/models/event.rb" if options.trackable?
76
+ template "models/recovery_code.rb", "app/models/recovery_code.rb" if two_factor?
76
77
  end
77
78
 
78
79
  def create_fixture_file
@@ -98,9 +99,13 @@ class AuthenticationGenerator < Rails::Generators::Base
98
99
 
99
100
  def create_views
100
101
  if options.api?
101
- directory "erb/user_mailer", "app/views/user_mailer"
102
+ template "erb/user_mailer/email_verification.html.erb", "app/views/user_mailer/email_verification.html.erb"
103
+ template "erb/user_mailer/password_reset.html.erb", "app/views/user_mailer/password_reset.html.erb"
102
104
  else
103
- directory "erb/user_mailer", "app/views/user_mailer"
105
+ template "erb/user_mailer/email_verification.html.erb", "app/views/user_mailer/email_verification.html.erb"
106
+ template "erb/user_mailer/password_reset.html.erb", "app/views/user_mailer/password_reset.html.erb"
107
+ template "erb/user_mailer/invitation_instructions.html.erb", "app/views/user_mailer/invitation_instructions.html.erb" if invitable?
108
+ template "erb/user_mailer/passwordless.html.erb", "app/views/user_mailer/passwordless.html.erb" if passwordless?
104
109
 
105
110
  directory "erb/home", "app/views/home"
106
111
 
@@ -133,6 +138,10 @@ class AuthenticationGenerator < Rails::Generators::Base
133
138
  route "resource :sudo, only: [:new, :create]", namespace: :sessions
134
139
  end
135
140
 
141
+ if invitable?
142
+ route "resource :invitation, only: [:new, :create]"
143
+ end
144
+
136
145
  if passwordless?
137
146
  route "resource :passwordless, only: [:new, :edit, :create]", namespace: :sessions
138
147
  end
@@ -148,8 +157,9 @@ class AuthenticationGenerator < Rails::Generators::Base
148
157
  end
149
158
 
150
159
  if two_factor?
151
- route "resource :totp, only: [:new, :create]", namespace: :two_factor_authentication
152
- route "resource :challenge, only: [:new, :create]", namespace: :two_factor_authentication
160
+ route "resources :recovery_codes, only: [:index, :create]", namespace: :two_factor_authentication
161
+ route "resource :totp, only: [:new, :create]", namespace: :two_factor_authentication
162
+ route "resource :challenge, only: [:new, :create]", namespace: :two_factor_authentication
153
163
  end
154
164
 
155
165
  if options.trackable?
@@ -160,11 +170,12 @@ class AuthenticationGenerator < Rails::Generators::Base
160
170
  route "resource :email_verification, only: [:show, :create]", namespace: :identity
161
171
  route "resource :email, only: [:edit, :update]", namespace: :identity
162
172
 
163
- route "resource :invitation, only: [:new, :create]" if invitable?
164
- route "resource :password, only: [:edit, :update]"
165
- route "resources :sessions, only: [:index, :show, :destroy]"
173
+ route "resource :password, only: [:edit, :update]"
174
+ route "resources :sessions, only: [:index, :show, :destroy]"
175
+
166
176
  route 'post "sign_up", to: "registrations#create"'
167
177
  route 'get "sign_up", to: "registrations#new"' unless options.api?
178
+
168
179
  route 'post "sign_in", to: "sessions#create"'
169
180
  route 'get "sign_in", to: "sessions#new"' unless options.api?
170
181
  end
@@ -213,11 +224,7 @@ class AuthenticationGenerator < Rails::Generators::Base
213
224
  options.sudoable? && !options.api?
214
225
  end
215
226
 
216
- def code_verifiable?
217
- options.code_verifiable? && options.api?
218
- end
219
-
220
227
  def redis?
221
- options.lockable? || options.ratelimit? || sudoable? || code_verifiable?
228
+ options.lockable? || options.ratelimit? || sudoable?
222
229
  end
223
230
  end
@@ -13,18 +13,8 @@ class Identity::EmailVerificationsController < ApplicationController
13
13
 
14
14
  private
15
15
  def set_user
16
- <%- if code_verifiable? -%>
17
- verified_user = User.find_by(email: params[:email])
18
-
19
- if verified_user && verified_user.verification_code.value == params[:token]
20
- @user = verified_user
21
- else
22
- render json: { error: "That email verification code is invalid" }, status: :bad_request
23
- end
24
- <%- else -%>
25
16
  @token = EmailVerificationToken.find_signed!(params[:sid]); @user = @token.user
26
- rescue
17
+ rescue StandardError
27
18
  render json: { error: "That email verification link is invalid" }, status: :bad_request
28
- <%- end -%>
29
19
  end
30
20
  end
@@ -6,6 +6,10 @@ class Identity::PasswordResetsController < ApplicationController
6
6
  <%- end -%>
7
7
  before_action :set_user, only: :update
8
8
 
9
+ def edit
10
+ head :no_content
11
+ end
12
+
9
13
  def create
10
14
  if @user = User.find_by(email: params[:email], verified: true)
11
15
  UserMailer.with(user: @user).password_reset.deliver_later
@@ -25,7 +29,7 @@ class Identity::PasswordResetsController < ApplicationController
25
29
  private
26
30
  def set_user
27
31
  @token = PasswordResetToken.find_signed!(params[:sid]); @user = @token.user
28
- rescue
32
+ rescue StandardError
29
33
  render json: { error: "That password reset link is invalid" }, status: :bad_request
30
34
  end
31
35
 
@@ -16,7 +16,7 @@ class Identity::EmailVerificationsController < ApplicationController
16
16
  private
17
17
  def set_user
18
18
  @token = EmailVerificationToken.find_signed!(params[:sid]); @user = @token.user
19
- rescue
19
+ rescue StandardError
20
20
  redirect_to edit_identity_email_path, alert: "That email verification link is invalid"
21
21
  end
22
22
 
@@ -32,7 +32,7 @@ class Identity::PasswordResetsController < ApplicationController
32
32
  private
33
33
  def set_user
34
34
  @token = PasswordResetToken.find_signed!(params[:sid]); @user = @token.user
35
- rescue
35
+ rescue StandardError
36
36
  redirect_to new_identity_password_reset_path, alert: "That password reset link is invalid"
37
37
  end
38
38
 
@@ -28,7 +28,7 @@ class Sessions::PasswordlessesController < ApplicationController
28
28
  private
29
29
  def set_user
30
30
  @token = SignInToken.find_signed!(params[:sid]); @user = @token.user
31
- rescue
31
+ rescue StandardError
32
32
  redirect_to new_sessions_passwordless_path, alert: "That sign in link is invalid"
33
33
  end
34
34
 
@@ -16,7 +16,7 @@ class SessionsController < ApplicationController
16
16
 
17
17
  if user && user.authenticate(params[:password])
18
18
  <%- if two_factor? -%>
19
- if user.otp_secret
19
+ if user.otp_secret.present?
20
20
  signed_id = user.signed_id(purpose: :authentication_challenge, expires_in: 20.minutes)
21
21
  redirect_to new_two_factor_authentication_challenge_path(token: signed_id)
22
22
  else
@@ -7,22 +7,46 @@ class TwoFactorAuthentication::ChallengesController < ApplicationController
7
7
  end
8
8
 
9
9
  def create
10
- @totp = ROTP::TOTP.new(@user.otp_secret, issuer: "YourAppName")
11
-
12
- if @totp.verify(params[:code], drift_behind: 15)
13
- session = @user.sessions.create!
14
- cookies.signed.permanent[:session_token] = { value: session.id, httponly: true }
15
-
16
- redirect_to root_path, notice: "Signed in successfully"
10
+ if params[:scheme_type] == "recovery_codes"
11
+ verify_recovery_code
17
12
  else
18
- redirect_to new_two_factor_authentication_challenge_path(token: params[:token]), alert: "That code didn't work. Please try again"
13
+ verify_time_based_one_time_password
19
14
  end
20
15
  end
21
16
 
22
17
  private
23
18
  def set_user
24
19
  @user = User.find_signed!(params[:token], purpose: :authentication_challenge)
25
- rescue
20
+ rescue StandardError
26
21
  redirect_to sign_in_path, alert: "That's taking too long. Please re-enter your password and try again"
27
22
  end
23
+
24
+ def verify_recovery_code
25
+ if recover_code = @user.recovery_codes.find_by(code: params[:code], used: false)
26
+ recover_code.update!(used: true); sign_in_and_redirect_to_root
27
+ else
28
+ redirect_to_authentication_challenge
29
+ end
30
+ end
31
+
32
+ def verify_time_based_one_time_password
33
+ @totp = ROTP::TOTP.new(@user.otp_secret, issuer: "YourAppName")
34
+
35
+ if @totp.verify(params[:code], drift_behind: 15)
36
+ sign_in_and_redirect_to_root
37
+ else
38
+ redirect_to_authentication_challenge
39
+ end
40
+ end
41
+
42
+ def sign_in_and_redirect_to_root
43
+ session = @user.sessions.create!
44
+ cookies.signed.permanent[:session_token] = { value: session.id, httponly: true }
45
+
46
+ redirect_to root_path, notice: "Signed in successfully"
47
+ end
48
+
49
+ def redirect_to_authentication_challenge
50
+ redirect_to new_two_factor_authentication_challenge_path(token: params[:token], scheme_type: params[:scheme_type]), alert: "That code didn't work. Please try again"
51
+ end
28
52
  end
@@ -0,0 +1,31 @@
1
+ class TwoFactorAuthentication::RecoveryCodesController < ApplicationController
2
+ before_action :set_user
3
+
4
+ def index
5
+ if Current.user.recovery_codes.exists?
6
+ @recovery_codes = @user.recovery_codes
7
+ else
8
+ @recovery_codes = @user.recovery_codes.create!(new_recovery_codes)
9
+ end
10
+ end
11
+
12
+ def create
13
+ @user.recovery_codes.delete_all
14
+ @user.recovery_codes.create!(new_recovery_codes)
15
+
16
+ redirect_to two_factor_authentication_recovery_codes_path, notice: "Your new recovery codes have been generated"
17
+ end
18
+
19
+ private
20
+ def set_user
21
+ @user = Current.user
22
+ end
23
+
24
+ def new_recovery_codes
25
+ 10.times.map { { code: new_recovery_code } }
26
+ end
27
+
28
+ def new_recovery_code
29
+ SecureRandom.alphanumeric(10).insert(5, "-").downcase
30
+ end
31
+ end
@@ -9,17 +9,18 @@ class TwoFactorAuthentication::TotpsController < ApplicationController
9
9
  def create
10
10
  if @totp.verify(params[:code], drift_behind: 15)
11
11
  @user.update! otp_secret: params[:secret]
12
- redirect_to root_path, notice: "2FA is enabled on your account"
12
+ redirect_to two_factor_authentication_recovery_codes_path
13
13
  else
14
14
  redirect_to new_two_factor_authentication_totp_path, alert: "That code didn't work. Please try again"
15
15
  end
16
16
  end
17
17
 
18
- def set_user
19
- @user = Current.user
20
- end
18
+ private
19
+ def set_user
20
+ @user = Current.user
21
+ end
21
22
 
22
- def set_totp
23
- @totp = ROTP::TOTP.new(params[:secret] || ROTP::Base32.random, issuer: "YourAppName")
24
- end
23
+ def set_totp
24
+ @totp = ROTP::TOTP.new(params[:secret] || ROTP::Base32.random, issuer: "YourAppName")
25
+ end
25
26
  end
@@ -33,6 +33,10 @@
33
33
  <div>
34
34
  <%%= link_to "Two-Factor Authentication", new_two_factor_authentication_totp_path %>
35
35
  </div>
36
+
37
+ <%% if Current.user.otp_secret.present? %>
38
+ <div><%%= link_to "Recovery Codes", two_factor_authentication_recovery_codes_path %></div>
39
+ <%% end %>
36
40
  <%- end -%>
37
41
 
38
42
  <br>
@@ -0,0 +1,19 @@
1
+ <%%= form_with(url: two_factor_authentication_challenge_path) do |form| %>
2
+ <%%= form.hidden_field :token, value: params[:token] %>
3
+ <%%= form.hidden_field :scheme_type, value: "recovery_codes" %>
4
+
5
+ <div>
6
+ <%%= form.label :code do %>
7
+ <h1>OK, enter one of your recovery codes below:</h1>
8
+ <%% end %>
9
+ <%%= form.text_field :code, autofocus: true, required: true, autocomplete: :off %>
10
+ </div>
11
+
12
+ <div>
13
+ <%%= form.submit "Continue" %>
14
+ </div>
15
+ <%% end %>
16
+
17
+ <div>
18
+ <p>To access your account, enter one of the recovery codes (e.g., XXXXX-XXXXX) you saved when you set up your two-factor authentication device.</p>
19
+ </div>
@@ -0,0 +1,20 @@
1
+ <%%= form_with(url: two_factor_authentication_challenge_path) do |form| %>
2
+ <%%= form.hidden_field :token, value: params[:token] %>
3
+ <%%= form.hidden_field :scheme_type, value: "totp" %>
4
+
5
+ <div>
6
+ <%%= form.label :code do %>
7
+ <h1>Next, open the 2FA authenticator app on your phone and type the six digit code below:</h1>
8
+ <%% end %>
9
+ <%%= form.text_field :code, autofocus: true, required: true, autocomplete: :off %>
10
+ </div>
11
+
12
+ <div>
13
+ <%%= form.submit "Verify" %>
14
+ </div>
15
+ <%% end %>
16
+
17
+ <div>
18
+ <p><strong>Don't have your phone?</strong></p>
19
+ <%%= link_to "Use a recovery code to access your account.", new_two_factor_authentication_challenge_path(token: params[:token], scheme_type: "recovery_codes") %>
20
+ </div>
@@ -1,16 +1,7 @@
1
1
  <p style="color: red"><%%= alert %></p>
2
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>
3
+ <%% if params[:scheme_type] == "recovery_codes" %>
4
+ <%%= render "recovery_code_form" %>
5
+ <%% else %>
6
+ <%%= render "totp_form" %>
16
7
  <%% end %>
@@ -0,0 +1,5 @@
1
+ <%% if recovery_code.used? %>
2
+ <li><del><%%= recovery_code.code %></del></li>
3
+ <%% else %>
4
+ <li><%%= recovery_code.code %></li>
5
+ <%% end %>
@@ -0,0 +1,16 @@
1
+ <p style="color: green"><%%= notice %></p>
2
+
3
+ <h1>Two-factor recovery codes</h1>
4
+ <p>Recovery codes provide a way to log in if you lose your phone (or don't have it with you). Save these and keep them somewhere safe.</p>
5
+
6
+ <ul><%%= render @recovery_codes %></ul>
7
+
8
+ <%%= link_to "OK, I'm done", root_path %>
9
+
10
+ <hr>
11
+
12
+ <h2>Need new recovery codes?</h2>
13
+
14
+ <p>If you think your codes have fallen into the wrong hands, you can get a new set. Be sure to save the new ones because the old codes will stop working.</p>
15
+
16
+ <%%= button_to "Generate new recovery codes", two_factor_authentication_recovery_codes_path %>
@@ -10,8 +10,8 @@
10
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
11
 
12
12
  <figure>
13
- <%%= image_tag @qr_code.as_png(resize_exactly_to: 200).to_data_url%>
14
- <figcaption>Point your camera here</figcaption>
13
+ <%%= image_tag @qr_code.as_png(resize_exactly_to: 200).to_data_url%>
14
+ <figcaption>Point your camera here</figcaption>
15
15
  </figure>
16
16
 
17
17
  <%%= form_with(url: two_factor_authentication_totp_path) do |form| %>
@@ -2,13 +2,9 @@
2
2
 
3
3
  <p>This is to confirm that <%%= @user.email %> is the email you want to use on your account. If you ever lose your password, that's where we'll email a reset link.</p>
4
4
 
5
- <p><strong>You must <%= code_verifiable? ? "put the code" : "hit the link" %> below to confirm that you received this email.</strong></p>
5
+ <p><strong>You must hit the link below to confirm that you received this email.</strong></p>
6
6
 
7
- <%- if code_verifiable? -%>
8
- <strong><%%= @user.verification_code.value %></strong>
9
- <%- else -%>
10
7
  <%%= link_to "Yes, use this email for my account", identity_email_verification_url(sid: @signed_id) %>
11
- <%- end -%>
12
8
 
13
9
  <hr>
14
10
 
@@ -8,11 +8,7 @@ class UserMailer < ApplicationMailer
8
8
 
9
9
  def email_verification
10
10
  @user = params[:user]
11
- <%- if code_verifiable? -%>
12
- @user.verification_code.value = rand.to_s[2..7]
13
- <%- else -%>
14
11
  @signed_id = @user.email_verification_tokens.create.signed_id(expires_in: 2.days)
15
- <%- end -%>
16
12
 
17
13
  mail to: @user.email, subject: "Verify your email"
18
14
  end
@@ -0,0 +1,11 @@
1
+ class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
2
+ def change
3
+ create_table :recovery_codes do |t|
4
+ t.references :user, null: false, foreign_key: true
5
+ t.string :code, null: false
6
+ t.boolean :used, null: false, default: false
7
+
8
+ t.timestamps
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ class RecoveryCode < ApplicationRecord
2
+ belongs_to :user
3
+ end
@@ -3,6 +3,9 @@ class User < ApplicationRecord
3
3
 
4
4
  has_many :email_verification_tokens, dependent: :destroy
5
5
  has_many :password_reset_tokens, dependent: :destroy
6
+ <%- if two_factor? -%>
7
+ has_many :recovery_codes, dependent: :destroy
8
+ <%- end -%>
6
9
  <%- if passwordless? -%>
7
10
  has_many :sign_in_tokens, dependent: :destroy
8
11
  <%- end -%>
@@ -11,9 +14,6 @@ class User < ApplicationRecord
11
14
  <%- if options.trackable? -%>
12
15
  has_many :events, dependent: :destroy
13
16
  <%- end -%>
14
- <%- if code_verifiable? %>
15
- kredis_string :verification_code, expires_in: 2.days
16
- <%- end -%>
17
17
 
18
18
  validates :email, presence: true, uniqueness: true, format: { with: URI::MailTo::EMAIL_REGEXP }
19
19
  validates :password, allow_nil: true, length: { minimum: 12 }
@@ -30,7 +30,7 @@ class User < ApplicationRecord
30
30
  end
31
31
 
32
32
  after_update if: :password_digest_previously_changed? do
33
- sessions.where.not(id: Current.session).destroy_all
33
+ sessions.where.not(id: Current.session).delete_all
34
34
  end
35
35
  <%- if options.trackable? %>
36
36
  after_update if: :email_previously_changed? do
@@ -5,6 +5,13 @@ class Identity::PasswordResetsControllerTest < ActionDispatch::IntegrationTest
5
5
  @user = users(:lazaro_nixon)
6
6
  end
7
7
 
8
+ test "should get edit" do
9
+ sid = @user.password_reset_tokens.create.signed_id(expires_in: 20.minutes)
10
+
11
+ get edit_identity_password_reset_url(sid: sid)
12
+ assert_response :no_content
13
+ end
14
+
8
15
  test "should send a password reset email" do
9
16
  assert_enqueued_email_with UserMailer, :password_reset, args: { user: @user } do
10
17
  post identity_password_reset_url, params: { email: @user.email }
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.16.15
4
+ version: 2.16.17
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nixon
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-04-06 00:00:00.000000000 Z
11
+ date: 2023-04-07 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
@@ -60,6 +60,7 @@ files:
60
60
  - lib/generators/authentication/templates/controllers/html/sessions/sudos_controller.rb.tt
61
61
  - lib/generators/authentication/templates/controllers/html/sessions_controller.rb.tt
62
62
  - lib/generators/authentication/templates/controllers/html/two_factor_authentication/challenges_controller.rb.tt
63
+ - lib/generators/authentication/templates/controllers/html/two_factor_authentication/recovery_codes_controller.rb.tt
63
64
  - lib/generators/authentication/templates/controllers/html/two_factor_authentication/totps_controller.rb.tt
64
65
  - lib/generators/authentication/templates/erb/authentications/events/index.html.erb.tt
65
66
  - lib/generators/authentication/templates/erb/home/index.html.erb.tt
@@ -73,7 +74,11 @@ files:
73
74
  - lib/generators/authentication/templates/erb/sessions/new.html.erb.tt
74
75
  - lib/generators/authentication/templates/erb/sessions/passwordlesses/new.html.erb.tt
75
76
  - lib/generators/authentication/templates/erb/sessions/sudos/new.html.erb.tt
77
+ - lib/generators/authentication/templates/erb/two_factor_authentication/challenges/_recovery_code_form.html.erb.tt
78
+ - lib/generators/authentication/templates/erb/two_factor_authentication/challenges/_totp_form.html.erb.tt
76
79
  - lib/generators/authentication/templates/erb/two_factor_authentication/challenges/new.html.erb.tt
80
+ - lib/generators/authentication/templates/erb/two_factor_authentication/recovery_codes/_recovery_code.html.erb.tt
81
+ - lib/generators/authentication/templates/erb/two_factor_authentication/recovery_codes/index.html.erb.tt
77
82
  - lib/generators/authentication/templates/erb/two_factor_authentication/totps/new.html.erb.tt
78
83
  - lib/generators/authentication/templates/erb/user_mailer/email_verification.html.erb.tt
79
84
  - lib/generators/authentication/templates/erb/user_mailer/invitation_instructions.html.erb.tt
@@ -83,6 +88,7 @@ files:
83
88
  - lib/generators/authentication/templates/migrations/create_email_verification_tokens_migration.rb.tt
84
89
  - lib/generators/authentication/templates/migrations/create_events_migration.rb.tt
85
90
  - lib/generators/authentication/templates/migrations/create_password_reset_tokens_migration.rb.tt
91
+ - lib/generators/authentication/templates/migrations/create_recovery_codes_migration.rb.tt
86
92
  - lib/generators/authentication/templates/migrations/create_sessions_migration.rb.tt
87
93
  - lib/generators/authentication/templates/migrations/create_sign_in_tokens_migration.rb.tt
88
94
  - lib/generators/authentication/templates/migrations/create_users_migration.rb.tt
@@ -90,6 +96,7 @@ files:
90
96
  - lib/generators/authentication/templates/models/email_verification_token.rb.tt
91
97
  - lib/generators/authentication/templates/models/event.rb.tt
92
98
  - lib/generators/authentication/templates/models/password_reset_token.rb.tt
99
+ - lib/generators/authentication/templates/models/recovery_code.rb.tt
93
100
  - lib/generators/authentication/templates/models/session.rb.tt
94
101
  - lib/generators/authentication/templates/models/sign_in_token.rb.tt
95
102
  - lib/generators/authentication/templates/models/user.rb.tt