no_password_auth 0.2.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.
- checksums.yaml +7 -0
 - data/MIT-LICENSE +20 -0
 - data/README-ES.md +230 -0
 - data/README.md +230 -0
 - data/Rakefile +18 -0
 - data/app/assets/config/no_password/manifest.js +4 -0
 - data/app/assets/config/no_password/tailwind.config.js +61 -0
 - data/app/assets/images/no_password/aoo.svg +1 -0
 - data/app/assets/javascripts/no_password/application.js +4 -0
 - data/app/assets/javascripts/no_password/controllers/alert_controller.js +31 -0
 - data/app/assets/javascripts/no_password/controllers/application.js +10 -0
 - data/app/assets/javascripts/no_password/controllers/index.js +11 -0
 - data/app/assets/stylesheets/no_password/application.css +15 -0
 - data/app/assets/stylesheets/no_password/application.tailwind.css +34 -0
 - data/app/controllers/concerns/no_password/controller_helpers.rb +45 -0
 - data/app/controllers/concerns/no_password/web_tokens.rb +33 -0
 - data/app/controllers/no_password/application_controller.rb +4 -0
 - data/app/controllers/no_password/session_confirmations_controller.rb +46 -0
 - data/app/controllers/no_password/sessions_controller.rb +49 -0
 - data/app/helpers/no_password/application_helper.rb +4 -0
 - data/app/helpers/no_password/no_password_helper.rb +13 -0
 - data/app/jobs/no_password/application_job.rb +4 -0
 - data/app/mailers/no_password/application_mailer.rb +6 -0
 - data/app/mailers/no_password/sessions_mailer.rb +16 -0
 - data/app/models/no_password/application_record.rb +5 -0
 - data/app/models/no_password/session.rb +20 -0
 - data/app/use_cases/no_password/session_manager.rb +51 -0
 - data/app/views/layouts/no_password/application.html.erb +26 -0
 - data/app/views/layouts/no_password/mailer.html.erb +106 -0
 - data/app/views/layouts/no_password/mailer.text.erb +6 -0
 - data/app/views/no_password/application/_notification.html.erb +55 -0
 - data/app/views/no_password/session_confirmations/_form.html.erb +19 -0
 - data/app/views/no_password/session_confirmations/edit.html.erb +15 -0
 - data/app/views/no_password/session_confirmations/update.turbo_stream.erb +3 -0
 - data/app/views/no_password/sessions/_form.html.erb +16 -0
 - data/app/views/no_password/sessions/create.turbo_stream.erb +3 -0
 - data/app/views/no_password/sessions/new.html.erb +15 -0
 - data/app/views/no_password/sessions_mailer/send_token.html.erb +14 -0
 - data/app/views/no_password/sessions_mailer/send_token.text.erb +12 -0
 - data/config/initializers/importmap.rb +17 -0
 - data/config/locales/en/flash.en.yml +11 -0
 - data/config/locales/en/forms.en.yml +9 -0
 - data/config/locales/en/mailers.en.yml +21 -0
 - data/config/locales/en/views.en.yml +12 -0
 - data/config/locales/es/flash.es.yml +11 -0
 - data/config/locales/es/forms.es.yml +9 -0
 - data/config/locales/es/mailers.es.yml +18 -0
 - data/config/locales/es/views.es.yml +13 -0
 - data/config/locales/models.en.yml +8 -0
 - data/config/locales/models.es.yml +8 -0
 - data/config/locales/models.yml +8 -0
 - data/config/routes.rb +7 -0
 - data/db/migrate/20211202211706_create_no_password_sessions.rb +16 -0
 - data/docs/aoorora-demo.gif +0 -0
 - data/docs/dummy-app.png +0 -0
 - data/lib/generators/no_password/install_generator.rb +30 -0
 - data/lib/generators/no_password/install_templates_generator.rb +27 -0
 - data/lib/generators/no_password/tailwind_config_generator.rb +9 -0
 - data/lib/generators/no_password/templates/app/assets/config/no_password/tailwind.config.js.tt +57 -0
 - data/lib/generators/no_password/templates/config/initializers/no_password.rb +10 -0
 - data/lib/no_password/engine.rb +11 -0
 - data/lib/no_password/railtie.rb +7 -0
 - data/lib/no_password/version.rb +3 -0
 - data/lib/no_password.rb +42 -0
 - data/lib/tasks/install.rake +17 -0
 - data/lib/tasks/no_password_tasks.rake +4 -0
 - data/lib/tasks/tailwind.rake +23 -0
 - metadata +227 -0
 
| 
         @@ -0,0 +1,34 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            @tailwind base;
         
     | 
| 
      
 2 
     | 
    
         
            +
            @tailwind components;
         
     | 
| 
      
 3 
     | 
    
         
            +
            @tailwind utilities;
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            @layer base {
         
     | 
| 
      
 6 
     | 
    
         
            +
              :root {
         
     | 
| 
      
 7 
     | 
    
         
            +
                --color-base: 31, 27, 54;
         
     | 
| 
      
 8 
     | 
    
         
            +
                --color-accented: 80, 70, 137;
         
     | 
| 
      
 9 
     | 
    
         
            +
                --color-inverted: 255, 255, 255;
         
     | 
| 
      
 10 
     | 
    
         
            +
                --color-accented-hover: 60, 53, 103;
         
     | 
| 
      
 11 
     | 
    
         
            +
                --color-muted: 55, 65, 81;
         
     | 
| 
      
 12 
     | 
    
         
            +
                --color-dimmed: 75, 85, 99;
         
     | 
| 
      
 13 
     | 
    
         
            +
                --color-error: 220, 38, 38;
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                --color-border-base: 209, 213, 219;
         
     | 
| 
      
 16 
     | 
    
         
            +
                --color-border-accented: 80, 70, 137;
         
     | 
| 
      
 17 
     | 
    
         
            +
              }
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
              .input {
         
     | 
| 
      
 20 
     | 
    
         
            +
                @apply border-skin-base rounded-md shadow-sm placeholder-gray-400 focus:outline-none focus:ring-skin-accented focus:border-skin-accented sm:text-sm;
         
     | 
| 
      
 21 
     | 
    
         
            +
              }
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
              .check {
         
     | 
| 
      
 24 
     | 
    
         
            +
                @apply border-skin-base h-4 w-4 text-skin-accented focus:ring-skin-accented rounded;
         
     | 
| 
      
 25 
     | 
    
         
            +
              }
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
              .button {
         
     | 
| 
      
 28 
     | 
    
         
            +
                @apply inline-flex justify-center py-2 px-4 border border-skin-base rounded-md shadow-sm text-sm font-medium text-skin-muted bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-skin-accented;
         
     | 
| 
      
 29 
     | 
    
         
            +
              }
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
              .button-accented {
         
     | 
| 
      
 32 
     | 
    
         
            +
                @apply border-transparent bg-skin-button-accented hover:bg-skin-button-accented-hover text-skin-inverted;
         
     | 
| 
      
 33 
     | 
    
         
            +
              }
         
     | 
| 
      
 34 
     | 
    
         
            +
            }
         
     | 
