searls-auth 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/.standard.yml +2 -0
 - data/CHANGELOG.md +11 -1
 - data/app/controllers/searls/auth/base_controller.rb +15 -8
 - data/app/controllers/searls/auth/logins_controller.rb +20 -10
 - data/app/controllers/searls/auth/registrations_controller.rb +17 -4
 - data/app/controllers/searls/auth/verifications_controller.rb +27 -12
 - data/app/helpers/searls/auth/application_helper.rb +4 -1
 - data/app/javascript/controllers/searls_auth_login_controller.js +0 -1
 - data/app/mailers/searls/auth/base_mailer.rb +11 -0
 - data/app/mailers/searls/auth/login_link_mailer.rb +1 -1
 - data/app/views/searls/auth/login_link_mailer/login_link.html.erb +1 -1
 - data/app/views/searls/auth/login_link_mailer/login_link.text.erb +2 -2
 - data/app/views/searls/auth/logins/show.html.erb +2 -8
 - data/app/views/searls/auth/registrations/show.html.erb +2 -8
 - data/app/views/searls/auth/verifications/show.html.erb +2 -1
 - data/lib/searls/auth/authenticates_user.rb +8 -6
 - data/lib/searls/auth/config.rb +12 -1
 - data/lib/searls/auth/version.rb +1 -1
 - data/lib/searls/auth.rb +17 -3
 - data/script/setup +14 -0
 - data/script/setup_ci +7 -0
 - data/script/test +13 -0
 - metadata +4 -1
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 5e2b6983e76f48e3663946462d67f0dfdf62914aa8187c29a332111f430335f2
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 0cfbab5b379a053ff0f23406e25c045caa6d7ca2c249330d0ac24b459e6f1ecf
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 820ae8d2258753dd6e7f2f9bea7054537f0242aacc5218f220cc3a83d639a11a5204bcca728a95ec06e4ae8094994609abf0859f5df8162684a56c42bd27757b
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 9d6f9469c3c4d99cd42f0b410f89934951924c605cff3fe9bf7490064d790d1a814b20e154574de45d525b325acaac6ccd793496f2ffbc83645042e4f2a0268f
         
     | 
    
        data/.standard.yml
    CHANGED
    
    
    
        data/CHANGELOG.md
    CHANGED
    
    | 
         @@ -1,5 +1,15 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ## [Unreleased]
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
      
 3 
     | 
    
         
            +
            ## [0.1.0] - 2025-04-26
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            * Add `max_allowed_short_code_attempts` configuration, beyond which the code is erased from the session and the user needs to login again (default: 10)
         
     | 
| 
      
 6 
     | 
    
         
            +
            * Allow configuration of flash messages
         
     | 
| 
      
 7 
     | 
    
         
            +
            * Fix a routing error if the user is already registered
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            ## [0.0.2] - 2025-04-16
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            * Little fixes. Renamed `user_name_field` to `user_name_method`
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
       3 
13 
     | 
    
         
             
            ## [0.0.1] - 2025-04-15
         
     | 
| 
       4 
14 
     | 
    
         | 
| 
       5 
     | 
    
         
            -
             
     | 
| 
      
 15 
     | 
    
         
            +
            * Initial release
         
     | 
| 
         @@ -13,22 +13,29 @@ module Searls 
     | 
|
| 
       13 
13 
     | 
    
         
             
                  end
         
     | 
| 
       14 
14 
     | 
    
         | 
| 
       15 
15 
     | 
    
         
             
                  def attach_short_code_to_session!(user)
         
     | 
