searls-auth 0.2.0 → 1.0.1

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -1
  3. data/README.md +162 -0
  4. data/app/controllers/searls/auth/base_controller.rb +42 -21
  5. data/app/controllers/searls/auth/email_verifications_controller.rb +57 -0
  6. data/app/controllers/searls/auth/logins_controller.rb +60 -39
  7. data/app/controllers/searls/auth/registrations_controller.rb +84 -32
  8. data/app/controllers/searls/auth/requests_password_resets_controller.rb +55 -0
  9. data/app/controllers/searls/auth/resets_passwords_controller.rb +73 -0
  10. data/app/controllers/searls/auth/settings_controller.rb +83 -0
  11. data/app/controllers/searls/auth/verifications_controller.rb +31 -61
  12. data/app/helpers/searls/auth/application_helper.rb +9 -5
  13. data/app/mailers/searls/auth/base_mailer.rb +1 -1
  14. data/app/mailers/searls/auth/email_verification_mailer.rb +29 -0
  15. data/app/mailers/searls/auth/login_link_mailer.rb +3 -3
  16. data/app/mailers/searls/auth/password_reset_mailer.rb +29 -0
  17. data/app/views/searls/auth/email_verification_mailer/verification_email.html.erb +23 -0
  18. data/app/views/searls/auth/email_verification_mailer/verification_email.text.erb +6 -0
  19. data/app/views/searls/auth/login_link_mailer/login_link.html.erb +5 -5
  20. data/app/views/searls/auth/login_link_mailer/login_link.text.erb +4 -5
  21. data/app/views/searls/auth/logins/show.html.erb +12 -4
  22. data/app/views/searls/auth/password_reset_mailer/password_reset.html.erb +23 -0
  23. data/app/views/searls/auth/password_reset_mailer/password_reset.text.erb +6 -0
  24. data/app/views/searls/auth/registrations/pending_email_verification.html.erb +12 -0
  25. data/app/views/searls/auth/registrations/show.html.erb +1 -2
  26. data/app/views/searls/auth/requests_password_resets/show.html.erb +17 -0
  27. data/app/views/searls/auth/resets_passwords/show.html.erb +26 -0
  28. data/app/views/searls/auth/settings/edit.html.erb +31 -0
  29. data/app/views/searls/auth/shared/_login_fields.html.erb +11 -0
  30. data/app/views/searls/auth/shared/_register_fields.html.erb +15 -0
  31. data/config/routes.rb +11 -0
  32. data/lib/searls/auth/authenticates_user.rb +54 -10
  33. data/lib/searls/auth/builds_target_redirect_url.rb +72 -0
  34. data/lib/searls/auth/config.rb +259 -12
  35. data/lib/searls/auth/creates_user.rb +12 -4
  36. data/lib/searls/auth/delivers_password_reset.rb +18 -0
  37. data/lib/searls/auth/emails_link.rb +2 -2
  38. data/lib/searls/auth/emails_verification.rb +33 -0
  39. data/lib/searls/auth/parses_time_safely.rb +32 -0
  40. data/lib/searls/auth/railtie.rb +0 -1
  41. data/lib/searls/auth/resets_password.rb +41 -0
  42. data/lib/searls/auth/updates_settings.rb +149 -0
  43. data/lib/searls/auth/version.rb +1 -1
  44. data/lib/searls/auth.rb +62 -13
  45. metadata +23 -1