| 
         @@ -0,0 +1,45 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module NoPassword
         
     | 
| 
      
 4 
     | 
    
         
            +
              module ControllerHelpers
         
     | 
| 
      
 5 
     | 
    
         
            +
                extend ActiveSupport::Concern
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                included do
         
     | 
| 
      
 8 
     | 
    
         
            +
                  def current_session
         
     | 
| 
      
 9 
     | 
    
         
            +
                    @session ||= begin
         
     | 
| 
      
 10 
     | 
    
         
            +
                      session_id = session[session_key]
         
     | 
| 
      
 11 
     | 
    
         
            +
                      return nil if session_id.blank?
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                      current_session = find_session(session_id)
         
     | 
| 
      
 14 
     | 
    
         
            +
                      session[session_key] = nil if current_session.blank?
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                      current_session
         
     | 
| 
      
 17 
     | 
    
         
            +
                    end
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  def signed_in_session?
         
     | 
| 
      
 21 
     | 
    
         
            +
                    current_session.present?
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  def authenticate_session!
         
     | 
| 
      
 25 
     | 
    
         
            +
                    redirect_to no_password.new_session_path(return_to: CGI.escape(request.fullpath)), alert: t("flash.update.session.alert") unless signed_in_session?
         
     | 
| 
      
 26 
     | 
    
         
            +
                  end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  helper_method :current_session, :signed_in_session?
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  protected
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                  def session_key(value = "id")
         
     | 
| 
      
 33 
     | 
    
         
            +
                    "—-no_password_session_#{value}"
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                  private
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                  def find_session(session_id)
         
     | 
| 
      
 39 
     | 
    
         
            +
                    NoPassword::Session.where.not(claimed_at: nil)
         
     | 
| 
      
 40 
     | 
    
         
            +
                      .where("expires_at > ?", Time.zone.now)
         
     | 
| 
      
 41 
     | 
    
         
            +
                      .find_by_id(session_id)
         
     | 
| 
      
 42 
     | 
    
         
            +
                  end
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
              end
         
     | 
| 
      
 45 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,33 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require "active_support/concern"
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module NoPassword
         
     | 
| 
      
 6 
     | 
    
         
            +
              module WebTokens
         
     | 
| 
      
 7 
     | 
    
         
            +
                extend ActiveSupport::Concern
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                included do
         
     | 
| 
      
 10 
     | 
    
         
            +
                  def sign_token(data)
         
     | 
| 
      
 11 
     | 
    
         
            +
                    secret_key = NoPassword.configuration.secret_key || Rails.application.secret_key_base
         
     | 
| 
      
 12 
     | 
    
         
            +
                    verifier = ActiveSupport::MessageVerifier.new(secret_key)
         
     | 
| 
      
 13 
     | 
    
         
            +
                    verifier.generate(data, expires_in: NoPassword.configuration.session_expiration, purpose: :no_password_login)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  def verify_token(data)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    secret_key = NoPassword.configuration.secret_key || Rails.application.secret_key_base
         
     | 
| 
      
 18 
     | 
    
         
            +
                    verifier = ActiveSupport::MessageVerifier.new(secret_key)
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                    token = token_from_url(data)
         
     | 
| 
      
 21 
     | 
    
         
            +
                    verifier.verified(token, purpose: :no_password_login)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  def token_to_url(token)
         
     | 
| 
      
 25 
     | 
    
         
            +
                    CGI.escape(token)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  def token_from_url(token)
         
     | 
| 
      
 29 
     | 
    
         
            +
                    CGI.unescape(token)
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
              end
         
     | 
| 
      
 33 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,46 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module NoPassword
         
     | 
| 
      
 4 
     | 
    
         
            +
              class SessionConfirmationsController < ApplicationController
         
     | 
| 
      
 5 
     | 
    
         
            +
                include NoPassword::ControllerHelpers
         
     | 
| 
      
 6 
     | 
    
         
            +
                include NoPassword::WebTokens
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                def edit
         
     | 
| 
      
 9 
     | 
    
         
            +
                  if params[:token].present?
         
     | 
| 
      
 10 
     | 
    
         
            +
                    token = verify_token(params[:token])
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                    sign_in_session(token, true)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  end
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                def update
         
     | 
| 
      
 17 
     | 
    
         
            +
                  result = sign_in_session(params[:token])
         
     | 
| 
      
 18 
     | 
    
         
            +
                  return result if result.present?
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  response.status = :unprocessable_entity
         
     | 
| 
      
 21 
     | 
    
         
            +
                  render turbo_stream: turbo_stream.update("notifications", partial: "notification")
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                private
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                def sign_in_session(token, by_url = false)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  current_session = SessionManager.new.claim(token)
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  flash.now.alert = t("flash.update.invalid_code.alert") if current_session.blank?
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                  result = if respond_to?(:after_sign_in!)
         
     | 
| 
      
 32 
     | 
    
         
            +
                    after_sign_in!(current_session.present?, by_url, current_session&.return_url)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  elsif current_session.present?
         
     | 
| 
      
 34 
     | 
    
         
            +
                    save_session_to_cookie(current_session)
         
     | 
