Files
fizzy/app/controllers/concerns/authentication.rb
T
David Heinemeier Hansson db29562c4c Tie access token directly to session
We need to present them differently in the session list and prevent them
from being deleted
2025-12-10 09:23:52 +01:00

130 lines
3.5 KiB
Ruby

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 || find_session_by_bearer_token
set_current_session session
end
end
def find_session_by_cookie
Session.find_signed(cookies.signed[:session_token])
end
def find_session_by_bearer_token
if request_authorized_by_bearer_token?
Identity::AccessToken.find_by(token: authorization_bearer_token)&.session
end
end
def request_authorized_by_bearer_token?
request.authorization.to_s.starts_with? "Bearer"
end
def authorization_bearer_token
request.authorization.to_s.split(" ", 2).second
end
def request_authentication
if request_authorized_by_bearer_token?
head :unauthorized
else
if Current.account.present?
session[:return_to_after_authenticating] = request.url
end
redirect_to_login_url
end
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 redirect_to_session_magic_link(magic_link, return_to: nil)
serve_development_magic_link(magic_link)
session[:return_to_after_authenticating] = return_to if return_to
redirect_to session_magic_link_url(script_name: nil)
end
def serve_development_magic_link(magic_link)
if Rails.env.development?
flash[:magic_link_code] = magic_link&.code
end
end
end