| 
       16 
     | 
    
         
            -
                    session[: 
     | 
| 
       17 
     | 
    
         
            -
                    session[: 
     | 
| 
       18 
     | 
    
         
            -
                    session[: 
     | 
| 
      
 16 
     | 
    
         
            +
                    session[:searls_auth_short_code_user_id] = user.id
         
     | 
| 
      
 17 
     | 
    
         
            +
                    session[:searls_auth_short_code] = SecureRandom.random_number(1000000).to_s.rjust(6, "0")
         
     | 
| 
      
 18 
     | 
    
         
            +
                    session[:searls_auth_short_code_generated_at] = Time.current
         
     | 
| 
      
 19 
     | 
    
         
            +
                    session[:searls_auth_short_code_verification_attempts] = 0
         
     | 
| 
       19 
20 
     | 
    
         
             
                  end
         
     | 
| 
       20 
21 
     | 
    
         | 
| 
       21 
22 
     | 
    
         
             
                  def reset_expired_short_code
         
     | 
| 
       22 
     | 
    
         
            -
                    if session[: 
     | 
| 
       23 
     | 
    
         
            -
                        Time.zone.parse(session[: 
     | 
| 
      
 23 
     | 
    
         
            +
                    if session[:searls_auth_short_code_generated_at].present? &&
         
     | 
| 
      
 24 
     | 
    
         
            +
                        Time.zone.parse(session[:searls_auth_short_code_generated_at]) < Searls::Auth.config.token_expiry_minutes.minutes.ago
         
     | 
| 
       24 
25 
     | 
    
         
             
                      clear_short_code_from_session!
         
     | 
| 
       25 
26 
     | 
    
         
             
                    end
         
     | 
| 
       26 
27 
     | 
    
         
             
                  end
         
     | 
| 
       27 
28 
     | 
    
         | 
| 
       28 
29 
     | 
    
         
             
                  def clear_short_code_from_session!
         
     | 
| 
       29 
     | 
    
         
            -
                    session.delete(: 
     | 
| 
       30 
     | 
    
         
            -
                    session.delete(: 
     | 
| 
       31 
     | 
    
         
            -
                    session.delete(: 
     | 
| 
      
 30 
     | 
    
         
            +
                    session.delete(:searls_auth_short_code_user_id)
         
     | 
| 
      
 31 
     | 
    
         
            +
                    session.delete(:searls_auth_short_code_generated_at)
         
     | 
| 
      
 32 
     | 
    
         
            +
                    session.delete(:searls_auth_short_code)
         
     | 
| 
      
 33 
     | 
    
         
            +
                    session.delete(:searls_auth_short_code_verification_attempts)
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                  def log_short_code_verification_attempt!
         
     | 
| 
      
 37 
     | 
    
         
            +
                    session[:searls_auth_short_code_verification_attempts] ||= 0
         
     | 
| 
      
 38 
     | 
    
         
            +
                    session[:searls_auth_short_code_verification_attempts] += 1
         
     | 
| 
       32 
39 
     | 
    
         
             
                  end
         
     | 
| 
       33 
40 
     | 
    
         
             
                end
         
     | 
| 
       34 
41 
     | 
    
         
             
              end
         
     | 
| 
         @@ -16,19 +16,26 @@ module Searls 
     | 
|
| 
       16 
16 
     | 
    
         
             
                        user:,
         
     | 
| 
       17 
17 
     | 
    
         
             
                        redirect_path: params[:redirect_path],
         
     | 
| 
       18 
18 
     | 
    
         
             
                        redirect_subdomain: params[:redirect_subdomain],
         
     | 
| 
       19 
     | 
    
         
            -
                        short_code: session[: 
     | 
| 
      
 19 
     | 
    
         
            +
                        short_code: session[:searls_auth_short_code]
         
     | 
| 
       20 
20 
     | 
    
         
             
                      )
         
     | 
| 
       21 
     | 
    
         
            -
                      flash[:notice] =  
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
      
 21 
     | 
    
         
            +
                      flash[:notice] = searls_auth_config.resolve(
         
     | 
| 
      
 22 
     | 
    
         
            +
                        :flash_notice_after_login_attempt,
         
     | 
| 
      
 23 
     | 
    
         
            +
                        user, params
         
     | 
| 
      
 24 
     | 
    
         
            +
                      )
         
     | 
| 
      
 25 
     | 
    
         
            +
                      redirect_to searls_auth.verify_path(
         
     | 
| 
       23 
26 
     | 
    
         
             
                        redirect_path: params[:redirect_path],
         
     | 
| 
       24 
27 
     | 
    
         
             
                        redirect_subdomain: params[:redirect_subdomain]
         
     | 
| 
       25 
28 
     | 
    
         
             
                      )
         
     | 
| 
       26 
29 
     | 
    
         
             
                    else
         
     | 
| 
       27 
     | 
    
         
            -
                      flash.now[:error] =  
     | 
| 
       28 
     | 
    
         
            -
                         
     | 
| 
       29 
     | 
    
         
            -
                         
     | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
       31 
     | 
    
         
            -
             
     | 
| 
      
 30 
     | 
    
         
            +
                      flash.now[:error] = searls_auth_config.resolve(
         
     | 
| 
      
 31 
     | 
    
         
            +
                        :flash_error_after_login_attempt_unknown_email,
         
     | 
| 
      
 32 
     | 
    
         
            +
                        searls_auth.register_path(
         
     | 
| 
      
 33 
     | 
    
         
            +
                          email: params[:email],
         
     | 
| 
      
 34 
     | 
    
         
            +
                          redirect_path: params[:redirect_path],
         
     | 
| 
      
 35 
     | 
    
         
            +
                          redirect_subdomain: params[:redirect_subdomain]
         
     | 
| 
      
 36 
     | 
    
         
            +
                        ),
         
     | 
| 
      
 37 
     | 
    
         
            +
                        params
         
     | 
| 
      
 38 
     | 
    
         
            +
                      )
         
     | 
| 
       32 
39 
     | 
    
         
             
                      render searls_auth_config.login_view, layout: searls_auth_config.layout, status: :unprocessable_entity
         
     | 
| 
       33 
40 
     | 
    
         
             
                    end
         
     | 
| 
       34 
41 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -36,8 +43,11 @@ module Searls 
     | 
|
| 
       36 
43 
     | 
    
         
             
                  def destroy
         
     | 
| 
       37 
44 
     | 
    
         
             
                    ResetsSession.new.reset(self, except_for: [:has_logged_in_before])
         
     | 
| 
       38 
45 
     | 
    
         | 
| 
       39 
     | 
    
         
            -
                    flash[:notice] =  
     | 
| 
       40 
     | 
    
         
            -
             
     | 
| 
      
 46 
     | 
    
         
            +
                    flash[:notice] = searls_auth_config.resolve(
         
     | 
| 
      
 47 
     | 
    
         
            +
                      :flash_notice_after_logout,
         
     | 
| 
      
 48 
     | 
    
         
            +
                      params
         
     | 
| 
      
 49 
     | 
    
         
            +
                    )
         
     | 
| 
      
 50 
     | 
    
         
            +
                    redirect_to searls_auth.login_path
         
     | 
| 
       41 
51 
     | 
    
         
             
                  end
         
     | 
| 
       42 
52 
     | 
    
         
             
                end
         
     | 
| 
       43 
53 
     | 
    
         
             
              end
         
     | 
| 
         @@ -20,13 +20,26 @@ module Searls 
     | 
|
| 
       20 
20 
     | 
    
         | 
| 
       21 
21 
     | 
    
         
             
                      EmailsLink.new.email(
         
     | 
| 
       22 
22 
     | 
    
         
             
                        user: result.user,
         
     | 
| 
       23 
     | 
    
         
            -
                        short_code: session[: 
     | 
| 
      
 23 
     | 
    
         
            +
                        short_code: session[:searls_auth_short_code],
         
     | 
| 
       24 
24 
     | 
    
         
             
                        **redirect_params
         
     | 
| 
       25 
25 
     | 
    
         
             
                      )
         
     | 
| 
       26 
     | 
    
         
            -
                      flash[:notice] =  
     | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
      
 26 
     | 
    
         
            +
                      flash[:notice] = searls_auth_config.resolve(
         
     | 
| 
      
 27 
     | 
    
         
            +
                        :flash_notice_after_registration,
         
     | 
| 
      
 28 
     | 
    
         
            +
                        result.user, params
         
     | 
| 
      
 29 
     | 
    
         
            +
                      )
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                      redirect_to searls_auth.verify_path(**redirect_params)
         
     | 
| 
       28 
32 
     | 
    
         
             
                    else
         
     | 
| 
       29 
     | 
    
         
            -
                      flash.now[:error] =  
     | 
| 
      
 33 
     | 
    
         
            +
                      flash.now[:error] = searls_auth_config.resolve(
         
     | 
| 
      
 34 
     | 
    
         
            +
                        :flash_error_after_register_attempt,
         
     | 
| 
      
 35 
     | 
    
         
            +
                        result.error_messages,
         
     | 
| 
      
 36 
     | 
    
         
            +
                        searls_auth.login_path(
         
     | 
| 
      
 37 
     | 
    
         
            +
                          email: params[:email],
         
     | 
| 
      
 38 
     | 
    
         
            +
                          redirect_path: params[:redirect_path],
         
     | 
| 
      
 39 
     | 
    
         
            +
                          redirect_subdomain: params[:redirect_subdomain]
         
     | 
| 
      
 40 
     | 
    
         
            +
                        ),
         
     | 
| 
      
 41 
     | 
    
         
            +
                        params
         
     | 
| 
      
 42 
     | 
    
         
            +
                      )
         
     | 
| 
       30 
43 
     | 
    
         
             
                      render searls_auth_config.register_view, layout: searls_auth_config.layout, status: :unprocessable_entity
         
     | 
| 
       31 
44 
     | 
    
         
             
                    end
         
     | 
| 
       32 
45 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -12,6 +12,7 @@ module Searls 
     | 
|
| 
       12 
12 
     | 
    
         
             
                    authenticator = AuthenticatesUser.new
         
     | 
| 
       13 
13 
     | 
    
         
             
                    result = case auth_method
         
     | 
| 
       14 
14 
     | 
    
         
             
                    when :short_code
         
     | 
| 
      
 15 
     | 
    
         
            +
                      log_short_code_verification_attempt!
         
     | 
| 
       15 
16 
     | 
    
         
             
                      authenticator.authenticate_by_short_code(params[:short_code], session)
         
     | 
| 
       16 
17 
     | 
    
         
             
                    when :token
         
     | 
| 
       17 
18 
     | 
    
         
             
                      authenticator.authenticate_by_token(params[:token])
         
     | 
| 
         @@ -20,6 +21,10 @@ module Searls 
     | 
|
| 
       20 
21 
     | 
    
         
             
                    if result.success?
         
     | 
| 
       21 
22 
     | 
    
         
             
                      session[:user_id] = result.user.id
         
     | 
| 
       22 
23 
     | 
    
         
             
                      session[:has_logged_in_before] = true
         
     | 
| 
      
 24 
     | 
    
         
            +
                      flash[:notice] = searls_auth_config.resolve(
         
     | 
| 
      
 25 
     | 
    
         
            +
                        :flash_notice_after_verification,
         
     | 
| 
      
 26 
     | 
    
         
            +
                        result.user, params
         
     | 
| 
      
 27 
     | 
    
         
            +
                      )
         
     | 
| 
       23 
28 
     | 
    
         
             
                      if params[:redirect_subdomain].present? && params[:redirect_subdomain] != request.subdomain
         
     | 
| 
       24 
29 
     | 
    
         
             
                        redirect_to generate_full_url(
         
     | 
| 
       25 
30 
     | 
    
         
             
                          params[:redirect_path],
         
     | 
| 
         @@ -32,25 +37,35 @@ module Searls 
     | 
|
| 
       32 
37 
     | 
    
         
             
                          result.user, params, request, main_app)
         
     | 
| 
       33 
38 
     | 
    
         
             
                      end
         
     | 
| 
       34 
39 
     | 
    
         
             
                    elsif auth_method == :short_code
         
     | 
| 
       35 
     | 
    
         
            -
                       
     | 
| 
       36 
     | 
    
         
            -
             
     | 
| 
      
 40 
     | 
    
         
            +
                      if result.exceeded_short_code_attempt_limit?
         
     | 
| 
      
 41 
     | 
    
         
            +
                        clear_short_code_from_session!
         
     | 
| 
      
 42 
     | 
    
         
            +
                        flash[:error] = searls_auth_config.resolve(
         
     | 
| 
      
 43 
     | 
    
         
            +
                          :flash_error_after_verify_attempt_exceeds_limit,
         
     | 
| 
      
 44 
     | 
    
         
            +
                          params
         
     | 
| 
      
 45 
     | 
    
         
            +
                        )
         
     | 
| 
      
 46 
     | 
    
         
            +
                        redirect_to searls_auth.login_path(
         
     | 
| 
      
 47 
     | 
    
         
            +
                          redirect_path: params[:redirect_path],
         
     | 
| 
      
 48 
     | 
    
         
            +
                          redirect_subdomain: params[:redirect_subdomain]
         
     | 
| 
      
 49 
     | 
    
         
            +
                        )
         
     | 
| 
      
 50 
     | 
    
         
            +
                      else
         
     | 
| 
      
 51 
     | 
    
         
            +
                        flash[:error] = searls_auth_config.resolve(
         
     | 
| 
      
 52 
     | 
    
         
            +
                          :flash_error_after_verify_attempt_incorrect_short_code,
         
     | 
| 
      
 53 
     | 
    
         
            +
                          params
         
     | 
| 
      
 54 
     | 
    
         
            +
                        )
         
     | 
| 
      
 55 
     | 
    
         
            +
                        render searls_auth_config.verify_view, layout: searls_auth_config.layout, status: :unprocessable_entity
         
     | 
| 
      
 56 
     | 
    
         
            +
                      end
         
     | 
| 
       37 
57 
     | 
    
         
             
                    else
         
     | 
| 
       38 
     | 
    
         
            -
                      flash[:error] =  
     | 
| 
       39 
     | 
    
         
            -
             
     | 
| 
      
 58 
     | 
    
         
            +
                      flash[:error] = searls_auth_config.resolve(
         
     | 
| 
      
 59 
     | 
    
         
            +
                        :flash_error_after_verify_attempt_invalid_link,
         
     | 
| 
      
 60 
     | 
    
         
            +
                        params
         
     | 
| 
      
 61 
     | 
    
         
            +
                      )
         
     | 
| 
      
 62 
     | 
    
         
            +
                      redirect_to searls_auth.login_path(
         
     | 
| 
       40 
63 
     | 
    
         
             
                        redirect_path: params[:redirect_path],
         
     | 
| 
       41 
64 
     | 
    
         
             
                        redirect_subdomain: params[:redirect_subdomain]
         
     | 
| 
       42 
65 
     | 
    
         
             
                      )
         
     | 
| 
       43 
66 
     | 
    
         
             
                    end
         
     | 
| 
       44 
67 
     | 
    
         
             
                  end
         
     | 
| 
       45 
68 
     | 
    
         | 
| 
       46 
     | 
    
         
            -
                  def destroy
         
     | 
| 
       47 
     | 
    
         
            -
                    ResetsSession.new.reset(self,
         
     | 
| 
       48 
     | 
    
         
            -
                      except_for: searls_auth_config.preserve_session_keys_after_logout)
         
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
       50 
     | 
    
         
            -
                    flash[:notice] = "You've been logged out."
         
     | 
| 
       51 
     | 
    
         
            -
                    redirect_to login_path
         
     | 
| 
       52 
     | 
    
         
            -
                  end
         
     | 
| 
       53 
     | 
    
         
            -
             
     | 
| 
       54 
69 
     | 
    
         
             
                  private
         
     | 
| 
       55 
70 
     | 
    
         | 
| 
       56 
71 
     | 
    
         
             
                  def generate_full_url(path, subdomain)
         
     | 
| 
         @@ -51,10 +51,13 @@ module Searls 
     | 
|
| 
       51 
51 
     | 
    
         
             
                  end
         
     | 
| 
       52 
52 
     | 
    
         | 
| 
       53 
53 
     | 
    
         
             
                  def attr_for(model, field_name)
         
     | 
| 
       54 
     | 
    
         
            -
                    model. 
     | 
| 
      
 54 
     | 
    
         
            +
                    if model.respond_to?(field_name)
         
     | 
| 
      
 55 
     | 
    
         
            +
                      model.send(field_name)
         
     | 
| 
      
 56 
     | 
    
         
            +
                    end
         
     | 
| 
       55 
57 
     | 
    
         
             
                  end
         
     | 
| 
       56 
58 
     | 
    
         | 
| 
       57 
59 
     | 
    
         
             
                  def rpad(s, spacer = " ", times = 1)
         
     | 
| 
      
 60 
     | 
    
         
            +
                    return "" if s.blank?
         
     | 
| 
       58 
61 
     | 
    
         
             
                    "#{s}#{spacer * times}"
         
     | 
| 
       59 
62 
     | 
    
         
             
                  end
         
     | 
| 
       60 
63 
     | 
    
         | 
| 
         @@ -3,6 +3,17 @@ module Searls 
     | 
|
| 
       3 
3 
     | 
    
         
             
                class BaseMailer < ApplicationMailer # TODO should this be ActionMailer::Base? Trade-offs?
         
     | 
| 
       4 
4 
     | 
    
         
             
                  helper Searls::Auth::ApplicationHelper
         
     | 
| 
       5 
5 
     | 
    
         
             
                  include Searls::Auth::ApplicationHelper
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                  protected
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                  def format_to(user)
         
     | 
| 
      
 10 
     | 
    
         
            +
                    name_field = searls_auth_helper.attr_for(user, Searls::Auth.config.user_name_method)
         
     | 
| 
      
 11 
     | 
    
         
            +
                    if name_field.present?
         
     | 
| 
      
 12 
     | 
    
         
            +
                      "#{user.name} <#{user.email}>"
         
     | 
| 
      
 13 
     | 
    
         
            +
                    else
         
     | 
| 
      
 14 
     | 
    
         
            +
                      user.email
         
     | 
| 
      
 15 
     | 
    
         
            +
                    end
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
       6 
17 
     | 
    
         
             
                end
         
     | 
| 
       7 
18 
     | 
    
         
             
              end
         
     | 
| 
       8 
19 
     | 
    
         
             
            end
         
     | 
| 
         @@ -11,7 +11,7 @@ module Searls 
     | 
|
| 
       11 
11 
     | 
    
         | 
| 
       12 
12 
     | 
    
         
             
                    mail(
         
     | 
| 
       13 
13 
     | 
    
         
             
                      to: format_to(@user),
         
     | 
| 
       14 
     | 
    
         
            -
                      subject: "Your #{searls_auth_helper.rpad(@config.app_name)} 
     | 
| 
      
 14 
     | 
    
         
            +
                      subject: "Your #{searls_auth_helper.rpad(@config.app_name)}login code is #{@short_code}",
         
     | 
| 
       15 
15 
     | 
    
         
             
                      template_path: @config.mail_login_template_path,
         
     | 
| 
       16 
16 
     | 
    
         
             
                      template_name: @config.mail_login_template_name
         
     | 
| 
       17 
17 
     | 
    
         
             
                    ) do |format|
         
     | 
| 
         @@ -1,6 +1,6 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            <%= content_for :width, 480 %>
         
     | 
| 
       2 
2 
     | 
    
         
             
              <p>
         
     | 
| 
       3 
     | 
    
         
            -
                <% if (user_name = searls_auth_helper.attr_for(@user, @config. 
     | 
| 
      
 3 
     | 
    
         
            +
                <% if (user_name = searls_auth_helper.attr_for(@user, @config.user_name_method)).present? %>
         
     | 
| 
       4 
4 
     | 
    
         
             
                  Hello, <strong><%= user_name %></strong>!
         
     | 
| 
       5 
5 
     | 
    
         
             
                <% else %>
         
     | 
| 
       6 
6 
     | 
    
         
             
                  Hello!
         
     | 
| 
         @@ -1,4 +1,4 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            <% if (user_name = searls_auth_helper.attr_for(@user, @config. 
     | 
| 
      
 1 
     | 
    
         
            +
            <% if (user_name = searls_auth_helper.attr_for(@user, @config.user_name_method)).present? %>
         
     | 
| 
       2 
2 
     | 
    
         
             
            Hi, <%= user_name %>!
         
     | 
| 
       3 
3 
     | 
    
         
             
            <% else %>
         
     | 
| 
       4 
4 
     | 
    
         
             
            Hello!
         
     | 
| 
         @@ -6,7 +6,7 @@ Hello! 
     | 
|
| 
       6 
6 
     | 
    
         | 
| 
       7 
7 
     | 
    
         
             
            You can log in to your <%= searls_auth_helper.rpad(@config.app_name) %>account at this URL:
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
       9 
     | 
    
         
            -
            <%= verify_token_url({
         
     | 
| 
      
 9 
     | 
    
         
            +
            <%= searls_auth.verify_token_url({
         
     | 
| 
       10 
10 
     | 
    
         
             
              token: @token,
         
     | 
| 
       11 
11 
     | 
    
         
             
              redirect_path: @redirect_path,
         
     | 
| 
       12 
12 
     | 
    
         
             
              redirect_subdomain: @redirect_subdomain
         
     | 
| 
         @@ -1,15 +1,9 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            <h1>Log In</h1>
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
             
     | 
| 
       4 
     | 
    
         
            -
              <%= flash[:error] %>
         
     | 
| 
       5 
     | 
    
         
            -
            </div>
         
     | 
| 
       6 
     | 
    
         
            -
            <div style="background: rgba(0,0,255, 0.1)">
         
     | 
| 
       7 
     | 
    
         
            -
              <%= flash[:notice] %>
         
     | 
| 
       8 
     | 
    
         
            -
            </div>
         
     | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
       10 
     | 
    
         
            -
            <%= form_with url: login_path, method: :post, data: {controller: searls_auth_helper.login_stimulus_controller} do |f| %>
         
     | 
| 
      
 3 
     | 
    
         
            +
            <%= form_with url: searls_auth.login_path, method: :post, data: {controller: searls_auth_helper.login_stimulus_controller} do |f| %>
         
     | 
| 
       11 
4 
     | 
    
         
             
              <%= f.hidden_field :redirect_path, value: params[:redirect_path] %>
         
     | 
| 
       12 
5 
     | 
    
         
             
              <%= f.hidden_field :redirect_subdomain, value: params[:redirect_subdomain] %>
         
     | 
| 
      
 6 
     | 
    
         
            +
              <%= f.label :email %>
         
     | 
| 
       13 
7 
     | 
    
         
             
              <%= f.email_field :email, required: true, value: params[:email], data: searls_auth_helper.email_field_stimulus_data %>
         
     | 
| 
       14 
8 
     | 
    
         
             
              <%= f.submit "Log in" %>
         
     | 
| 
       15 
9 
     | 
    
         
             
            <% end %>
         
     | 
| 
         @@ -1,15 +1,9 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            <h1>Create your account</h1>
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
             
     | 
| 
       4 
     | 
    
         
            -
              <%= flash[:error] %>
         
     | 
| 
       5 
     | 
    
         
            -
            </div>
         
     | 
| 
       6 
     | 
    
         
            -
            <div style="background: rgba(0,0,255, 0.1)">
         
     | 
| 
       7 
     | 
    
         
            -
              <%= flash[:notice] %>
         
     | 
| 
       8 
     | 
    
         
            -
            </div>
         
     | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
       10 
     | 
    
         
            -
            <%= form_with url: register_path, data: {controller: searls_auth_helper.login_stimulus_controller} do |f| %>
         
     | 
| 
      
 3 
     | 
    
         
            +
            <%= form_with url: searls_auth.register_path, data: {controller: searls_auth_helper.login_stimulus_controller} do |f| %>
         
     | 
| 
       11 
4 
     | 
    
         
             
              <%= f.hidden_field :redirect_path, value: params[:redirect_path] %>
         
     | 
| 
       12 
5 
     | 
    
         
             
              <%= f.hidden_field :redirect_subdomain, value: params[:redirect_subdomain] %>
         
     | 
| 
      
 6 
     | 
    
         
            +
              <%= f.label :email %>
         
     | 
| 
       13 
7 
     | 
    
         
             
              <%= f.email_field :email, value: params[:email], required: true, data: searls_auth_helper.email_field_stimulus_data %>
         
     | 
| 
       14 
8 
     | 
    
         
             
              <%= f.submit "Register" %>
         
     | 
| 
       15 
9 
     | 
    
         
             
            <% end %>
         
     | 
| 
         @@ -3,13 +3,14 @@ 
     | 
|
| 
       3 
3 
     | 
    
         
             
              In the next few moments, you should receive an email that will provide you
         
     | 
| 
       4 
4 
     | 
    
         
             
              two ways to log in: a link and a six-digit code that you can enter below.
         
     | 
| 
       5 
5 
     | 
    
         
             
            </p>
         
     | 
| 
       6 
     | 
    
         
            -
            <%= form_with(url: verify_path, method: :post, data: {
         
     | 
| 
      
 6 
     | 
    
         
            +
            <%= form_with(url: searls_auth.verify_path, method: :post, data: {
         
     | 
| 
       7 
7 
     | 
    
         
             
                # Don't use turbo on cross-domain redirects
         
     | 
| 
       8 
8 
     | 
    
         
             
                turbo: searls_auth_helper.enable_turbo?
         
     | 
| 
       9 
9 
     | 
    
         
             
              }) do |f| %>
         
     | 
| 
       10 
10 
     | 
    
         
             
              <%= f.hidden_field :redirect_path, value: params[:redirect_path] %>
         
     | 
| 
       11 
11 
     | 
    
         
             
              <%= f.hidden_field :redirect_subdomain, value: params[:redirect_subdomain] %>
         
     | 
| 
       12 
12 
     | 
    
         
             
              <div data-controller="<%= searls_auth_helper.otp_stimulus_controller %>">
         
     | 
| 
      
 13 
     | 
    
         
            +
                <%= f.label :short_code, "Code" %>
         
     | 
| 
       13 
14 
     | 
    
         
             
                <%= f.text_field :short_code,
         
     | 
| 
       14 
15 
     | 
    
         
             
                  maxlength: 6,
         
     | 
| 
       15 
16 
     | 
    
         
             
                  inputmode: "numeric",
         
     | 
| 
         @@ -1,15 +1,17 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module Searls
         
     | 
| 
       2 
2 
     | 
    
         
             
              module Auth
         
     | 
| 
       3 
3 
     | 
    
         
             
                class AuthenticatesUser
         
     | 
| 
       4 
     | 
    
         
            -
                  Result = Struct.new(:success?, :user, keyword_init: true)
         
     | 
| 
      
 4 
     | 
    
         
            +
                  Result = Struct.new(:success?, :user, :exceeded_short_code_attempt_limit?, keyword_init: true)
         
     | 
| 
       5 
5 
     | 
    
         | 
| 
       6 
6 
     | 
    
         
             
                  def authenticate_by_short_code(short_code, session)
         
     | 
| 
       7 
     | 
    
         
            -
                     
     | 
| 
      
 7 
     | 
    
         
            +
                    if session[:searls_auth_short_code_verification_attempts] > Searls::Auth.config.max_allowed_short_code_attempts
         
     | 
| 
      
 8 
     | 
    
         
            +
                      return Result.new(success?: false, exceeded_short_code_attempt_limit?: true)
         
     | 
| 
      
 9 
     | 
    
         
            +
                    end
         
     | 
| 
       8 
10 
     | 
    
         | 
| 
       9 
     | 
    
         
            -
                    if session[: 
     | 
| 
       10 
     | 
    
         
            -
                        Time.zone.parse(session[: 
     | 
| 
       11 
     | 
    
         
            -
                         
     | 
| 
       12 
     | 
    
         
            -
                         
     | 
| 
      
 11 
     | 
    
         
            +
                    if session[:searls_auth_short_code_generated_at].present? &&
         
     | 
| 
      
 12 
     | 
    
         
            +
                        Time.zone.parse(session[:searls_auth_short_code_generated_at]) > Searls::Auth.config.token_expiry_minutes.minutes.ago &&
         
     | 
| 
      
 13 
     | 
    
         
            +
                        short_code == session[:searls_auth_short_code] &&
         
     | 
| 
      
 14 
     | 
    
         
            +
                        (user = Searls::Auth.config.user_finder_by_id.call(session[:searls_auth_short_code_user_id])).present?
         
     | 
| 
       13 
15 
     | 
    
         
             
                      Searls::Auth.config.after_login_success&.call(user)
         
     | 
| 
       14 
16 
     | 
    
         
             
                      Result.new(success?: true, user: user)
         
     | 
| 
       15 
17 
     | 
    
         
             
                    else
         
     | 
    
        data/lib/searls/auth/config.rb
    CHANGED
    
    | 
         @@ -6,11 +6,12 @@ module Searls 
     | 
|
| 
       6 
6 
     | 
    
         
             
                  :user_finder_by_id, # proc (id)
         
     | 
| 
       7 
7 
     | 
    
         
             
                  :user_finder_by_token, # proc (token)
         
     | 
| 
       8 
8 
     | 
    
         
             
                  :user_initializer, # proc (params)
         
     | 
| 
       9 
     | 
    
         
            -
                  : 
     | 
| 
      
 9 
     | 
    
         
            +
                  :user_name_method, # string
         
     | 
| 
       10 
10 
     | 
    
         
             
                  :token_generator, # proc ()
         
     | 
| 
       11 
11 
     | 
    
         
             
                  :token_expiry_minutes, # integer
         
     | 
| 
       12 
12 
     | 
    
         
             
                  # Controller setup
         
     | 
| 
       13 
13 
     | 
    
         
             
                  :preserve_session_keys_after_logout, # array of symbols
         
     | 
| 
      
 14 
     | 
    
         
            +
                  :max_allowed_short_code_attempts, # integer
         
     | 
| 
       14 
15 
     | 
    
         
             
                  # View setup
         
     | 
| 
       15 
16 
     | 
    
         
             
                  :layout, # string
         
     | 
| 
       16 
17 
     | 
    
         
             
                  :login_view, # string
         
     | 
| 
         @@ -32,6 +33,16 @@ module Searls 
     | 
|
| 
       32 
33 
     | 
    
         
             
                  :email_banner_image_path, # string
         
     | 
| 
       33 
34 
     | 
    
         
             
                  :email_background_color, # string
         
     | 
| 
       34 
35 
     | 
    
         
             
                  :email_button_color, # string
         
     | 
| 
      
 36 
     | 
    
         
            +
                  # Messages setup
         
     | 
| 
      
 37 
     | 
    
         
            +
                  :flash_notice_after_registration, # string or proc(user, params)
         
     | 
| 
      
 38 
     | 
    
         
            +
                  :flash_error_after_register_attempt, # string or proc(error_messages, login_path, params)
         
     | 
| 
      
 39 
     | 
    
         
            +
                  :flash_notice_after_login_attempt, # string or proc(user, params)
         
     | 
| 
      
 40 
     | 
    
         
            +
                  :flash_error_after_login_attempt_unknown_email, # string or proc(register_path, params)
         
     | 
| 
      
 41 
     | 
    
         
            +
                  :flash_notice_after_logout, # string or proc(params)
         
     | 
| 
      
 42 
     | 
    
         
            +
                  :flash_notice_after_verification, # string or proc(user, params)
         
     | 
| 
      
 43 
     | 
    
         
            +
                  :flash_error_after_verify_attempt_exceeds_limit, # string or proc(params)
         
     | 
| 
      
 44 
     | 
    
         
            +
                  :flash_error_after_verify_attempt_incorrect_short_code, # string or proc(params)
         
     | 
| 
      
 45 
     | 
    
         
            +
                  :flash_error_after_verify_attempt_invalid_link, # string or proc(params)
         
     | 
| 
       35 
46 
     | 
    
         
             
                  keyword_init: true
         
     | 
| 
       36 
47 
     | 
    
         
             
                ) do
         
     | 
| 
       37 
48 
     | 
    
         
             
                  # Get values from values that might be procs
         
     | 
    
        data/lib/searls/auth/version.rb
    CHANGED
    
    
    
        data/lib/searls/auth.rb
    CHANGED
    
    | 
         @@ -16,12 +16,13 @@ module Searls 
     | 
|
| 
       16 
16 
     | 
    
         
             
                  user_finder_by_email: ->(email) { User.find_by(email:) },
         
     | 
| 
       17 
17 
     | 
    
         
             
                  user_finder_by_id: ->(id) { User.find_by(id:) },
         
     | 
| 
       18 
18 
     | 
    
         
             
                  user_finder_by_token: ->(token) { User.find_by_token_for(:email_auth, token) },
         
     | 
| 
       19 
     | 
    
         
            -
                  user_initializer: ->(params) { User.new(params[:email]) },
         
     | 
| 
       20 
     | 
    
         
            -
                   
     | 
| 
      
 19 
     | 
    
         
            +
                  user_initializer: ->(params) { User.new(email: params[:email]) },
         
     | 
| 
      
 20 
     | 
    
         
            +
                  user_name_method: "name",
         
     | 
| 
       21 
21 
     | 
    
         
             
                  token_generator: ->(user) { user.generate_token_for(:email_auth) },
         
     | 
| 
       22 
22 
     | 
    
         
             
                  token_expiry_minutes: 30,
         
     | 
| 
       23 
23 
     | 
    
         
             
                  # Controller setup
         
     | 
| 
       24 
24 
     | 
    
         
             
                  preserve_session_keys_after_logout: [],
         
     | 
| 
      
 25 
     | 
    
         
            +
                  max_allowed_short_code_attempts: 10,
         
     | 
| 
       25 
26 
     | 
    
         
             
                  # View setup
         
     | 
| 
       26 
27 
     | 
    
         
             
                  layout: "application",
         
     | 
| 
       27 
28 
     | 
    
         
             
                  register_view: "searls/auth/registrations/show",
         
     | 
| 
         @@ -48,7 +49,20 @@ module Searls 
     | 
|
| 
       48 
49 
     | 
    
         
             
                  support_email_address: nil,
         
     | 
| 
       49 
50 
     | 
    
         
             
                  email_background_color: "#d8d7ed",
         
     | 
| 
       50 
51 
     | 
    
         
             
                  email_button_color: "#c664f3",
         
     | 
| 
       51 
     | 
    
         
            -
                  email_banner_image_path: nil
         
     | 
| 
      
 52 
     | 
    
         
            +
                  email_banner_image_path: nil,
         
     | 
| 
      
 53 
     | 
    
         
            +
                  # Messages setup
         
     | 
| 
      
 54 
     | 
    
         
            +
                  flash_notice_after_registration: ->(user, params) { "Verification email sent to #{params[:email]}" },
         
     | 
| 
      
 55 
     | 
    
         
            +
                  flash_error_after_register_attempt: ->(error_messages, login_path, params) { error_messages },
         
     | 
| 
      
 56 
     | 
    
         
            +
                  flash_notice_after_login_attempt: ->(user, params) { "Login details sent to #{params[:email]}" },
         
     | 
| 
      
 57 
     | 
    
         
            +
                  flash_error_after_login_attempt_unknown_email: ->(register_path, params) {
         
     | 
| 
      
 58 
     | 
    
         
            +
                    "We don't know that email. <a href=\"#{register_path}\">Sign up</a> instead?".html_safe
         
     | 
| 
      
 59 
     | 
    
         
            +
                  },
         
     | 
| 
      
 60 
     | 
    
         
            +
                  flash_notice_after_logout: "You've been logged out",
         
     | 
| 
      
 61 
     | 
    
         
            +
                  flash_notice_after_verification: "You are now logged in",
         
     | 
| 
      
 62 
     | 
    
         
            +
                  flash_error_after_verify_attempt_exceeds_limit: "Too many verification attempts. Please login again to generate a new code",
         
     | 
| 
      
 63 
     | 
    
         
            +
                  flash_error_after_verify_attempt_incorrect_short_code: "We weren't able to log you in with that code. Try again?",
         
     | 
| 
      
 64 
     | 
    
         
            +
                  flash_error_after_verify_attempt_invalid_link: "We weren't able to log you in with that link. Try again?"
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
       52 
66 
     | 
    
         
             
                }.freeze
         
     | 
| 
       53 
67 
     | 
    
         | 
| 
       54 
68 
     | 
    
         
             
                CONFIG = Config.new(**DEFAULT_CONFIG)
         
     | 
    
        data/script/setup
    ADDED
    
    | 
         @@ -0,0 +1,14 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #!/usr/bin/env bash
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            set -e
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            bundle
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            cd example/simple_app
         
     | 
| 
      
 8 
     | 
    
         
            +
            bundle
         
     | 
| 
      
 9 
     | 
    
         
            +
            bin/rake db:setup
         
     | 
| 
      
 10 
     | 
    
         
            +
            export PLAYWRIGHT_CLI_VERSION=$(bundle exec ruby -e 'require "playwright"; puts Playwright::COMPATIBLE_PLAYWRIGHT_VERSION.strip')
         
     | 
| 
      
 11 
     | 
    
         
            +
            PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 yarn add -D "playwright@$PLAYWRIGHT_CLI_VERSION"
         
     | 
| 
      
 12 
     | 
    
         
            +
            yarn run playwright install chromium
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            cd ../..
         
     | 
    
        data/script/setup_ci
    ADDED
    
    
    
        data/script/test
    ADDED
    
    
    
        metadata
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: searls-auth
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 0.0 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.1.0
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Justin Searls
         
     | 
| 
         @@ -60,6 +60,9 @@ files: 
     | 
|
| 
       60 
60 
     | 
    
         
             
            - lib/searls/auth/railtie.rb
         
     | 
| 
       61 
61 
     | 
    
         
             
            - lib/searls/auth/resets_session.rb
         
     | 
| 
       62 
62 
     | 
    
         
             
            - lib/searls/auth/version.rb
         
     | 
| 
      
 63 
     | 
    
         
            +
            - script/setup
         
     | 
| 
      
 64 
     | 
    
         
            +
            - script/setup_ci
         
     | 
| 
      
 65 
     | 
    
         
            +
            - script/test
         
     | 
| 
       63 
66 
     | 
    
         
             
            homepage: https://github.com/searls/searls-auth
         
     | 
| 
       64 
67 
     | 
    
         
             
            licenses:
         
     | 
| 
       65 
68 
     | 
    
         
             
            - GPL-3.0-only
         
     |