| 
      
 35 
     | 
    
         
            +
                    redirect_to(current_session.return_url || main_app.root_path)
         
     | 
| 
      
 36 
     | 
    
         
            +
                  end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                  result if result.present?
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                def save_session_to_cookie(current_session, key = nil, data = nil)
         
     | 
| 
      
 42 
     | 
    
         
            +
                  session[session_key] = current_session.id
         
     | 
| 
      
 43 
     | 
    
         
            +
                  session[session_key(key)] = data if key.present? && data.present?
         
     | 
| 
      
 44 
     | 
    
         
            +
                end
         
     | 
| 
      
 45 
     | 
    
         
            +
              end
         
     | 
| 
      
 46 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,49 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module NoPassword
         
     | 
| 
      
 4 
     | 
    
         
            +
              class SessionsController < ApplicationController
         
     | 
| 
      
 5 
     | 
    
         
            +
                include NoPassword::ControllerHelpers
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                def new
         
     | 
| 
      
 8 
     | 
    
         
            +
                  @return_to = params[:return_to].to_s
         
     | 
| 
      
 9 
     | 
    
         
            +
                  @resource = Session.new
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                def create
         
     | 
| 
      
 13 
     | 
    
         
            +
                  return_to = params[:return_to].to_s
         
     | 
| 
      
 14 
     | 
    
         
            +
                  current_session = SessionManager.new.create(request.user_agent, params.dig(:session, :email), request.remote_ip, referrer_path(return_to))
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  if current_session.present?
         
     | 
| 
      
 17 
     | 
    
         
            +
                    SessionsMailer.with(session: current_session).send_token.deliver_later
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                    after_session_request if respond_to?(:after_session_request)
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                    respond_to do |format|
         
     | 
| 
      
 22 
     | 
    
         
            +
                      format.html { redirect_to no_password.edit_session_confirmations_path }
         
     | 
| 
      
 23 
     | 
    
         
            +
                      format.turbo_stream
         
     | 
| 
      
 24 
     | 
    
         
            +
                    end
         
     | 
| 
      
 25 
     | 
    
         
            +
                  end
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                def destroy
         
     | 
| 
      
 29 
     | 
    
         
            +
                  sign_out
         
     | 
| 
      
 30 
     | 
    
         
            +
                  redirect_to main_app.root_path
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                private
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                def referrer_path(return_to)
         
     | 
| 
      
 36 
     | 
    
         
            +
                  referrer = CGI.unescape(return_to)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  return nil if referrer.blank?
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                  referrer.include?(no_password.new_session_path) || referrer.include?(no_password.edit_session_confirmations_path) ? nil : referrer
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                def sign_out(key = nil)
         
     | 
| 
      
 43 
     | 
    
         
            +
                  session.delete(session_key)
         
     | 
| 
      
 44 
     | 
    
         
            +
                  session.delete(session_key(key)) if key.present?
         
     | 
| 
      
 45 
     | 
    
         
            +
                  @session = nil
         
     | 
| 
      
 46 
     | 
    
         
            +
                  true
         
     | 
| 
      
 47 
     | 
    
         
            +
                end
         
     | 
| 
      
 48 
     | 
    
         
            +
              end
         
     | 
| 
      
 49 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,13 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module NoPassword
         
     | 
| 
      
 2 
     | 
    
         
            +
              module NoPasswordHelper
         
     | 
| 
      
 3 
     | 
    
         
            +
                def no_password_importmap_tags(entry_point = "application", shim: true)
         
     | 
| 
      
 4 
     | 
    
         
            +
                  safe_join [
         
     | 
| 
      
 5 
     | 
    
         
            +
                    javascript_inline_importmap_tag(NoPassword.configuration.importmap.to_json(resolver: self)),
         
     | 
| 
      
 6 
     | 
    
         
            +
                    javascript_importmap_module_preload_tags(NoPassword.configuration.importmap),
         
     | 
| 
      
 7 
     | 
    
         
            +
                    (javascript_importmap_shim_nonce_configuration_tag if shim),
         
     | 
| 
      
 8 
     | 
    
         
            +
                    (javascript_importmap_shim_tag if shim),
         
     | 
| 
      
 9 
     | 
    
         
            +
                    javascript_import_module_tag(entry_point)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  ].compact, "\n"
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
              end
         
     | 
| 
      
 13 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,16 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module NoPassword
         
     | 
| 
      
 4 
     | 
    
         
            +
              class SessionsMailer < ApplicationMailer
         
     | 
| 
      
 5 
     | 
    
         
            +
                include NoPassword::WebTokens
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                def send_token
         
     | 
| 
      
 8 
     | 
    
         
            +
                  @session = params[:session]
         
     | 
| 
      
 9 
     | 
    
         
            +
                  @title = t("mailers.send_token.subject")
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @friendly_token = @session.token
         
     | 
| 
      
 11 
     | 
    
         
            +
                  @signed_token = token_to_url(sign_token(@friendly_token))
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                  mail(to: @session.email, subject: t("mailers.send_token.subject"))
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
              end
         
     | 
| 
      
 16 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,20 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module NoPassword
         
     | 
| 
      
 4 
     | 
    
         
            +
              class Session < ApplicationRecord
         
     | 
| 
      
 5 
     | 
    
         
            +
                validates :token, :user_agent, :remote_addr, :email, presence: true
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                def claimed?
         
     | 
| 
      
 8 
     | 
    
         
            +
                  claimed_at.present?
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                def expired?
         
     | 
| 
      
 12 
     | 
    
         
            +
                  current_time = Time.zone.now
         
     | 
| 
      
 13 
     | 
    
         
            +
                  expires_at <= current_time
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                def invalid?
         
     | 
| 
      
 17 
     | 
    
         
            +
                  claimed? || expired?
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
              end
         
     | 
| 
      
 20 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,51 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module NoPassword
         
     | 
| 
      
 4 
     | 
    
         
            +
              class SessionManager
         
     | 
| 
      
 5 
     | 
    
         
            +
                def create(user_agent, email, remote_addr, return_url = nil)
         
     | 