@@ -0,0 +1,55 @@
1
+ module Searls
2
+ module Auth
3
+ class RequestsPasswordResetsController < BaseController
4
+ before_action :ensure_password_reset_enabled
5
+ before_action :clear_email_otp_from_session!, only: [:show, :create]
6
+
7
+ def show
8
+ render Searls::Auth.config.password_reset_request_view, layout: Searls::Auth.config.layout
9
+ end
10
+
11
+ def create
12
+ email = params[:email].to_s.strip
13
+ user = Searls::Auth.config.user_finder_by_email.call(email)
14
+
15
+ if proceed_with_password_reset_request?(user) && deliverable_user?(user)
16
+ Searls::Auth::DeliversPasswordReset.new.deliver(
17
+ user:,
18
+ redirect_path: params[:redirect_path],
19
+ redirect_subdomain: params[:redirect_subdomain]
20
+ )
21
+ end
22
+
23
+ flash[:notice] = Searls::Auth.config.resolve(:flash_notice_after_password_reset_email, params)
24
+ redirect_to searls_auth.password_reset_request_path(
25
+ email: email,
26
+ redirect_path: params[:redirect_path],
27
+ redirect_subdomain: params[:redirect_subdomain]
28
+ )
29
+ end
30
+
31
+ private
32
+
33
+ def ensure_password_reset_enabled
34
+ return if Searls::Auth.config.password_reset_enabled?
35
+
36
+ flash[:alert] = Searls::Auth.config.resolve(:flash_error_after_password_reset_not_enabled, params)
37
+ redirect_to searls_auth.login_path(
38
+ redirect_path: params[:redirect_path],
39
+ redirect_subdomain: params[:redirect_subdomain]
40
+ )
41
+ nil
42
+ end
43
+
44
+ def deliverable_user?(user)
45
+ return false if user.blank?
46
+ Searls::Auth.config.password_present?(user)
47
+ end
48
+
49
+ def proceed_with_password_reset_request?(user)
50
+ result = Searls::Auth.config.before_password_reset.call(user, params, self)
51
+ !(result == false || result == :halt)
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,73 @@
1
+ module Searls
2
+ module Auth
3
+ class ResetsPasswordsController < BaseController
4
+ before_action :ensure_password_reset_enabled
5
+ before_action :load_user_from_token
6
+ before_action :clear_email_otp_from_session!, only: [:show, :update]
7
+
8
+ def show
9
+ @token = params[:token]
10
+ @user_email = @user.email
11
+ render Searls::Auth.config.password_reset_edit_view, layout: Searls::Auth.config.layout
12
+ end
13
+
14
+ def update
15
+ result = ResetsPassword.new.reset(
16
+ user: @user,
17
+ password: params[:password],
18
+ password_confirmation: params[:password_confirmation]
19
+ )
20
+
21
+ if result.success?
22
+ handle_successful_reset(result.user)
23
+ else
24
+ flash.now[:alert] = Array(result.errors).first
25
+ @token = params[:token]
26
+ @user_email = @user.email
27
+ render Searls::Auth.config.password_reset_edit_view, layout: Searls::Auth.config.layout, status: :unprocessable_content
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def ensure_password_reset_enabled
34
+ return if Searls::Auth.config.password_reset_enabled?
35
+
36
+ flash[:alert] = Searls::Auth.config.resolve(:flash_error_after_password_reset_not_enabled, params)
37
+ redirect_to searls_auth.login_path(
38
+ redirect_path: params[:redirect_path],
39
+ redirect_subdomain: params[:redirect_subdomain]
40
+ )
41
+ nil
42
+ end
43
+
44
+ def load_user_from_token
45
+ token = params[:token].to_s
46
+ @user = Searls::Auth.config.password_reset_token_finder.call(token)
47
+ return if @user.present?
48
+
49
+ flash[:alert] = Searls::Auth.config.resolve(:flash_error_after_password_reset_token_invalid, params)
50
+ redirect_to searls_auth.password_reset_request_path(
51
+ redirect_path: params[:redirect_path],
52
+ redirect_subdomain: params[:redirect_subdomain]
53
+ )
54
+ nil
55
+ end
56
+
57
+ def handle_successful_reset(user)
58
+ flash[:notice] = Searls::Auth.config.resolve(:flash_notice_after_password_reset, user, params)
59
+
60
+ if Searls::Auth.config.auto_login_after_password_reset
61
+ session[:user_id] = user.id
62
+ session[:has_logged_in_before] = true
63
+ redirect_after_login(user)
64
+ else
65
+ redirect_to searls_auth.login_path(
66
+ redirect_path: params[:redirect_path],
67
+ redirect_subdomain: params[:redirect_subdomain]
68
+ )
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,83 @@
1
+ module Searls
2
+ module Auth
3
+ class SettingsController < BaseController
4
+ before_action :ensure_password_auth_enabled
5
+ before_action :ensure_authenticated_user
6
+ before_action :load_settings_user
7
+
8
+ helper_method :settings_user, :password_on_file?
9
+
10
+ def edit
11
+ render :edit, layout: Searls::Auth.config.layout
12
+ end
13
+
14
+ def update
15
+ permitted_params = settings_params.to_h
16
+ result = UpdatesSettings.new(
17
+ user: settings_user,
18
+ params: permitted_params
19
+ ).update
20
+
21
+ @settings_user = result.user
22
+ @password_on_file = nil
23
+
24
+ if result.success?
25
+ flash[:notice] = Searls::Auth.config.resolve(:flash_notice_after_settings_update, settings_user, params)
26
+ else
27
+ # Normally, we would flash.now and render `settings_view`, but this controller is
28
+ # intended to back forms hosted elsewhere. Redirecting keeps the host UI in control
29
+ # while surfacing validation errors via the session flash.
30
+ flash[:alert] = Array(result.errors).compact_blank.first
31
+ end
32
+ redirect_target = Searls::Auth.config.resolve(
33
+ :redirect_path_after_settings_change,
34
+ settings_user, params, request, searls_auth
35
+ ) || searls_auth.edit_settings_path
36
+ redirect_to redirect_target
37
+ end
38
+
39
+ private
40
+
41
+ def ensure_password_auth_enabled
42
+ return if Searls::Auth.config.auth_methods.include?(:password)
43
+
44
+ head :not_found
45
+ nil
46
+ end
47
+
48
+ def ensure_authenticated_user
49
+ return if session[:user_id].present?
50
+
51
+ redirect_to searls_auth.login_path(
52
+ redirect_path: request.original_fullpath,
53
+ redirect_subdomain: request.subdomain
54
+ )
55
+ nil
56
+ end
57
+
58
+ def load_settings_user
59
+ @settings_user = Searls::Auth.config.user_finder_by_id.call(session[:user_id])
60
+ return if @settings_user.present?
61
+
62
+ session.delete(:user_id)
63
+ redirect_to searls_auth.login_path(
64
+ redirect_path: request.original_fullpath,
65
+ redirect_subdomain: request.subdomain
66
+ )
67
+ nil
68
+ end
69
+
70
+ attr_reader :settings_user
71
+
72
+ def password_on_file?
73
+ @password_on_file ||= settings_user && Searls::Auth.config.password_present?(settings_user)
74
+ end
75
+
76
+ def settings_params
77
+ permitted = [:password, :password_confirmation, :current_password]
78
+
79
+ params.fetch(:settings, ActionController::Parameters.new).permit(permitted)
80
+ end
81
+ end
82
+ end
83
+ end
@@ -1,93 +1,63 @@
1
1
  module Searls
