module Authentication extend ActiveSupport::Concern included do before_action :require_account # Checking and setting account must happen first before_action :require_authentication after_action :ensure_development_magic_link_not_leaked helper_method :authenticated? etag { Current.session.id if authenticated? } include LoginHelper end class_methods do def require_unauthenticated_access(**options) allow_unauthenticated_access **options before_action :redirect_authenticated_user, **options end def allow_unauthenticated_access(**options) skip_before_action :require_authentication, **options before_action :resume_session, **options allow_unauthorized_access **options end def disallow_account_scope(**options) skip_before_action :require_account, **options before_action :redirect_tenanted_request, **options end end private def authenticated? Current.session.present? end def require_account unless Current.account.present? redirect_to session_menu_url(script_name: nil) end end def require_authentication resume_session || request_authentication end def resume_session if session = find_session_by_cookie set_current_session session end end def find_session_by_cookie Session.find_signed(cookies.signed[:session_token]) end def request_authentication if Current.account.present? session[:return_to_after_authenticating] = request.url end redirect_to_login_url end def after_authentication_url session.delete(:return_to_after_authenticating) || landing_url end def redirect_authenticated_user redirect_to root_url if authenticated? end def redirect_tenanted_request redirect_to root_url if Current.account.present? end def start_new_session_for(identity) identity.sessions.create!(user_agent: request.user_agent, ip_address: request.remote_ip).tap do |session| set_current_session session end end def set_current_session(session) Current.session = session cookies.signed.permanent[:session_token] = { value: session.signed_id, httponly: true, same_site: :lax } end def terminate_session Current.session.destroy cookies.delete(:session_token) end def ensure_development_magic_link_not_leaked unless Rails.env.development? raise "Leaking magic link via flash in #{Rails.env}?" if flash[:magic_link_code].present? end end def serve_development_magic_link(magic_link) if Rails.env.development? flash[:magic_link_code] = magic_link&.code end end end