| 
      
 6 
     | 
    
         
            +
                  expire_unclaimed_session(email)
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                  Session.create(
         
     | 
| 
      
 9 
     | 
    
         
            +
                    user_agent: user_agent,
         
     | 
| 
      
 10 
     | 
    
         
            +
                    email: email,
         
     | 
| 
      
 11 
     | 
    
         
            +
                    expires_at: NoPassword.configuration.session_expiration.from_now,
         
     | 
| 
      
 12 
     | 
    
         
            +
                    token: generate_friendly_token,
         
     | 
| 
      
 13 
     | 
    
         
            +
                    remote_addr: remote_addr,
         
     | 
| 
      
 14 
     | 
    
         
            +
                    return_url: return_url
         
     | 
| 
      
 15 
     | 
    
         
            +
                  )
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                def claim(token)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  current_time = Time.zone.now
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  session = Session
         
     | 
| 
      
 22 
     | 
    
         
            +
                    .where(token: token, claimed_at: nil)
         
     | 
| 
      
 23 
     | 
    
         
            +
                    .where("expires_at > ?", current_time)
         
     | 
| 
      
 24 
     | 
    
         
            +
                    .first
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                  if session.present?
         
     | 
| 
      
 27 
     | 
    
         
            +
                    session.claimed_at = current_time
         
     | 
| 
      
 28 
     | 
    
         
            +
                    session.save
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                    return session
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                  nil
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                private
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                def expire_unclaimed_session(email)
         
     | 
| 
      
 39 
     | 
    
         
            +
                  current_time = Time.zone.now
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                  Session
         
     | 
| 
      
 42 
     | 
    
         
            +
                    .where(email: email, claimed_at: nil)
         
     | 
| 
      
 43 
     | 
    
         
            +
                    .where("expires_at > ?", current_time)
         
     | 
| 
      
 44 
     | 
    
         
            +
                    .update_all(expires_at: current_time)
         
     | 
| 
      
 45 
     | 
    
         
            +
                end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                def generate_friendly_token
         
     | 
| 
      
 48 
     | 
    
         
            +
                  "#{SecureRandom.alphanumeric(4)}-#{SecureRandom.random_number(10_000)}-#{SecureRandom.alphanumeric(4)}"
         
     | 
| 
      
 49 
     | 
    
         
            +
                end
         
     | 
| 
      
 50 
     | 
    
         
            +
              end
         
     | 
| 
      
 51 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,26 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            <!DOCTYPE html>
         
     | 
| 
      
 2 
     | 
    
         
            +
            <html>
         
     | 
| 
      
 3 
     | 
    
         
            +
              <head>
         
     | 
| 
      
 4 
     | 
    
         
            +
                <title>No Password</title>
         
     | 
| 
      
 5 
     | 
    
         
            +
                <meta name="viewport" content="width=device-width,initial-scale=1">
         
     | 
| 
      
 6 
     | 
    
         
            +
                <%= csrf_meta_tags %>
         
     | 
| 
      
 7 
     | 
    
         
            +
                <%= csp_meta_tag %>
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                <%= stylesheet_link_tag "no_password/tailwind", "inter-font", "data-turbo-track": "reload" %>
         
     | 
| 
      
 10 
     | 
    
         
            +
                <%= stylesheet_link_tag "no_password/application", media: "all", "data-turbo-track": "reload" %>
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                <%= no_password_importmap_tags %>
         
     | 
| 
      
 13 
     | 
    
         
            +
              </head>
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
              <body>
         
     | 
| 
      
 16 
     | 
    
         
            +
                <div aria-live="assertive" class="fixed inset-0 z-50 flex items-end px-4 py-6 pointer-events-none sm:p-6 sm:items-start">
         
     | 
| 
      
 17 
     | 
    
         
            +
                  <%= turbo_frame_tag "notifications", class: "w-full flex flex-col items-center space-y-4 sm:items-end" do %>
         
     | 
| 
      
 18 
     | 
    
         
            +
                    <%= render partial: "notification" %>
         
     | 
| 
      
 19 
     | 
    
         
            +
                  <% end %>
         
     | 
| 
      
 20 
     | 
    
         
            +
                </div>
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                <section>
         
     | 
| 
      
 23 
     | 
    
         
            +
                  <%= yield %>
         
     | 
| 
      
 24 
     | 
    
         
            +
                </section>
         
     | 
| 
      
 25 
     | 
    
         
            +
              </body>
         
     | 
| 
      
 26 
     | 
    
         
            +
            </html>
         
     | 
| 
         @@ -0,0 +1,106 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            <!DOCTYPE html>
         
     | 
| 
      
 2 
     | 
    
         
            +
            <html lang="es" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
              <head>
         
     | 
| 
      
 5 
     | 
    
         
            +
                <meta charset="utf-8">
         
     | 
| 
      
 6 
     | 
    
         
            +
                <meta name="x-apple-disable-message-reformatting">
         
     | 
| 
      
 7 
     | 
    
         
            +
                <meta http-equiv="x-ua-compatible" content="ie=edge">
         
     | 
| 
      
 8 
     | 
    
         
            +
                <meta name="viewport" content="width=device-width, initial-scale=1">
         
     | 
| 
      
 9 
     | 
    
         
            +
                <meta name="format-detection" content="telephone=no, date=no, address=no, email=no">
         
     | 
| 
      
 10 
     | 
    
         
            +
                <!--[if mso]>
         
     | 
| 
      
 11 
     | 
    
         
            +
                  <xml><o:OfficeDocumentSettings><o:PixelsPerInch>96</o:PixelsPerInch></o:OfficeDocumentSettings></xml>
         
     | 
| 
      
 12 
     | 
    
         
            +
                  <style>
         
     | 
| 
      
 13 
     | 
    
         
            +
                    td,th,div,p,a,h1,h2,h3,h4,h5,h6 {font-family: "Segoe UI", sans-serif; mso-line-height-rule: exactly;}
         
     | 
| 
      
 14 
     | 
    
         
            +
                  </style>
         
     | 
| 
      
 15 
     | 
    
         
            +
                <![endif]-->
         
     | 