2
2
  module Auth
3
3
  class VerificationsController < BaseController
4
- before_action :reset_expired_short_code
4
+ before_action :reset_expired_email_otp
5
5
 
6
6
  def show
7
- render searls_auth_config.verify_view, layout: searls_auth_config.layout
7
+ if !(Searls::Auth.config.auth_methods & [:email_link, :email_otp]).any?
8
+ redirect_to searls_auth.login_path(**forwardable_params)
9
+ else
10
+ render Searls::Auth.config.verify_view, layout: Searls::Auth.config.layout
11
+ end
8
12
  end
9
13
 
10
14
  def create
11
15
  auth_method = params[:short_code].present? ? :email_otp : :email_link
12
- if auth_method == :email_otp && !searls_auth_config.auth_methods.include?(:email_otp)
13
- flash[:error] = searls_auth_config.resolve(:flash_error_after_verify_attempt_invalid_link, params)
14
- return redirect_to searls_auth.login_path(
15
- redirect_path: params[:redirect_path],
16
- redirect_subdomain: params[:redirect_subdomain]
17
- )
16
+ if auth_method == :email_otp && !Searls::Auth.config.auth_methods.include?(:email_otp)
17
+ flash[:alert] = Searls::Auth.config.resolve(:flash_error_after_verify_attempt_invalid_link, params)
18
+ return redirect_to searls_auth.login_path(**forwardable_params)
18
19
  end
