PhoenixKitWeb.Users.MultiSession (phoenix_kit v1.7.133)

Copy Markdown View Source

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

add_account(conn, email_or_username, password)

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.

add_authenticated_user(conn, user)

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.

delete_all_stack_tokens(conn)

Deletes every stack token from the DB (used by 'Log out all').

gate_allowed?(session)

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.

list_accounts(session)

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.

log_out_active(conn)

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.

max_accounts()

Maximum number of accounts allowed in one stack.

remove_account(conn, ref)

Removes a non-root token from the stack and deletes it from the DB.

scope_fields(session)

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.

stack_tokens(session)

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.

switch_to(conn, ref)

Activates a token already present in the stack, identified by ref.