| 
      
 16 
     | 
    
         
            +
                <title>
         
     | 
| 
      
 17 
     | 
    
         
            +
                  <%= @title %>
         
     | 
| 
      
 18 
     | 
    
         
            +
                </title>
         
     | 
| 
      
 19 
     | 
    
         
            +
                <style>
         
     | 
| 
      
 20 
     | 
    
         
            +
                  .hover-underline:hover {
         
     | 
| 
      
 21 
     | 
    
         
            +
                    text-decoration: underline !important;
         
     | 
| 
      
 22 
     | 
    
         
            +
                  }
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  @media (max-width: 600px) {
         
     | 
| 
      
 25 
     | 
    
         
            +
                    .sm-h-32 {
         
     | 
| 
      
 26 
     | 
    
         
            +
                      height: 32px !important;
         
     | 
| 
      
 27 
     | 
    
         
            +
                    }
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                    .sm-w-full {
         
     | 
| 
      
 30 
     | 
    
         
            +
                      width: 100% !important;
         
     | 
| 
      
 31 
     | 
    
         
            +
                    }
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                    .sm-px-24 {
         
     | 
| 
      
 34 
     | 
    
         
            +
                      padding-left: 24px !important;
         
     | 
| 
      
 35 
     | 
    
         
            +
                      padding-right: 24px !important;
         
     | 
| 
      
 36 
     | 
    
         
            +
                    }
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                    .sm-pb-12 {
         
     | 
| 
      
 39 
     | 
    
         
            +
                      padding-bottom: 12px !important;
         
     | 
| 
      
 40 
     | 
    
         
            +
                    }
         
     | 
| 
      
 41 
     | 
    
         
            +
                  }
         
     | 
| 
      
 42 
     | 
    
         
            +
                </style>
         
     | 
| 
      
 43 
     | 
    
         
            +
              </head>
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
              <body
         
     | 
| 
      
 46 
     | 
    
         
            +
                style="margin: 0; width: 100%; padding: 0; word-break: break-word; -webkit-font-smoothing: antialiased; height: 100%; background-color: #f3f4f6;">
         
     | 
| 
      
 47 
     | 
    
         
            +
                <div role="article" aria-roledescription="email" aria-label="<%= @title %>" lang="es">
         
     | 
| 
      
 48 
     | 
    
         
            +
                  <table
         
     | 
| 
      
 49 
     | 
    
         
            +
                    style="height: 100%; width: 100%; font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;"
         
     | 
| 
      
 50 
     | 
    
         
            +
                    cellpadding="0" cellspacing="0" role="presentation">
         
     | 
| 
      
 51 
     | 
    
         
            +
                    <tr>
         
     | 
| 
      
 52 
     | 
    
         
            +
                      <td align="center"
         
     | 
| 
      
 53 
     | 
    
         
            +
                        style="background-color: #f3f4f6; padding-top: 24px; padding-bottom: 24px; vertical-align: middle;"
         
     | 
| 
      
 54 
     | 
    
         
            +
                        valign="middle">
         
     | 
| 
      
 55 
     | 
    
         
            +
                        <table class="sm-w-full" style="width: 600px;" cellpadding="0" cellspacing="0" role="presentation">
         
     | 
| 
      
 56 
     | 
    
         
            +
                          <tr>
         
     | 
| 
      
 57 
     | 
    
         
            +
                            <td class="sm-pb-12" style="padding-bottom: 24px; text-align: center;">
         
     | 
| 
      
 58 
     | 
    
         
            +
                              <h1 style="font-size: 24px; font-weight: 600; color: #000000;">
         
     | 
| 
      
 59 
     | 
    
         
            +
                                <%= @title %>
         
     | 
| 
      
 60 
     | 
    
         
            +
                              </h1>
         
     | 
| 
      
 61 
     | 
    
         
            +
                            </td>
         
     | 
| 
      
 62 
     | 
    
         
            +
                          </tr>
         
     | 
| 
      
 63 
     | 
    
         
            +
                          <tr>
         
     | 
| 
      
 64 
     | 
    
         
            +
                            <td align="center" class="sm-px-24">
         
     | 
| 
      
 65 
     | 
    
         
            +
                              <table class="sm-w-full" style="width: 75%;" cellpadding="0" cellspacing="0" role="presentation">
         
     | 
| 
      
 66 
     | 
    
         
            +
                                <tr>
         
     | 
| 
      
 67 
     | 
    
         
            +
                                  <td>
         
     | 
| 
      
 68 
     | 
    
         
            +
                                    <div class="sm-px-24" style="overflow: hidden; border-radius: 6px; background-color: #ffffff; padding: 36px; box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);">
         
     | 
| 
      
 69 
     | 
    
         
            +
                                      <%= image_tag("no_password/aoo.svg", style: "height: 3rem; width: auto; margin-bottom: 2rem;")%>
         
     | 
| 
      
 70 
     | 
    
         
            +
                                      <%= yield %>
         
     | 
| 
      
 71 
     | 
    
         
            +
                                    </div>
         
     | 
| 
      
 72 
     | 
    
         
            +
                                  </td>
         
     | 
| 
      
 73 
     | 
    
         
            +
                                </tr>
         
     | 
| 
      
 74 
     | 
    
         
            +
                                <tr>
         
     | 
| 
      
 75 
     | 
    
         
            +
                                  <td style="padding: 32px; text-align: center; font-size: 12px; color: #4b5563;">
         
     | 
| 
      
 76 
     | 
    
         
            +
                                    <%= link_to "https://aoorora.com" do %>
         
     | 
| 
      
 77 
     | 
    
         
            +
                                      <%= image_tag("no_password/aoo.svg",
         
     | 
| 
      
 78 
     | 
    
         
            +
                                        style: "width: 120px;margin-bottom: 24px;") %>
         
     | 
| 
      
 79 
     | 
    
         
            +
                                    <% end %>
         
     | 
| 
      
 80 
     | 
    
         
            +
                                    <p style="margin: 0 0 4px; text-transform: uppercase;">
         
     | 
| 
      
 81 
     | 
    
         
            +
                                      <%= t(".service") %>
         
     | 