19
- if auth_method == :email_link && !searls_auth_config.auth_methods.include?(:email_link)
20
- flash[:error] = searls_auth_config.resolve(:flash_error_after_verify_attempt_invalid_link, params)
21
- return redirect_to searls_auth.login_path(
22
- redirect_path: params[:redirect_path],
23
- redirect_subdomain: params[:redirect_subdomain]
24
- )
20
+ if auth_method == :email_link && !Searls::Auth.config.auth_methods.include?(:email_link)
21
+ flash[:alert] = Searls::Auth.config.resolve(:flash_error_after_verify_attempt_invalid_link, params)
22
+ return redirect_to searls_auth.login_path(**forwardable_params)
25
23
  end
26
24
  authenticator = AuthenticatesUser.new
27
25
  result = case auth_method
28
26
  when :email_otp
29
- log_short_code_verification_attempt!
30
- authenticator.authenticate_by_short_code(params[:short_code], session)
27
+ log_email_otp_verification_attempt!
28
+ authenticator.authenticate_by_email_otp(params[:short_code], session)
31
29
  when :email_link
32
30
  authenticator.authenticate_by_token(params[:token])
33
31
  end
34
32
 
35
33
  if result.success?
34
+ if [:email_otp, :email_link].include?(auth_method)
35
+ unless Searls::Auth.config.email_verified_predicate.call(result.user)
36
+ Searls::Auth.config.email_verified_setter.call(result.user)
37
+ end
38
+ end
36
39
  session[:user_id] = result.user.id
37
40
  session[:has_logged_in_before] = true
38
- flash[:notice] = searls_auth_config.resolve(
39
- :flash_notice_after_verification,
40
- result.user, params
41
- )
42
- if params[:redirect_subdomain].present? && params[:redirect_subdomain] != request.subdomain
43
- redirect_to generate_full_url(
44
- params[:redirect_path],
45
- params[:redirect_subdomain]
46
- ), allow_other_host: true
47
- elsif params[:redirect_path].present?
48
- redirect_to params[:redirect_path]
41
+ flash[:notice] = Searls::Auth.config.resolve(:flash_notice_after_login, result.user, params)
42
+ if (target = target_redirect_url)
43
+ redirect_with_host_awareness(target)
49
44
  else
50
- redirect_to searls_auth_config.resolve(:default_redirect_path_after_login,
51
- result.user, params, request, main_app)
45
+ redirect_to Searls::Auth.config.resolve(:redirect_path_after_login, result.user, params, request, main_app)
52
46
  end
53
47
  elsif auth_method == :email_otp
54
- if result.exceeded_short_code_attempt_limit?
55
- clear_short_code_from_session!
56
- flash[:error] = searls_auth_config.resolve(
57
- :flash_error_after_verify_attempt_exceeds_limit,
58
- params
59
- )
60
- redirect_to searls_auth.login_path(
61
- redirect_path: params[:redirect_path],
62
- redirect_subdomain: params[:redirect_subdomain]
63
- )
48
+ if result.exceeded_email_otp_attempt_limit?
49
+ clear_email_otp_from_session!
50
+ flash[:alert] = Searls::Auth.config.resolve(:flash_error_after_verify_attempt_exceeds_limit, params)
51
+ redirect_to searls_auth.login_path(**forwardable_params)
64
52
  else
65
- flash[:error] = searls_auth_config.resolve(
66
- :flash_error_after_verify_attempt_incorrect_short_code,
67
- params
68
- )
69
- render searls_auth_config.verify_view, layout: searls_auth_config.layout, status: :unprocessable_entity
53
+ flash[:alert] = Searls::Auth.config.resolve(:flash_error_after_verify_attempt_incorrect_email_otp, params)
54
+ render Searls::Auth.config.verify_view, layout: Searls::Auth.config.layout, status: :unprocessable_content
70
55
  end
