Multi-account session switching.
The Plug session holds an ordered stack of raw session tokens under
:pk_session_accounts. hd/1 of the stack is the ROOT account (the original
login). The currently active token stays in :user_token, so all existing auth
resolution (fetch_phoenix_kit_current_*, on_mount) is untouched.
Read helpers (gate_allowed?/1, list_accounts/1) take the string-keyed session
map (works from both the plug and the LiveView on_mount). Conn-mutating ops
(add_account/3, add_authenticated_user/2, switch_to/2, remove_account/2,
logout helpers) take and return a Plug.Conn.
Summary
Functions
Validates credentials and appends a real session for that user to the stack, making it the active account. The new account may be any role; the gate is enforced by the caller (controller) against the root account.
Appends an already-authenticated (active) user to the session stack and makes
them the active account. Shares all invariants with add_account/3
Deletes every stack token from the DB (used by 'Log out all').
True when the root session belongs to ANY authenticated user AND the
multi_session_enabled setting is on. Evaluated against the root so the
switcher stays visible even when a secondary account is active.
Resolves each stack token to a render struct:
%{ref, user, email, role, active?, root?}. Tokens that no longer resolve to a
user (expired/deleted) are dropped.
Logs out the active account. When a non-root account is active, deletes it and
switches back to root ({:switched, conn, root_user}). When the root account is
active, signals a full logout ({:full, conn}) for the caller to run.
Maximum number of accounts allowed in one stack.
Removes a non-root token from the stack and deletes it from the DB.
Resolves the two transient Scope fields {multi_session_allowed?, multi_session_accounts}
for a session in one call.
The list of raw session tokens in the stack. Falls back to the single active
token when no explicit stack is stored, and [] when there is no active token.
Activates a token already present in the stack, identified by ref.
Functions
Validates credentials and appends a real session for that user to the stack, making it the active account. The new account may be any role; the gate is enforced by the caller (controller) against the root account.
Returns {:error, :already_in_stack} if the user is already present.
Appends an already-authenticated (active) user to the session stack and makes
them the active account. Shares all invariants with add_account/3:
- Stack-limit check (
:stack_full) - Dedup check — returns
{:error, :already_in_stack}if the user is already present - Session-fixation protection via
renew_and_put_active_token/2
Used by the OAuth add-account callback so the same logic applies whether the user was authenticated via password or via OAuth.
Deletes every stack token from the DB (used by 'Log out all').
True when the root session belongs to ANY authenticated user AND the
multi_session_enabled setting is on. Evaluated against the root so the
switcher stays visible even when a secondary account is active.
Anonymous (no root token / no valid user) always returns false.
Resolves each stack token to a render struct:
%{ref, user, email, role, active?, root?}. Tokens that no longer resolve to a
user (expired/deleted) are dropped.
Logs out the active account. When a non-root account is active, deletes it and
switches back to root ({:switched, conn, root_user}). When the root account is
active, signals a full logout ({:full, conn}) for the caller to run.
Maximum number of accounts allowed in one stack.
Removes a non-root token from the stack and deletes it from the DB.
Resolves the two transient Scope fields {multi_session_allowed?, multi_session_accounts}
for a session in one call.
Crucially, the (DB-heavy) account stack is resolved ONLY when the setting is on.
When multi_session_enabled is off — the default — this short-circuits to
{false, []} without touching the DB, so the hot auth path (plug + every
LiveView mount) pays nothing for a feature that is disabled.
When it IS on, allowed? is derived from the resolved stack (a surviving root
account) rather than a separate gate_allowed?/1 call, which would re-resolve
the root token in its own query on top of the list_accounts/1 walk.
The list of raw session tokens in the stack. Falls back to the single active
token when no explicit stack is stored, and [] when there is no active token.
Activates a token already present in the stack, identified by ref.