| 
      
 82 
     | 
    
         
            +
                                    </p>
         
     | 
| 
      
 83 
     | 
    
         
            +
                                    <p style="margin: 0; font-style: italic;">
         
     | 
| 
      
 84 
     | 
    
         
            +
                                      <%= t(".platform") %>
         
     | 
| 
      
 85 
     | 
    
         
            +
                                    </p>
         
     | 
| 
      
 86 
     | 
    
         
            +
                                    <p style="cursor: default;">
         
     | 
| 
      
 87 
     | 
    
         
            +
                                      <%= link_to "Aoorora" , "https://aoorora.com" , class: "hover-underline" ,
         
     | 
| 
      
 88 
     | 
    
         
            +
                                        style: "color: #4b5563; text-decoration: none;" %> •
         
     | 
| 
      
 89 
     | 
    
         
            +
                                      <%= link_to "OnBoarding" , "https://aoorora.com" , class: "hover-underline" ,
         
     | 
| 
      
 90 
     | 
    
         
            +
                                        style: "color: #4b5563; text-decoration: none;" %> •
         
     | 
| 
      
 91 
     | 
    
         
            +
                                      <%= link_to "Identidad" , "https://aoorora.com/identidad" , class: "hover-underline" ,
         
     | 
| 
      
 92 
     | 
    
         
            +
                                        style: "color: #4b5563; text-decoration: none;" %>
         
     | 
| 
      
 93 
     | 
    
         
            +
                                    </p>
         
     | 
| 
      
 94 
     | 
    
         
            +
                                  </td>
         
     | 
| 
      
 95 
     | 
    
         
            +
                                </tr>
         
     | 
| 
      
 96 
     | 
    
         
            +
                              </table>
         
     | 
| 
      
 97 
     | 
    
         
            +
                            </td>
         
     | 
| 
      
 98 
     | 
    
         
            +
                          </tr>
         
     | 
| 
      
 99 
     | 
    
         
            +
                        </table>
         
     | 
| 
      
 100 
     | 
    
         
            +
                      </td>
         
     | 
| 
      
 101 
     | 
    
         
            +
                    </tr>
         
     | 
| 
      
 102 
     | 
    
         
            +
                  </table>
         
     | 
| 
      
 103 
     | 
    
         
            +
                </div>
         
     | 
| 
      
 104 
     | 
    
         
            +
              </body>
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
            </html>
         
     | 
| 
         @@ -0,0 +1,55 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            <% unless flash.empty? %>
         
     | 
| 
      
 2 
     | 
    
         
            +
              <% if flash.alert.present? && flash.alert.is_a?(Hash) %>
         
     | 
| 
      
 3 
     | 
    
         
            +
                <% title = flash.alert&.dig(:title) || flash.alert&.dig("title") %>
         
     | 
| 
      
 4 
     | 
    
         
            +
                <% description = flash.alert&.dig(:description) || flash.alert&.dig("description") %>
         
     | 
| 
      
 5 
     | 
    
         
            +
              <% elsif flash.notice.present? && flash.notice.is_a?(Hash) %>
         
     | 
| 
      
 6 
     | 
    
         
            +
                <% title = flash.notice&.dig(:title) || flash.notice&.dig("title") %>
         
     | 
| 
      
 7 
     | 
    
         
            +
                <% description = flash.notice&.dig(:description) || flash.notice&.dig("description") %>
         
     | 
| 
      
 8 
     | 
    
         
            +
              <% else %>
         
     | 
| 
      
 9 
     | 
    
         
            +
                <% title = "Aplicación" %>
         
     | 
| 
      
 10 
     | 
    
         
            +
                <% description = flash.notice || flash.alert %>
         
     | 
| 
      
 11 
     | 
    
         
            +
              <% end %>
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              <div class="flex flex-col items-center w-full space-y-4 sm:items-end" data-controller="alert">
         
     | 
| 
      
 14 
     | 
    
         
            +
                <div class="w-full max-w-sm overflow-hidden bg-white rounded-lg shadow-lg pointer-events-auto ring-1 ring-black ring-opacity-5 hidden"
         
     | 
| 
      
 15 
     | 
    
         
            +
                  data-alert-target="notification"
         
     | 
| 
      
 16 
     | 
    
         
            +
                  data-transition-enter="transform ease-out duration-300 transition"
         
     | 
| 
      
 17 
     | 
    
         
            +
                  data-transition-enter-active="translate-y-2 opacity-0 sm:translate-y-0 sm:translate-x-2"
         
     | 
| 
      
 18 
     | 
    
         
            +
                  data-transition-enter-to="translate-y-0 opacity-100 sm:translate-x-0"
         
     | 
| 
      
 19 
     | 
    
         
            +
                  data-transition-leave="transition ease-in duration-100"
         
     | 
| 
      
 20 
     | 
    
         
            +
                  data-transition-leave-active="opacity-100"
         
     | 
| 
      
 21 
     | 
    
         
            +
                  data-transition-leave-to="opacity-0">
         
     | 
| 
      
 22 
     | 
    
         
            +
                  <div class="p-4">
         
     | 
| 
      
 23 
     | 
    
         
            +
                    <div class="flex items-start">
         
     | 
| 
      
 24 
     | 
    
         
            +
                      <div class="flex-shrink-0">
         
     | 
| 
      
 25 
     | 
    
         
            +
                        <% if alert %>
         
     | 
| 
      
 26 
     | 
    
         
            +
                          <svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-red-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
         
     | 
| 
      
 27 
     | 
    
         
            +
                            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
         
     | 
| 
      
 28 
     | 
    
         
            +
                          </svg>
         
     | 
| 
      
 29 
     | 
    
         
            +
                        <% else %>
         
     | 
| 
      
 30 
     | 
    
         
            +
                          <svg class="w-6 h-6 text-green-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
         
     | 
| 
      
 31 
     | 
    
         
            +
                            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
         
     | 
| 
      
 32 
     | 
    
         
            +
                          </svg>
         
     | 
| 
      
 33 
     | 
    
         
            +
                        <% end %>
         
     | 