71
56
  else
72
- flash[:error] = searls_auth_config.resolve(
73
- :flash_error_after_verify_attempt_invalid_link,
74
- params
75
- )
76
- redirect_to searls_auth.login_path(
77
- redirect_path: params[:redirect_path],
78
- redirect_subdomain: params[:redirect_subdomain]
79
- )
57
+ flash[:alert] = Searls::Auth.config.resolve(:flash_error_after_verify_attempt_invalid_link, params)
58
+ redirect_to searls_auth.login_path(**forwardable_params)
80
59
  end
81
60
  end
82
-
83
- private
84
-
85
- def generate_full_url(path, subdomain)
86
- port = request.port
87
- port_string = (port == 80 || port == 443) ? "" : ":#{port}"
88
-
89
- "#{request.protocol}#{subdomain}.#{request.domain}#{port_string}#{path}"
90
- end
91
61
  end
92
62
  end
93
63
  end
@@ -25,6 +25,14 @@ module Searls
25
25
  routes.register_url(forwardable_params.merge(kwargs))
26
26
  end
27
27
 
28
+ def password_reset_request_path(**kwargs)
29
+ routes.password_reset_request_path(forwardable_params.merge(kwargs))
30
+ end
31
+
32
+ def password_reset_available?
33
+ Searls::Auth.config.password_reset_enabled?
34
+ end
35
+
28
36
  def login_stimulus_controller
29
37
  "searls-auth-login"
30
38
  end
@@ -64,11 +72,7 @@ module Searls
64
72
  private
65
73
 
66
74
  def forwardable_params
67
- {
68
- email: params[:email],
69
- redirect_path: params[:redirect_path],
70
- redirect_subdomain: params[:redirect_subdomain]
71
- }.compact_blank
75
+ @view_context.forwardable_params
72
76
  end
73
77
 
74
78
  def params
@@ -1,6 +1,6 @@
1
1
  module Searls
2
2
  module Auth
3
- class BaseMailer < ApplicationMailer # TODO should this be ActionMailer::Base? Trade-offs?
3
+ class BaseMailer < ApplicationMailer
4
4
  helper Searls::Auth::ApplicationHelper
5
5
  include Searls::Auth::ApplicationHelper
6
6
 
@@ -0,0 +1,29 @@
1
+ module Searls
2
+ module Auth
3
+ class EmailVerificationMailer < BaseMailer
4
+ def verification_email
5
+ @config = Searls::Auth.config
6
+ @user = params[:user]
7
+ @token = params[:token]
8
+ @redirect_path = params[:redirect_path]
9
+ @redirect_subdomain = params[:redirect_subdomain]
10
+
11
+ mail(
12
+ to: format_to(@user),
13
+ subject: mail_subject,
14
+ template_path: @config.mail_email_verification_template_path,
15
+ template_name: @config.mail_email_verification_template_name
16
+ ) do |format|
17
+ format.html { render layout: @config.mail_layout }
18
+ format.text
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def mail_subject
25
+ "Verify your #{searls_auth_helper.rpad(@config.app_name)}email"
26
+ end
27
+ end
28
+ end
29
+ end
@@ -7,7 +7,7 @@ module Searls
7
7
  @token = params[:token]
8
8
  @redirect_path = params[:redirect_path]
9
9
  @redirect_subdomain = params[:redirect_subdomain]
10
- @short_code = params[:short_code]
10
+ @email_otp = params[:email_otp]
11
11
 
