authentication-zero 2.16.4 → 2.16.6

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 (35) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -0
  3. data/Gemfile.lock +1 -1
  4. data/README.md +8 -8
  5. data/lib/authentication_zero/version.rb +1 -1
  6. data/lib/generators/authentication/authentication_generator.rb +16 -7
  7. data/lib/generators/authentication/templates/controllers/api/application_controller.rb.tt +0 -7
  8. data/lib/generators/authentication/templates/controllers/api/identity/emails_controller.rb.tt +3 -4
  9. data/lib/generators/authentication/templates/controllers/api/identity/password_resets_controller.rb.tt +6 -2
  10. data/lib/generators/authentication/templates/controllers/html/application_controller.rb.tt +0 -11
  11. data/lib/generators/authentication/templates/controllers/html/identity/email_verifications_controller.rb.tt +5 -1
  12. data/lib/generators/authentication/templates/controllers/html/identity/emails_controller.rb.tt +3 -4
  13. data/lib/generators/authentication/templates/controllers/html/identity/password_resets_controller.rb.tt +11 -3
  14. data/lib/generators/authentication/templates/controllers/html/sessions/passwordlesses_controller.rb.tt +42 -0
  15. data/lib/generators/authentication/templates/controllers/html/sessions_controller.rb.tt +0 -1
  16. data/lib/generators/authentication/templates/controllers/html/two_factor_authentication/totps_controller.rb.tt +3 -4
  17. data/lib/generators/authentication/templates/erb/identity/emails/edit.html.erb.tt +6 -1
  18. data/lib/generators/authentication/templates/erb/identity/password_resets/new.html.erb.tt +1 -1
  19. data/lib/generators/authentication/templates/erb/registrations/new.html.erb.tt +1 -1
  20. data/lib/generators/authentication/templates/erb/sessions/new.html.erb.tt +9 -1
  21. data/lib/generators/authentication/templates/erb/sessions/passwordlesses/new.html.erb.tt +14 -0
  22. data/lib/generators/authentication/templates/erb/two_factor_authentication/totps/new.html.erb.tt +6 -1
  23. data/lib/generators/authentication/templates/erb/user_mailer/passwordless.html.erb.tt +9 -0
  24. data/lib/generators/authentication/templates/mailers/user_mailer.rb.tt +8 -0
  25. data/lib/generators/authentication/templates/migrations/create_sign_in_tokens_migration.rb.tt +7 -0
  26. data/lib/generators/authentication/templates/models/session.rb.tt +0 -8
  27. data/lib/generators/authentication/templates/models/sign_in_token.rb.tt +3 -0
  28. data/lib/generators/authentication/templates/models/user.rb.tt +6 -3
  29. data/lib/generators/authentication/templates/test_unit/controllers/api/identity/emails_controller_test.rb.tt +8 -1
  30. data/lib/generators/authentication/templates/test_unit/controllers/html/identity/emails_controller_test.rb.tt +8 -1
  31. data/lib/generators/authentication/templates/test_unit/system/identity/emails_test.rb.tt +1 -0
  32. metadata +7 -5
  33. data/lib/generators/authentication/templates/controllers/api/sessions/sudos_controller.rb.tt +0 -11
  34. data/lib/generators/authentication/templates/controllers/html/sessions/sudos_controller.rb.tt +0 -14
  35. data/lib/generators/authentication/templates/erb/sessions/sudos/new.html.erb.tt +0 -28
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 32a85d186a97bb53ee18ac30be1acdd6eb64716c7ed30fd0cfe4d52afca0d990
4
- data.tar.gz: b155fd90fe8df3d548cd6cfa5c2b3cbf7bea30ffa23db6c3381a68b1b1d0c3ed
3
+ metadata.gz: d512c515587bc57bb47c2f89b27eea08f96a74c440bb0d404756613fa0cdbec5
4
+ data.tar.gz: 7b9781caa350fe751d9b2799d186b73a563bc4d108051b9a9491b09a45bb6e9e
5
5
  SHA512:
6
- metadata.gz: 5c0d67ddd39e6698f383ec398d32e3f7884776adb42a9d2724498007458760e9558310fab74609c151ce6eae633d83a55be366f978d457efe5ae433118a236e5
7
- data.tar.gz: b710950c1407acdf4a689a7e5858aafd344248f8f5917af5207db04b4d24828499fd4f1a61d54a7463b69b3866aeed499b900eff86e28072c613c871c6c97e24
6
+ metadata.gz: 4e0c90d112f6187a644841f3e394c9435eb9e9b9faf96109b29b16a96443ffeda5f3f375d8ba9b987742288807dab06e79a156f5b0a70d1794d81242a4f44c25
7
+ data.tar.gz: 8b79ad8fcc211053fe15ffcabac6ffd7033c7d1d093d715bf5cb6ceffbf595718f7f85d14b94e90d8170bb78c676a6890e4c0a76b3707034252073c580af6fac
data/CHANGELOG.md CHANGED
@@ -1,3 +1,14 @@
1
+ ## Authentication Zero 2.16.6 ##
2
+
3
+ * Remove passwordless from api template
4
+ * Remove sudoable, I want to make things simple for new users,
5
+ and it will became even simpler with the new rails 7.1 "password challenge api"
6
+
7
+ ## Authentication Zero 2.16.5 ##
8
+
9
+ * Revoke all password reset tokens (security enhancement)
10
+ * Sign in without password (new feature)
11
+
1
12
  ## Authentication Zero 2.16.4 (February 11, 2023) ##