| 
      
 34 
     | 
    
         
            +
                      </div>
         
     | 
| 
      
 35 
     | 
    
         
            +
                      <div class="ml-3 w-0 flex-1 pt-0.5">
         
     | 
| 
      
 36 
     | 
    
         
            +
                        <p class="text-sm font-medium text-gray-900">
         
     | 
| 
      
 37 
     | 
    
         
            +
                          <%= title %>
         
     | 
| 
      
 38 
     | 
    
         
            +
                        </p>
         
     | 
| 
      
 39 
     | 
    
         
            +
                        <p class="mt-1 text-sm text-gray-500">
         
     | 
| 
      
 40 
     | 
    
         
            +
                          <%= description %>
         
     | 
| 
      
 41 
     | 
    
         
            +
                        </p>
         
     | 
| 
      
 42 
     | 
    
         
            +
                      </div>
         
     | 
| 
      
 43 
     | 
    
         
            +
                      <div class="flex flex-shrink-0 ml-4">
         
     | 
| 
      
 44 
     | 
    
         
            +
                        <button data-action="alert#close" class="inline-flex text-gray-400 bg-white rounded-md hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-creditario-500">
         
     | 
| 
      
 45 
     | 
    
         
            +
                          <span class="sr-only">Close</span>
         
     | 
| 
      
 46 
     | 
    
         
            +
                          <svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
         
     | 
| 
      
 47 
     | 
    
         
            +
                            <path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" />
         
     | 
| 
      
 48 
     | 
    
         
            +
                          </svg>
         
     | 
| 
      
 49 
     | 
    
         
            +
                        </button>
         
     | 
| 
      
 50 
     | 
    
         
            +
                      </div>
         
     | 
| 
      
 51 
     | 
    
         
            +
                    </div>
         
     | 
| 
      
 52 
     | 
    
         
            +
                  </div>
         
     | 
| 
      
 53 
     | 
    
         
            +
                </div>
         
     | 
| 
      
 54 
     | 
    
         
            +
              </div>
         
     | 
| 
      
 55 
     | 
    
         
            +
            <% end %>
         
     | 
| 
         @@ -0,0 +1,19 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            <%= turbo_frame_tag :form do %>
         
     | 
| 
      
 2 
     | 
    
         
            +
              <%= form_with url: no_password.session_confirmations_path, method: "patch", class: "space-y-6", html: { "data-turbo-frame": "_top" } do |form| %>
         
     | 
| 
      
 3 
     | 
    
         
            +
                <p class="text-skin-muted px-4 text-center">
         
     | 
| 
      
 4 
     | 
    
         
            +
                  <%= t("no_password.session_confirmations.edit.description") %>
         
     | 
| 
      
 5 
     | 
    
         
            +
                </p>
         
     | 
| 
      
 6 
     | 
    
         
            +
                <div>
         
     | 
| 
      
 7 
     | 
    
         
            +
                  <label class="block text-sm font-medium text-skin-muted"><%= form.label t("form.labels.token") %></label>
         
     | 
| 
      
 8 
     | 
    
         
            +
                  <div class="mt-1">
         
     | 
| 
      
 9 
     | 
    
         
            +
                    <%= form.text_field :token, maxlength: 60, required: true, class: "input w-full", type: "text", placeholder: t("form.placeholders.token") %>
         
     | 
| 
      
 10 
     | 
    
         
            +
                  </div>
         
     | 
| 
      
 11 
     | 
    
         
            +
                </div>
         
     | 
| 
      
 12 
     | 
    
         
            +
                <div>
         
     | 
| 
      
 13 
     | 
    
         
            +
                  <%= form.submit t("form.submit.login"), class: "w-full flex button button-accented" %>
         
     | 
| 
      
 14 
     | 
    
         
            +
                </div>
         
     | 
| 
      
 15 
     | 
    
         
            +
                <div>
         
     | 
| 
      
 16 
     | 
    
         
            +
                  <%= link_to t("no_password.session_confirmations.edit.request_token"), no_password.new_session_path, class: "w-full block text-center text-skin-accented hover:text-skin-accented-hover", "data-turbo": false %>
         
     | 
| 
      
 17 
     | 
    
         
            +
                </div>
         
     | 
| 
      
 18 
     | 
    
         
            +
              <% end %>
         
     | 
| 
      
 19 
     | 
    
         
            +
            <% end %>
         
     | 
| 
         @@ -0,0 +1,15 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            <div class="h-screen bg-gray-50">
         
     | 
| 
      
 2 
     | 
    
         
            +
              <div class="min-h-full flex flex-col justify-center py-12 sm:px-6 lg:px-8">
         
     | 
| 
      
 3 
     | 
    
         
            +
                <div class="sm:mx-auto sm:w-full sm:max-w-md">
         
     | 
| 
      
 4 
     | 
    
         
            +
                  <%= image_tag("no_password/aoo.svg", class:"mx-auto h-12 w-auto", alt: "Workflow")%>
         
     | 
| 
      
 5 
     | 
    
         
            +
                  <h2 class="mt-6 text-center text-3xl font-bold text-skin-base leading-3">
         
     | 
| 
      
 6 
     | 
    
         
            +
                    <%= t("no_password.session_confirmations.edit.title") %>
         
     | 
| 
      
 7 
     | 
    
         
            +
                  </h2>
         
     | 
| 
      
 8 
     | 
    
         
            +
                </div>
         
     | 
| 
      
 9 
     | 
    
         
            +
                <div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
         
     | 
| 
      
 10 
     | 
    
         
            +
                  <div class="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
         
     | 
| 
      
 11 
     | 
    
         
            +
                    <%= render partial: "form" %>
         
     | 
| 
      
 12 
     | 
    
         
            +
                  </div>
         
     | 
| 
      
 13 
     | 
    
         
            +
                </div>
         
     | 
| 
      
 14 
     | 
    
         
            +
              </div>
         
     | 
| 
      
 15 
     | 
    
         
            +
            </div>
         
     | 
| 
         @@ -0,0 +1,16 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            <%= turbo_frame_tag :form do %>
         
     | 