12
12
  mail(
13
13
  to: format_to(@user),
@@ -24,8 +24,8 @@ module Searls
24
24
 
25
25
  def mail_subject
26
26
  methods = Searls::Auth.config.auth_methods
27
- if methods.include?(:email_otp) && @short_code.present?
28
- "Your #{searls_auth_helper.rpad(@config.app_name)}login code is #{@short_code}"
27
+ if methods.include?(:email_otp) && @email_otp.present?
28
+ "Your #{searls_auth_helper.rpad(@config.app_name)}login code is #{@email_otp}"
29
29
  else
30
30
  "Your #{searls_auth_helper.rpad(@config.app_name)}login link"
31
31
  end
@@ -0,0 +1,29 @@
1
+ module Searls
2
+ module Auth
3
+ class PasswordResetMailer < BaseMailer
4
+ def password_reset
5
+ @config = Searls::Auth.config
6
+ @user = params[:user]
7
+ @token = params[:token]
8
+ @redirect_path = params[:redirect_path]
9
+ @redirect_subdomain = params[:redirect_subdomain]
10
+
11
+ mail(
12
+ to: format_to(@user),
13
+ subject: mail_subject,
14
+ template_path: @config.mail_password_reset_template_path,
15
+ template_name: @config.mail_password_reset_template_name
16
+ ) do |format|
17
+ format.html { render layout: @config.mail_layout }
18
+ format.text
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def mail_subject
25
+ "Reset your #{searls_auth_helper.rpad(@config.app_name)}password"
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,23 @@
1
+ <%= content_for :width, 480 %>
2
+ <p>
3
+ <% if (user_name = searls_auth_helper.attr_for(@user, @config.user_name_method)).present? %>
4
+ Hello, <strong><%= user_name %></strong>!
5
+ <% else %>
6
+ Hello!
7
+ <% end %>
8
+ </p>
9
+ <p style="margin-top: 1rem;">
10
+ Please verify your email address to keep your account secure:
11
+ <div style="margin-top: 2rem; text-align: center;">
12
+ <%= link_to searls_auth.verify_email_url({
13
+ token: @token,
14
+ redirect_path: @redirect_path,
15
+ redirect_subdomain: @redirect_subdomain
16
+ }.compact_blank) do %>
17
+ <span style="width: 70%; box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); display: inline-block; padding-left: 2rem; padding-right: 2rem; padding-top: 1rem; padding-bottom: 1rem; font-size: 1.5rem; font-weight: 700; border-radius: 0.75rem; background-color: <%= @config.email_button_color %>; color: white;">Verify email address</span>
18
+ <% end %>
19
+ </div>
20
+ </p>
21
+ <p style="margin-top: 2rem; font-size: 0.875rem; color: #6b7280;">
22
+ If you didn't create an account or change your email, you can ignore this message.
23
+ </p>
@@ -0,0 +1,6 @@
1
+ Hello<% if (user_name = searls_auth_helper.attr_for(@user, @config.user_name_method)).present? %>, <%= user_name %><% end %>!
2
+
3
+ Verify your email using this link:
4
+ <%= searls_auth.verify_email_url({ token: @token, redirect_path: @redirect_path, redirect_subdomain: @redirect_subdomain }.compact_blank) %>
5
+
6
+ If you didn't ask for this, you can ignore this email.
@@ -6,7 +6,7 @@
6
6
  Hello!
7
7
  <% end %>
8
8
  </p>
9
- <% if Searls::Auth.config.auth_methods.include?(:email_link) %>
9
+ <% if @config.auth_methods.include?(:email_link) %>
10
10
  <p style="margin-top: 1rem;">
11
11
  You can log in by clicking this button:
12
12
  <div style="margin-top: 2rem; text-align: center;">
@@ -18,17 +18,17 @@
18
18
  <span style="width: 70%; box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); display: inline-block; padding-left: 2rem; padding-right: 2rem; padding-top: 1rem; padding-bottom: 1rem; font-size: 1.5rem; font-weight: 700; border-radius: 0.75rem; background-color: <%= @config.email_button_color %>; color: white;">Log in</span>
19
19
  <% end %>
20
20
  <div style="margin-top: 0.5rem; font-size: 0.875rem; color: #6b7280;">
21
- (This link will expire in <%= @config.token_expiry_minutes %> minutes.)
21
+ (This link expires soon, so we recommend using it right away.)
22
22
  </div>
23
23
  </div>
24
24
  </p>
25
25
  <% end %>
26
- <% if Searls::Auth.config.auth_methods.include?(:email_otp) && @short_code.present? %>
26
+ <% if @config.auth_methods.include?(:email_otp) && @email_otp.present? %>
27
27
  <p style="margin-top: 2rem;">
28
28
  You can log in by entering this code:
29
29
  <div style="margin-top: 2rem; text-align: center;">
30
30
  <span id="one-time-code" style="width: 70%; display: inline-block; padding-left: 2rem; padding-right: 2rem; padding-top: 1rem; padding-bottom: 1rem; font-size: 1.5rem; font-weight: 700; border: 1px solid; border-radius: 0.75rem; background-color: #f3f4f6; border-color: #e5e7eb;">
31
- <%= @short_code %>
31
+ <%= @email_otp %>
32
32
  </span>
33
33
  <div style="margin-top: 0.5rem; font-size: 0.875rem; color: #6b7280;">
34
34
  (So will this code.)
@@ -37,5 +37,5 @@
37
37
  </p>
38
38
  <% end %>
39
39
  <p style="margin-top: 2rem; font-size: 0.875rem; color: #6b7280;">
40
- p.s. Didn't try to log in? You can safely ignore this email. If this keeps happening, please <%= link_to "let us know", "mailto:#{@config.support_email_address}" %>.
40
+ p.s. Didn't try to log in? You can safely ignore this email.
41
41
  </p>
@@ -4,7 +4,7 @@ Hi, <%= user_name %>!
4
4
  Hello!
5
5
  <% end %>
6
6
 
7
- <% if Searls::Auth.config.auth_methods.include?(:email_link) %>
7
+ <% if @config.auth_methods.include?(:email_link) %>
8
8
  You can log in by visiting this URL for your <%= searls_auth_helper.rpad(@config.app_name) %>account:
9
9
 
10
10
  <%= searls_auth.verify_token_url({
@@ -14,12 +14,11 @@ You can log in by visiting this URL for your <%= searls_auth_helper.rpad(@config
14
14
  }.compact_blank) %>
15
15
  <% end %>
16
16
 
17
- <% if Searls::Auth.config.auth_methods.include?(:email_otp) && @short_code.present? %>
17
+ <% if @config.auth_methods.include?(:email_otp) && @email_otp.present? %>
18
18
 
19
19
  You can log in by entering this code:
20
20
 
21
- <%= @short_code %>
21
+ <%= @email_otp %>
22
22
  <% end %>
23
23
 
24
- Didn't try to log in? You can safely ignore this email.<%= " If this keeps
25
- happening, please let us know at #{@config.support_email_address}" if @config.support_email_address.present? %>
24
+ Didn't try to log in? You can safely ignore this email.
@@ -3,12 +3,20 @@
3
3
  <%= form_with url: searls_auth.login_path, method: :post, data: {controller: searls_auth_helper.login_stimulus_controller} do |f| %>
4
4
  <%= f.hidden_field :redirect_path, value: params[:redirect_path] %>
5
5
  <%= f.hidden_field :redirect_subdomain, value: params[:redirect_subdomain] %>
6
- <%= f.label :email %>
7
- <%= f.email_field :email, required: true, value: params[:email], data: searls_auth_helper.email_field_stimulus_data %>
8
- <%= f.submit "Log in" %>
6
+ <%= render "searls/auth/shared/login_fields", f: f %>
7
+ <div>
8
+ <%= f.submit "Log in" %>
9
+ <% if Searls::Auth.config.auth_methods.include?(:password) && (Searls::Auth.config.auth_methods & [:email_link, :email_otp]).any? %>
10
+ <%= button_tag "Send login email", name: "send_login_email", value: "1", type: "submit" %>
11
+ <% end %>
12
+ </div>
13
+ <% if searls_auth_helper.password_reset_available? %>
14
+ <div>
15
+ <%= link_to "Forgot your password?", searls_auth_helper.password_reset_request_path %>
16
+ </div>
17
+ <% end %>
9
18
  <% end %>
10
19
 
11
20
  <p>
12
21
  Not registered yet? <%= link_to "Sign up", searls_auth_helper.register_path %> instead!
13
22
  </p>
14
-
@@ -0,0 +1,23 @@
1
+ <%= content_for :width, 480 %>
2
+ <p>
3
+ <% if (user_name = searls_auth_helper.attr_for(@user, @config.user_name_method)).present? %>
4
+ Hello, <strong><%= user_name %></strong>!
5
+ <% else %>
6
+ Hello!
7
+ <% end %>
8
+ </p>
9
+ <p style="margin-top: 1rem;">
10
+ You can reset your password by clicking this button:
11
+ <div style="margin-top: 2rem; text-align: center;">
12
+ <%= link_to password_reset_edit_url({
13
+ token: @token,
14
+ redirect_path: @redirect_path,
15
+ redirect_subdomain: @redirect_subdomain
16
+ }.compact_blank) do %>
17
+ <span style="width: 70%; box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); display: inline-block; padding-left: 2rem; padding-right: 2rem; padding-top: 1rem; padding-bottom: 1rem; font-size: 1.5rem; font-weight: 700; border-radius: 0.75rem; background-color: <%= @config.email_button_color %>; color: white;">Reset password</span>
18
+ <% end %>
19
+ </div>
20
+ </p>
21
+ <p style="margin-top: 2rem; font-size: 0.875rem; color: #6b7280;">
22
+ If you didn't ask to reset your password, you can ignore this email.
23
+ </p>
@@ -0,0 +1,6 @@
1
+ Hello<% if (user_name = searls_auth_helper.attr_for(@user, @config.user_name_method)).present? %>, <%= user_name %><% end %>!
2
+
3
+ Reset your password using this link:
4
+ <%= password_reset_edit_url({ token: @token, redirect_path: @redirect_path, redirect_subdomain: @redirect_subdomain }.compact_blank) %>
5
+
6
+ If you didn't ask for this, you can ignore this email.
@@ -0,0 +1,12 @@
1
+ <h1>Check your email!</h1>
2
+ <p>
3
+ We sent a verification link to <strong><%= h(session[:searls_auth_pending_email]) || h(params[:email]) %></strong>.
4
+ Click the link to verify your email before you can log in with a password.
5
+ If it doesn’t arrive in a minute, check spam or resend it below.
6
+ </p>
7
+
8
+ <p style="margin-top: 1rem;">
9
+ <%= link_to "Resend verification email",
10
+ searls_auth.resend_email_verification_path(**forwardable_params),
11
+ data: { turbo_method: :patch } %>
12
+ </p>
@@ -3,8 +3,7 @@
3
3
  <%= form_with url: searls_auth.register_path, data: {controller: searls_auth_helper.login_stimulus_controller} do |f| %>
4
4
  <%= f.hidden_field :redirect_path, value: params[:redirect_path] %>
5
5
  <%= f.hidden_field :redirect_subdomain, value: params[:redirect_subdomain] %>
6
- <%= f.label :email %>
7
- <%= f.email_field :email, value: params[:email], required: true, data: searls_auth_helper.email_field_stimulus_data %>
6
+ <%= render "searls/auth/shared/register_fields", f: f %>
8
7
  <%= f.submit "Register" %>
9
8
  <% end %>
10
9
 
@@ -0,0 +1,17 @@
1
+ <h1>Forgot your password?</h1>
2
+
3
+ <%= form_with url: searls_auth.password_reset_request_path, method: :post, data: {controller: searls_auth_helper.login_stimulus_controller} do |f| %>
4
+ <%= f.hidden_field :redirect_path, value: params[:redirect_path] %>
5
+ <%= f.hidden_field :redirect_subdomain, value: params[:redirect_subdomain] %>
6
+ <div>
7
+ <%= f.label :email, "Email address" %>
8
+ <%= f.email_field :email, required: true, value: params[:email], autocomplete: "email" %>
9
+ </div>
10
+ <div>
11
+ <%= f.submit "Send reset instructions" %>
12
+ </div>
13
+ <% end %>
14
+
15
+ <p>
16
+ Remembered your password? <%= link_to "Log in", searls_auth_helper.login_path %> instead.
17
+ </p>