2
13
 
3
14
  * Increase attemps for lockable sign-in
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- authentication-zero (2.16.4)
4
+ authentication-zero (2.16.6)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -27,11 +27,11 @@ Since Authentication Zero generates this code into your application instead of b
27
27
  - Checks if a password has been found in any data breach (--pwned)
28
28
  - Authentication by cookie
29
29
  - Authentication by token (--api)
30
+ - Passwordless authentication (--passwordless)
30
31
  - Two factor authentication (--two-factor)
31
32
  - Social Login with OmniAuth (--omniauthable)
32
33
  - Verify email using a link with token
33
34
  - Verify email using a six random digits code for api (--code-verifiable)
34
- - Ask password before sensitive data changes, aka: sudo (--sudoable)
35
35
  - Reset the user password and send reset instructions
36
36
  - Reset the user password only from verified emails
37
37
  - Lock mechanism for resetting password and sign-in (--lockable)
@@ -41,17 +41,17 @@ Since Authentication Zero generates this code into your application instead of b
41
41
  - Activity log (--trackable)
42
42
  - Log out
43
43
 
44
- ## Security and best practices
44
+ ## Generated code
45
45
 
46
- - [has_secure_password](https://api.rubyonrails.org/classes/ActiveModel/SecurePassword/ClassMethods.html#method-i-has_secure_password): Adds methods to set and authenticate against a BCrypt password.
46
+ - [has_secure_password](https://api.rubyonrails.org/classes/ActiveModel/SecurePassword/ClassMethods.html#method-i-has_secure_password): Adds methods to set and authenticate against a bcrypt password.
47
47
  - [signed cookies](https://api.rubyonrails.org/classes/ActionDispatch/Cookies.html): Returns a jar that'll automatically generate a signed representation of cookie value and verify it when reading from the cookie again.
48
48
  - [httponly cookies](https://api.rubyonrails.org/classes/ActionDispatch/Cookies.html): A cookie with the httponly attribute is inaccessible to the JavaScript, this precaution helps mitigate cross-site scripting (XSS) attacks.
49
49
  - [signed_id](https://api.rubyonrails.org/classes/ActiveRecord/SignedId.html): Returns a signed id that is tamper proof, so it's safe to send in an email or otherwise share with the outside world.
50
- - [Current attributes](https://api.rubyonrails.org/classes/ActiveSupport/CurrentAttributes.html): Abstract super class that provides a thread-isolated attributes singleton, which resets automatically before and after each request.
51
- - [Action mailer](https://api.rubyonrails.org/classes/ActionMailer/Base.html): Action Mailer allows you to send email from your application using a mailer model and views.
52
- - [Log filtering](https://guides.rubyonrails.org/action_controller_overview.html#log-filtering): Parameters 'token' and 'password' are marked [FILTERED] in the log.
53
- - [Functional Tests](https://guides.rubyonrails.org/testing.html#functional-tests-for-your-controllers): In Rails, testing the various actions of a controller is a form of writing functional tests.
54
- - [System Testing](https://guides.rubyonrails.org/testing.html#system-testing): System tests allow you to test user interactions with your application, running tests in either a real or a headless browser.
50
+ - [current attributes](https://api.rubyonrails.org/classes/ActiveSupport/CurrentAttributes.html): Abstract super class that provides a thread-isolated attributes singleton, which resets automatically before and after each request.
51
+ - [action mailer](https://api.rubyonrails.org/classes/ActionMailer/Base.html): Action Mailer allows you to send email from your application using a mailer model and views.
52
+ - [log filtering](https://guides.rubyonrails.org/action_controller_overview.html#log-filtering): Parameters 'token' and 'password' are marked [FILTERED] in the log.
53
+ - [functional tests](https://guides.rubyonrails.org/testing.html#functional-tests-for-your-controllers): In Rails, testing the various actions of a controller is a form of writing functional tests.
54
+ - [system testing](https://guides.rubyonrails.org/testing.html#system-testing): System tests allow you to test user interactions with your application, running tests in either a real or a headless browser.
55
55
 
56
56
  ## Development
57
57
 
@@ -1,3 +1,3 @@
1
1
  module AuthenticationZero
2
- VERSION = "2.16.4"
2
+ VERSION = "2.16.6"
3
3
  end
@@ -6,8 +6,8 @@ class AuthenticationGenerator < Rails::Generators::Base
6
6
  class_option :api, type: :boolean, desc: "Generates API authentication"
7
7
  class_option :pwned, type: :boolean, desc: "Add pwned password validation"
8
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
9
  class_option :lockable, type: :boolean, desc: "Add password reset locking"
10
+ class_option :passwordless, type: :boolean, desc: "Add passwordless sign"
11
11
  class_option :omniauthable, type: :boolean, desc: "Add social login support"
12
12
  class_option :trackable, type: :boolean, desc: "Add activity log support"
13
13
  class_option :two_factor, type: :boolean, desc: "Add two factor authentication"
@@ -52,6 +52,7 @@ class AuthenticationGenerator < Rails::Generators::Base
52
52
  migration_template "migrations/create_sessions_migration.rb", "#{db_migrate_path}/create_sessions.rb"
53
53
  migration_template "migrations/create_email_verification_tokens_migration.rb", "#{db_migrate_path}/create_email_verification_tokens.rb"
54
54
  migration_template "migrations/create_password_reset_tokens_migration.rb", "#{db_migrate_path}/create_password_reset_tokens.rb"
55
+ migration_template "migrations/create_sign_in_tokens_migration.rb", "#{db_migrate_path}/create_sign_in_tokens_migration.rb" if passwordless?
55
56
  migration_template "migrations/create_events_migration.rb", "#{db_migrate_path}/create_events.rb" if options.trackable?
56
57
  end
57
58
 
@@ -60,6 +61,7 @@ class AuthenticationGenerator < Rails::Generators::Base
60
61
  template "models/session.rb", "app/models/session.rb"
61
62
  template "models/email_verification_token.rb", "app/models/email_verification_token.rb"
62
63
  template "models/password_reset_token.rb", "app/models/password_reset_token.rb"
64
+ template "models/sign_in_token.rb", "app/models/sign_in_token.rb" if passwordless?
63
65
  template "models/current.rb", "app/models/current.rb"
64
66
  template "models/event.rb", "app/models/event.rb" if options.trackable?
65
67
  end
@@ -77,8 +79,8 @@ class AuthenticationGenerator < Rails::Generators::Base
77
79
  template "controllers/#{format_folder}/passwords_controller.rb", "app/controllers/passwords_controller.rb"
78
80
  template "controllers/#{format_folder}/registrations_controller.rb", "app/controllers/registrations_controller.rb"
79
81
  template "controllers/#{format_folder}/home_controller.rb", "app/controllers/home_controller.rb" unless options.api?
80
- template "controllers/#{format_folder}/sessions/sudos_controller.rb", "app/controllers/sessions/sudos_controller.rb" if options.sudoable?
81
82
  template "controllers/#{format_folder}/sessions/omniauth_controller.rb", "app/controllers/sessions/omniauth_controller.rb" if omniauthable?
83
+ template "controllers/#{format_folder}/sessions/passwordlesses_controller.rb", "app/controllers/sessions/passwordlesses_controller.rb" if passwordless?
82
84
  template "controllers/#{format_folder}/authentications/events_controller.rb", "app/controllers/authentications/events_controller.rb" if options.trackable?
83
85
  end
84
86
 
@@ -96,10 +98,10 @@ class AuthenticationGenerator < Rails::Generators::Base
96
98
  directory "erb/passwords", "app/views/passwords"
97
99
  directory "erb/registrations", "app/views/registrations"
98
100
 
99
- template "erb/sessions/index.html.erb", "app/views/sessions/index.html.erb"
100
- template "erb/sessions/new.html.erb", "app/views/sessions/new.html.erb"
101
+ template "erb/sessions/index.html.erb", "app/views/sessions/index.html.erb"
102
+ template "erb/sessions/new.html.erb", "app/views/sessions/new.html.erb"
101
103
 
102
- directory "erb/sessions/sudos", "app/views/sessions/sudos" if options.sudoable?
104
+ directory "erb/sessions/passwordlesses", "app/views/sessions/passwordlesses" if passwordless?
103
105
 
104
106
  directory "erb/two_factor_authentication", "app/views/two_factor_authentication" if two_factor?
105
107
  directory "erb/authentications/events", "app/views/authentications/events" if options.trackable?
@@ -113,6 +115,10 @@ class AuthenticationGenerator < Rails::Generators::Base
113
115
  def add_routes
114
116
  route "root 'home#index'" unless options.api?
115
117
 
118
+ if passwordless?
119
+ route "resource :passwordless, only: [:new, :edit, :create]", namespace: :sessions
120
+ end
121
+
116
122
  if omniauthable?
117
123
  route "post '/auth/:provider/callback', to: 'sessions/omniauth#create'"
118
124
  route "get '/auth/:provider/callback', to: 'sessions/omniauth#create'"
@@ -131,7 +137,6 @@ class AuthenticationGenerator < Rails::Generators::Base
131
137
  route "resource :password_reset, only: [:new, :edit, :create, :update]", namespace: :identity
132
138
  route "resource :email_verification, only: [:edit, :create]", namespace: :identity
133
139
  route "resource :email, only: [:edit, :update]", namespace: :identity
134
- route "resource :sudo, only: [:new, :create]", namespace: :sessions if options.sudoable?
135
140
  route "resource :password, only: [:edit, :update]"
136
141
  route "resources :sessions, only: [:index, :show, :destroy]"
137
142
  route "post 'sign_up', to: 'registrations#create'"
@@ -157,6 +162,10 @@ class AuthenticationGenerator < Rails::Generators::Base
157
162
  options.omniauthable? && !options.api?
158
163
  end
159
164
 
165
+ def passwordless?
166
+ options.passwordless? && !options.api?
167
+ end
168
+
160
169
  def two_factor?
161
170
  options.two_factor? && !options.api?
162
171
  end
@@ -166,6 +175,6 @@ class AuthenticationGenerator < Rails::Generators::Base
166
175
  end
167
176
 
168
177
  def redis?
169
- options.lockable? || options.sudoable? || code_verifiable?
178
+ options.lockable? || code_verifiable?
170
179
  end
171
180
  end
@@ -3,13 +3,6 @@ class ApplicationController < ActionController::API
3
3
 
4
4
  before_action :set_current_request_details
5
5
  before_action :authenticate
6
- <%- if options.sudoable? %>
7
- def require_sudo
8
- unless Current.session.sudo?
9
- render json: { error: "Enter your password to continue" }, status: :forbidden
10
- end
11
- end
12
- <%- end -%>
13
6
  <%- if options.lockable? %>
14
7
  def require_lock(wait: 1.hour, attempts: 10)
15
8
  counter = Kredis.counter("require_lock:#{request.remote_ip}:#{params[:controller]}:#{params[:action]}", expires_in: wait)
@@ -1,11 +1,10 @@
1
1
  class Identity::EmailsController < ApplicationController
2
- <%- if options.sudoable? -%>
3
- before_action :require_sudo
4
- <%- end -%>
5
2
  before_action :set_user
6
3
 
7
4
  def update
8
- if @user.update(user_params)
5
+ if !@user.authenticate(params[:current_password])
6
+ render json: { error: "The password you entered is incorrect" }, status: :bad_request
7
+ elsif @user.update(user_params)
9
8
  render_show
10
9
  else
11
10
  render json: @user.errors, status: :unprocessable_entity
@@ -1,10 +1,10 @@
1
1
  class Identity::PasswordResetsController < ApplicationController
2
2
  skip_before_action :authenticate
3
3
 
4
- before_action :set_user, only: :update
5
4
  <%- if options.lockable? -%>
6
5
  before_action :require_lock, only: :create
7
6
  <%- end -%>
7
+ before_action :set_user, only: :update
8
8
 
9
9
  def create
10
10
  if @user = User.find_by(email: params[:email], verified: true)
@@ -16,7 +16,7 @@ class Identity::PasswordResetsController < ApplicationController
16
16
 
17
17
  def update
18
18
  if @user.update(user_params)
19
- @token.destroy; render json: @user
19
+ revoke_tokens; render(json: @user)
20
20
  else
21
21
  render json: @user.errors, status: :unprocessable_entity
22
22
  end
@@ -32,4 +32,8 @@ class Identity::PasswordResetsController < ApplicationController
32
32
  def user_params
33
33
  params.permit(:password, :password_confirmation)
34
34
  end
35
+
36
+ def revoke_tokens
37
+ @user.password_reset_tokens.delete_all
38
+ end
35
39
  end
@@ -1,17 +1,6 @@
1
1
  class ApplicationController < ActionController::Base
2
2
  before_action :set_current_request_details
3
3
  before_action :authenticate
4
- <%- if options.sudoable? %>
5
- def require_sudo
6
- <%- if omniauthable? -%>
7
- unless Current.session.sudo? || Current.session.user.provider.present?
8
- <%- else -%>
9
- unless Current.session.sudo?
10
- <%- end -%>
11
- redirect_to new_sessions_sudo_path(proceed_to_url: request.url)
12
- end
13
- end
14
- <%- end -%>
15
4
  <%- if options.lockable? %>
16
5
  def require_lock(wait: 1.hour, attempts: 10)
17
6
  counter = Kredis.counter("require_lock:#{request.remote_ip}:#{params[:controller]}:#{params[:action]}", expires_in: wait)
@@ -9,7 +9,7 @@ class Identity::EmailVerificationsController < ApplicationController
9
9
  end
10
10
 
11
11
  def create
12
- UserMailer.with(user: Current.user).email_verification.deliver_later
12
+ send_email_verification
13
13
  redirect_to root_path, notice: "We sent a verification email to your email address"
14
14
  end
15
15
 
@@ -19,4 +19,8 @@ class Identity::EmailVerificationsController < ApplicationController
19
19
  rescue
20
20
  redirect_to edit_identity_email_path, alert: "That email verification link is invalid"
21
21
  end
22
+
23
+ def send_email_verification
24
+ UserMailer.with(user: Current.user).email_verification.deliver_later
25
+ end
22
26
  end
@@ -1,14 +1,13 @@
1
1
  class Identity::EmailsController < ApplicationController
2
- <%- if options.sudoable? -%>
3
- before_action :require_sudo
4
- <%- end -%>
5
2
  before_action :set_user
6
3
 
7
4
  def edit
8
5
  end
9
6
 
10
7
  def update
11
- if @user.update(user_params)
8
+ if !@user.authenticate(params[:current_password])
9
+ redirect_to edit_identity_email_path, alert: "The password you entered is incorrect"
10
+ elsif @user.update(user_params)
12
11
  redirect_to_root
13
12
  else
14
13
  render :edit, status: :unprocessable_entity
@@ -1,10 +1,10 @@
1
1
  class Identity::PasswordResetsController < ApplicationController
2
2
  skip_before_action :authenticate
3
3
 
4
- before_action :set_user, only: %i[ edit update ]
5
4
  <%- if options.lockable? -%>
6
5
  before_action :require_lock, only: :create
7
6
  <%- end -%>
7
+ before_action :set_user, only: %i[ edit update ]
8
8
 
9
9
  def new
10
10
  end
@@ -14,7 +14,7 @@ class Identity::PasswordResetsController < ApplicationController
14
14
 
15
15
  def create
16
16
  if @user = User.find_by(email: params[:email], verified: true)
17
- UserMailer.with(user: @user).password_reset.deliver_later
17
+ send_password_reset_email
18
18
  redirect_to sign_in_path, notice: "Check your email for reset instructions"
19
19
  else
20
20
  redirect_to new_identity_password_reset_path, alert: "You can't reset your password until you verify your email"
@@ -23,7 +23,7 @@ class Identity::PasswordResetsController < ApplicationController
23
23
 
24
24
  def update
25
25
  if @user.update(user_params)
26
- @token.destroy; redirect_to(sign_in_path, notice: "Your password was reset successfully. Please sign in")
26
+ revoke_tokens; redirect_to(sign_in_path, notice: "Your password was reset successfully. Please sign in")
27
27
  else
28
28
  render :edit, status: :unprocessable_entity
29
29
  end
@@ -39,4 +39,12 @@ class Identity::PasswordResetsController < ApplicationController
39
39
  def user_params
40
40
  params.permit(:password, :password_confirmation)
41
41
  end
42
+
43
+ def send_password_reset_email
44
+ UserMailer.with(user: @user).password_reset.deliver_later
45
+ end
46
+
47
+ def revoke_tokens
48
+ @user.password_reset_tokens.delete_all
49
+ end
42
50
  end
@@ -0,0 +1,42 @@
1
+ class Sessions::PasswordlessesController < ApplicationController
2
+ skip_before_action :authenticate
3
+
4
+ <%- if options.lockable? -%>
5
+ before_action :require_lock, only: :create
6
+ <%- end -%>
7
+ before_action :set_user, only: :edit
8
+
9
+ def new
10
+ end
11
+
12
+ def edit
13
+ @session = @user.sessions.create!
14
+ cookies.signed.permanent[:session_token] = { value: @session.id, httponly: true }
15
+
16
+ revoke_tokens; redirect_to(root_path, notice: "Signed in successfully")
17
+ end
18
+
19
+ def create
20
+ if @user = User.find_by(email: params[:email], verified: true)
21
+ send_passwordless_email
22
+ redirect_to sign_in_path, notice: "Check your email for sign in instructions"
23
+ else
24
+ redirect_to new_sessions_passwordless_path, alert: "You can't sign in until you verify your email"
25
+ end
26
+ end
27
+
28
+ private
29
+ def set_user
30
+ @token = SignInToken.find_signed!(params[:sid]); @user = @token.user
31
+ rescue
32
+ redirect_to new_sessions_passwordless_path, alert: "That sign in link is invalid"
33
+ end
34
+
35
+ def send_passwordless_email
36
+ UserMailer.with(user: @user).passwordless.deliver_later
37
+ end
38
+
39
+ def revoke_tokens
40
+ @user.sign_in_tokens.delete_all
41
+ end
42
+ end
@@ -21,7 +21,6 @@ class SessionsController < ApplicationController
21
21
  <%- if two_factor? -%>
22
22
  if user.otp_secret
23
23
  signed_id = user.signed_id(purpose: :authentication_challenge, expires_in: 20.minutes)
24
-
25
24
  redirect_to new_two_factor_authentication_challenge_path(token: signed_id)
26
25
  else
27
26
  @session = user.sessions.create!
@@ -1,7 +1,4 @@
1
1
  class TwoFactorAuthentication::TotpsController < ApplicationController
2
- <%- if options.sudoable? -%>
3
- before_action :require_sudo
4
- <%- end -%>
5
2
  before_action :set_user
6
3
  before_action :set_totp
7
4
 
@@ -10,7 +7,9 @@ class TwoFactorAuthentication::TotpsController < ApplicationController
10
7
  end
11
8
 
12
9
  def create
13
- if @totp.verify(params[:code], drift_behind: 15)
10
+ if !@user.authenticate(params[:current_password])
11
+ redirect_to two_factor_authentication_totp_path, alert: "The password you entered is incorrect"
12
+ elsif @totp.verify(params[:code], drift_behind: 15)
14
13
  @user.update! otp_secret: params[:secret]
15
14
  redirect_to root_path, notice: "2FA is enabled on your account"
16
15
  else
@@ -23,7 +23,12 @@
23
23
 
24
24
  <div>
25
25
  <%%= form.label :email, "New email", style: "display: block" %>
26
- <%%= form.email_field :email %>
26
+ <%%= form.email_field :email, required: true, autofocus: true %>
27
+ </div>
28
+
29
+ <div>
30
+ <%%= form.label :current_password, style: "display: block" %>
31
+ <%%= form.password_field :current_password, required: true, autocomplete: "current-password" %>
27
32
  </div>
28
33
 
29
34
  <div>
@@ -5,7 +5,7 @@
5
5
  <%%= form_with(url: identity_password_reset_path) do |form| %>
6
6
  <div>
7
7
  <%%= form.label :email, style: "display: block" %>
8
- <%%= form.email_field :email, autofocus: true, required: true %>
8
+ <%%= form.email_field :email, required: true, autofocus: true %>
9
9
  </div>
10
10
 
11
11
  <div>
@@ -15,7 +15,7 @@
15
15
 
16
16
  <div>
17
17
  <%%= form.label :email, style: "display: block" %>
18
- <%%= form.email_field :email, autofocus: true, autocomplete: "email" %>
18
+ <%%= form.email_field :email, required: true, autofocus: true, autocomplete: "email" %>
19
19
  </div>
20
20
 
21
21
  <div>
@@ -6,7 +6,7 @@
6
6
  <%%= form_with(url: sign_in_path) do |form| %>
7
7
  <div>
8
8
  <%%= form.label :email, style: "display: block" %>
9
- <%%= form.email_field :email, value: params[:email_hint], autofocus: true, required: true, autocomplete: "email" %>
9
+ <%%= form.email_field :email, value: params[:email_hint], required: true, autofocus: true, autocomplete: "email" %>
10
10
  </div>
11
11
 
12
12
  <div>
@@ -18,6 +18,14 @@
18
18
  <%%= form.submit "Sign in" %>
19
19
  </div>
20
20
  <%% end %>
21
+
22
+ <br>
23
+
24
+ <%- if passwordless? %>
25
+ <div>
26
+ <%%= link_to "Sign in without password", new_sessions_passwordless_path %>
27
+ </div>
28
+ <%- end -%>
21
29
  <%- if omniauthable? %>
22
30
  <div>
23
31
  <%%= button_to "Sign in with OmniAuth", "/auth/developer", "data-turbo" => false %>
@@ -0,0 +1,14 @@
1
+ <p style="color: red"><%%= alert %></p>
2
+
3
+ <h1>Sign in without password</h1>
4
+
5
+ <%%= form_with(url: sessions_passwordless_path) do |form| %>
6
+ <div>
7
+ <%%= form.label :email, style: "display: block" %>
8
+ <%%= form.email_field :email, required: true, autofocus: true%>
9
+ </div>
10
+
11
+ <div>
12
+ <%%= form.submit "Sign in" %>
13
+ </div>
14
+ <%% end %>
@@ -19,7 +19,12 @@
19
19
 
20
20
  <div>
21
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 %>
22
+ <%%= form.text_field :code, required: true, autofocus: true, autocomplete: :off %>
23
+ </div>
24
+
25
+ <div>
26
+ <%%= form.label :current_password, style: "display: block" %>
27
+ <%%= form.password_field :current_password, required: true, autocomplete: "current-password" %>
23
28
  </div>
24
29
 
25
30
  <div>
@@ -0,0 +1,9 @@
1
+ <p>Hey there,</p>
2
+
3
+ <p>You requested a magic sign-in link. Here you go:</p>
4
+
5
+ <%%= link_to "Sign in without password", edit_sessions_passwordless_url(sid: @signed_id) %>
6
+
7
+ <hr>
8
+
9
+ <p>Have questions or need help? Just reply to this email and our support team will help you sort it out.</p>
@@ -16,4 +16,12 @@ class UserMailer < ApplicationMailer
16
16
 
17
17
  mail to: @user.email, subject: "Verify your email"
18
18
  end
19
+ <%- if passwordless? %>
20
+ def passwordless
21
+ @user = params[:user]
22
+ @signed_id = @user.sign_in_tokens.create.signed_id(expires_in: 1.day)
23
+
24
+ mail to: @user.email, subject: "Your sign in link"
25
+ end
26
+ <%- end -%>
19
27
  end
@@ -0,0 +1,7 @@
1
+ class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
2
+ def change
3
+ create_table :sign_in_tokens do |t|
4
+ t.references :user, null: false, foreign_key: true
5
+ end
6
+ end
7
+ end
@@ -1,18 +1,10 @@
1
1
  class Session < ApplicationRecord
2
2
  belongs_to :user
3
- <%- if options.sudoable? %>
4
- kredis_flag :sudo, expires_in: 30.minutes
5
- <%- end -%>
6
3
 
7
4
  before_create do
8
5
  self.user_agent = Current.user_agent
9
6
  self.ip_address = Current.ip_address
10
7
  end
11
- <%- if options.sudoable? %>
12
- after_create_commit do
13
- self.sudo.mark
14
- end
15
- <%- end -%>
16
8
 
17
9
  after_create_commit do
18
10
  SessionMailer.with(session: self).signed_in_notification.deliver_later
@@ -0,0 +1,3 @@
1
+ class SignInToken < 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 passwordless? -%>
7
+ has_many :sign_in_tokens, dependent: :destroy
8
+ <%- end -%>
6
9
 
7
10
  has_many :sessions, dependent: :destroy
8
11
  <%- if options.trackable? -%>
@@ -22,7 +25,7 @@ class User < ApplicationRecord
22
25
  self.email = email.downcase.strip
23
26
  end
24
27
 
25
- before_validation if: :email_changed?, unless: :new_record? do
28
+ before_validation if: :email_changed?, on: :update do
26
29
  self.verified = false
27
30
  end
28
31
 
@@ -38,8 +41,8 @@ class User < ApplicationRecord
38
41
  events.create! action: "password_changed"
39
42
  end
40
43
 
41
- after_update if: :verified_previously_changed? do
42
- events.create! action: "email_verified" if verified?
44
+ after_update if: [:verified_previously_changed?, :verified?] do
45
+ events.create! action: "email_verified"
43
46
  end
44
47
  <%- end -%>
45
48
  end
@@ -10,7 +10,14 @@ class Identity::EmailsControllerTest < ActionDispatch::IntegrationTest
10
10
  end
11
11
 
12
12
  test "should update email" do
13
- patch identity_email_url, params: { email: "new_email@hey.com" }, headers: default_headers
13
+ patch identity_email_url, params: { email: "new_email@hey.com", current_password: "Secret1*3*5*" }, headers: default_headers
14
14
  assert_response :success
15
15
  end
16
+
17
+ test "should not update email with wrong current password" do
18
+ patch identity_email_url, params: { email: "new_email@hey.com", current_password: "SecretWrong1*3" }, headers: default_headers
19
+
20
+ assert_response :bad_request
21
+ assert_equal "The password you entered is incorrect", response.parsed_body["error"]
22
+ end
16
23
  end
@@ -11,7 +11,14 @@ class Identity::EmailsControllerTest < ActionDispatch::IntegrationTest
11
11
  end
12
12
 
13
13
  test "should update email" do
14
- patch identity_email_url, params: { email: "new_email@hey.com" }
14
+ patch identity_email_url, params: { email: "new_email@hey.com", current_password: "Secret1*3*5*" }
15
15
  assert_redirected_to root_url
16
16
  end
17
+
18
+ test "should not update email with wrong current password" do
19
+ patch identity_email_url, params: { email: "new_email@hey.com", current_password: "SecretWrong1*3" }
20
+
21
+ assert_redirected_to edit_identity_email_url
22
+ assert_equal "The password you entered is incorrect", flash[:alert]
23
+ end
17
24
  end
@@ -9,6 +9,7 @@ class Identity::EmailsTest < ApplicationSystemTestCase
9
9
  click_on "Change email address"
10
10
 
11
11
  fill_in "New email", with: "new_email@hey.com"
12
+ fill_in "Current password", with: "Secret1*3*5*"
12
13
  click_on "Save changes"
13
14
 
14
15
  assert_text "Your email has been changed"
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.4
4
+ version: 2.16.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nixon
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-02-11 00:00:00.000000000 Z
11
+ date: 2023-02-14 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
@@ -44,7 +44,6 @@ files:
44
44
  - lib/generators/authentication/templates/controllers/api/identity/password_resets_controller.rb.tt
45
45
  - lib/generators/authentication/templates/controllers/api/passwords_controller.rb.tt
46
46
  - lib/generators/authentication/templates/controllers/api/registrations_controller.rb.tt
47
- - lib/generators/authentication/templates/controllers/api/sessions/sudos_controller.rb.tt
48
47
  - lib/generators/authentication/templates/controllers/api/sessions_controller.rb.tt
49
48
  - lib/generators/authentication/templates/controllers/html/application_controller.rb.tt
50
49
  - lib/generators/authentication/templates/controllers/html/authentications/events_controller.rb.tt
@@ -55,7 +54,7 @@ files:
55
54
  - lib/generators/authentication/templates/controllers/html/passwords_controller.rb.tt
56
55
  - lib/generators/authentication/templates/controllers/html/registrations_controller.rb.tt
57
56
  - lib/generators/authentication/templates/controllers/html/sessions/omniauth_controller.rb.tt
58
- - lib/generators/authentication/templates/controllers/html/sessions/sudos_controller.rb.tt
57
+ - lib/generators/authentication/templates/controllers/html/sessions/passwordlesses_controller.rb.tt
59
58
  - lib/generators/authentication/templates/controllers/html/sessions_controller.rb.tt
60
59
  - lib/generators/authentication/templates/controllers/html/two_factor_authentication/challenges_controller.rb.tt
61
60
  - lib/generators/authentication/templates/controllers/html/two_factor_authentication/totps_controller.rb.tt
@@ -69,23 +68,26 @@ files:
69
68
  - lib/generators/authentication/templates/erb/session_mailer/signed_in_notification.html.erb.tt
70
69
  - lib/generators/authentication/templates/erb/sessions/index.html.erb.tt
71
70
  - lib/generators/authentication/templates/erb/sessions/new.html.erb.tt
72
- - lib/generators/authentication/templates/erb/sessions/sudos/new.html.erb.tt
71
+ - lib/generators/authentication/templates/erb/sessions/passwordlesses/new.html.erb.tt
73
72
  - lib/generators/authentication/templates/erb/two_factor_authentication/challenges/new.html.erb.tt
74
73
  - lib/generators/authentication/templates/erb/two_factor_authentication/totps/new.html.erb.tt
75
74
  - lib/generators/authentication/templates/erb/user_mailer/email_verification.html.erb.tt
76
75
  - lib/generators/authentication/templates/erb/user_mailer/password_reset.html.erb.tt
76
+ - lib/generators/authentication/templates/erb/user_mailer/passwordless.html.erb.tt
77
77
  - lib/generators/authentication/templates/mailers/session_mailer.rb.tt
78
78
  - lib/generators/authentication/templates/mailers/user_mailer.rb.tt
79
79
  - lib/generators/authentication/templates/migrations/create_email_verification_tokens_migration.rb.tt
80
80
  - lib/generators/authentication/templates/migrations/create_events_migration.rb.tt
81
81
  - lib/generators/authentication/templates/migrations/create_password_reset_tokens_migration.rb.tt
82
82
  - lib/generators/authentication/templates/migrations/create_sessions_migration.rb.tt
83
+ - lib/generators/authentication/templates/migrations/create_sign_in_tokens_migration.rb.tt
83
84
  - lib/generators/authentication/templates/migrations/create_users_migration.rb.tt
84
85
  - lib/generators/authentication/templates/models/current.rb.tt
85
86
  - lib/generators/authentication/templates/models/email_verification_token.rb.tt
86
87
  - lib/generators/authentication/templates/models/event.rb.tt
87
88
  - lib/generators/authentication/templates/models/password_reset_token.rb.tt
88
89
  - lib/generators/authentication/templates/models/session.rb.tt
90
+ - lib/generators/authentication/templates/models/sign_in_token.rb.tt
89
91
  - lib/generators/authentication/templates/models/user.rb.tt
90
92
  - lib/generators/authentication/templates/test_unit/application_system_test_case.rb.tt
91
93
  - lib/generators/authentication/templates/test_unit/controllers/api/identity/email_verifications_controller_test.rb.tt
@@ -1,11 +0,0 @@
1
- class Sessions::SudosController < ApplicationController
2
- def create
3
- session = Current.session
4
-
5
- if session.user.authenticate(params[:password])
6
- session.sudo.mark
7
- else
8
- render json: { error: "The password you entered is incorrect" }, status: :bad_request
9
- end
10
- end
11
- end
@@ -1,14 +0,0 @@
1
- class Sessions::SudosController < ApplicationController
2
- def new
3
- end
4
-
5
- def create
6
- session = Current.session
7
-
8
- if session.user.authenticate(params[:password])
9
- session.sudo.mark; redirect_to(params[:proceed_to_url])
10
- else
11
- redirect_to new_sessions_sudo_path(proceed_to_url: params[:proceed_to_url]), alert: "The password you entered is incorrect"
12
- end
13
- end
14
- end
@@ -1,28 +0,0 @@
1
- <p style="color: red"><%%= alert %></p>
2
-
3
- <h1>Enter your password to continue</h1>
4
-
5
- <%%= form_with(url: sessions_sudo_path) do |form| %>
6
-
7
- <%%= form.hidden_field :proceed_to_url, value: params[:proceed_to_url] %>
8
-
9
- <div>
10
- <%%= form.password_field :password, required: true, autofocus: true, autocomplete: "current-password" %>
11
- </div>
12
-
13
- <div>
14
- <%%= form.submit "Continue" %>
15
- </div>
16
- <%% end %>
17
-
18
- <br>
19
-
20
- <p>
21
- <strong>Why are you asking me to do this?</strong><br>
22
- To better protect your account, we'll occasionally ask you to confirm your password before performing sensitive actions.
23
- </p>
24
-
25
- <p>
26
- <strong>Forgot your password?</strong><br>
27
- We'll help you <%%= link_to "reset it", new_identity_password_reset_path %> so you can continue.
28
- </p>