| 
      
 2 
     | 
    
         
            +
              <%= form_with model: @resource, local: true, url: no_password.session_path(return_to: @return_to), class: "space-y-6", html: {"data-turbo": false} do |form| %>
         
     | 
| 
      
 3 
     | 
    
         
            +
                <p class="text-skin-muted px-4 text-center">
         
     | 
| 
      
 4 
     | 
    
         
            +
                    <%= t("no_password.sessions.new.description") %>
         
     | 
| 
      
 5 
     | 
    
         
            +
                </p>
         
     | 
| 
      
 6 
     | 
    
         
            +
                <div>
         
     | 
| 
      
 7 
     | 
    
         
            +
                  <label class="block text-sm font-medium text-skin-muted"><%= form.label :email %></label>
         
     | 
| 
      
 8 
     | 
    
         
            +
                  <div class="mt-1">
         
     | 
| 
      
 9 
     | 
    
         
            +
                    <%= form.text_field :email, maxlength: 60, required: true, class: "input w-full", type: "email", placeholder: t("form.placeholders.email") %>
         
     | 
| 
      
 10 
     | 
    
         
            +
                  </div>
         
     | 
| 
      
 11 
     | 
    
         
            +
                </div>
         
     | 
| 
      
 12 
     | 
    
         
            +
                <div>
         
     | 
| 
      
 13 
     | 
    
         
            +
                    <%= form.submit t("form.submit.login"), class: "w-full flex button button-accented" %>
         
     | 
| 
      
 14 
     | 
    
         
            +
                </div>
         
     | 
| 
      
 15 
     | 
    
         
            +
              <% end %>
         
     | 
| 
      
 16 
     | 
    
         
            +
            <% end %>
         
     | 
| 
         @@ -0,0 +1,15 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            <div class="h-screen bg-gray-50">
         
     | 
| 
      
 2 
     | 
    
         
            +
              <div class="min-h-full flex flex-col justify-center py-12 sm:px-6 lg:px-8">
         
     | 
| 
      
 3 
     | 
    
         
            +
                <div class="sm:mx-auto sm:w-full sm:max-w-md">
         
     | 
| 
      
 4 
     | 
    
         
            +
                  <%= image_tag("no_password/aoo.svg", class:"mx-auto h-12 w-auto", alt: "Workflow")%>
         
     | 
| 
      
 5 
     | 
    
         
            +
                  <h2 class="mt-6 text-center text-3xl font-bold text-skin-base leading-3">
         
     | 
| 
      
 6 
     | 
    
         
            +
                    <%= t("no_password.sessions.new.title") %>
         
     | 
| 
      
 7 
     | 
    
         
            +
                  </h2>
         
     | 
| 
      
 8 
     | 
    
         
            +
                </div>
         
     | 
| 
      
 9 
     | 
    
         
            +
                <div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
         
     | 
| 
      
 10 
     | 
    
         
            +
                  <div class="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
         
     | 
| 
      
 11 
     | 
    
         
            +
                    <%= render partial: "form" %>
         
     | 
| 
      
 12 
     | 
    
         
            +
                  </div>
         
     | 
| 
      
 13 
     | 
    
         
            +
                </div>
         
     | 
| 
      
 14 
     | 
    
         
            +
              </div>
         
     | 
| 
      
 15 
     | 
    
         
            +
            </div>
         
     | 
| 
         @@ -0,0 +1,14 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            <p style="margin: 0; font-size: 18px;"><%= t("mailers.send_token.greetings") %></p>
         
     | 
| 
      
 2 
     | 
    
         
            +
            <p style="font-size: 16px; color: #374151;"><%= t("mailers.send_token.instructions_1") %></p>
         
     | 
| 
      
 3 
     | 
    
         
            +
            <p style="text-align:center; font-size: 20px; color: #374151; font-weight: 600;"><%= @friendly_token %></p>
         
     | 
| 
      
 4 
     | 
    
         
            +
            <p style="font-size: 16px; color: #374151;"><%= t("mailers.send_token.instructions_2") %></p>
         
     | 
| 
      
 5 
     | 
    
         
            +
            <div style="margin: auto 0; text-align: center;">
         
     | 
| 
      
 6 
     | 
    
         
            +
              <%= link_to no_password.session_confirmation_url(token: @signed_token), class: "hover-bg-skin-button-accented-hover", style: "display: inline-block; margin: auto; border-radius: 6px; background-color: rgb(80, 70, 137); padding: 16px 24px; font-size: 16px; font-weight: 500; color: rgb(255, 255, 255); text-decoration: none; box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);" do %>
         
     | 
| 
      
 7 
     | 
    
         
            +
                <!--[if mso]><i style="letter-spacing: 24px; mso-font-width: -100%; mso-text-raise: 26pt;"> </i><![endif]-->
         
     | 
| 
      
 8 
     | 
    
         
            +
                <span style="mso-text-raise: 13pt;"><%= t("mailers.send_token.start_session") %></span>
         
     | 
| 
      
 9 
     | 
    
         
            +
                <!--[if mso]><i style="letter-spacing: 24px; mso-font-width: -100%;"> </i><![endif]-->
         
     | 
| 
      
 10 
     | 
    
         
            +
              <% end %>
         
     | 
| 
      
 11 
     | 
    
         
            +
            </div>
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            <hr style="margin-top: 25px; margin-bottom: 25px; border: 1px solid #f3f4f6" />
         
     | 
| 
      
 14 
     | 
    
         
            +
            <p style="font-size: 16px; color: #374151;"><%= t("mailers.send_token.instructions_3") %></p>
         
     | 
| 
         @@ -0,0 +1,12 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            <%= t("mailers.send_token.subject") %>
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            <%= t("mailers.send_token.greetings") %>
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            <%= t("mailers.send_token.instructions_1") %>
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            <%= @friendly_token %>
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            <%= t("mailers.send_token.instructions_2_text") %>
         
     | 
| 
      
 10 
     | 
    
         
            +
            <%= no_password.session_confirmation_url(token: @signed_token)%>
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            <%= t("mailers.send_token.instructions_3") %>
         
     |