A live browser session as an actor, for code that runs on a person's behalf without a
Plug.Conn (an assistant LiveView, a task it starts).
A session is referred to by {session_token_uuid, user_uuid}: the uuid of its row in
phoenix_kit_users_tokens and the user it belongs to. Neither is a secret, so the pair
may travel in the signed (but not encrypted) session map of a nested LiveView; the raw
session token must not, and never leaves the cookie session.
scope_for_session/2 rebuilds the user's PhoenixKit.Users.Auth.Scope from that row
on every call, by the rules a web request follows
(PhoenixKit.Users.Auth.UserToken.verify_session_token_uuid_query/1): the row is a
"session" row and has not expired, its user is not a service user, and the scope is
narrowed to the role this session acts as (PhoenixKit.Users.ActiveRole). The account
must also pass the gate every authenticated page applies: a confirmed email while
require_email_confirmation is on, and the invite-only gate
(PhoenixKit.Users.Referrals.access_satisfied?/1), so a session acts with no more than
its pages would let it. Logging out, revoking the session or blocking the user takes
effect on the next call.
from_session/1 reads the reference from a LiveView's session map.
The pair is not a secret, but it is an in-process credential: any code that hands it to
PhoenixKit.API.Executor.execute_as/5 or visible_for/3 acts as that session. Take it
only from the signed session of the LiveView that runs in that session
(from_session/1 in its connected mount), never from params or client events.
Maintenance mode is deliberately not checked on this path: the web shows the maintenance layout at mount, where nothing that calls the Executor is rendered.
Summary
Types
Why a session cannot act: :session_gone (no such live session: logged out, revoked,
expired, not a session row, or a service user's), :mismatch (the session belongs to
another user), :inactive (the user is blocked), :unconfirmed (the email is not
confirmed and confirmation is required) or :referral_required (the invite-only gate
still asks for a referral code).
{session_token_uuid, user_uuid}.
Functions
The reference of the session a LiveView runs in, read from its session map: the
"user_token" of the cookie session resolved to {session_token_uuid, user_uuid}.
:error when there is no token or it is not a live session.
The scope of the live session session_token_uuid, which must belong to user_uuid.
Types
@type refusal() ::
:session_gone | :mismatch | :inactive | :unconfirmed | :referral_required
Why a session cannot act: :session_gone (no such live session: logged out, revoked,
expired, not a session row, or a service user's), :mismatch (the session belongs to
another user), :inactive (the user is blocked), :unconfirmed (the email is not
confirmed and confirmation is required) or :referral_required (the invite-only gate
still asks for a referral code).
{session_token_uuid, user_uuid}.
Functions
The reference of the session a LiveView runs in, read from its session map: the
"user_token" of the cookie session resolved to {session_token_uuid, user_uuid}.
:error when there is no token or it is not a live session.
Call it in the nested LiveView itself (a sticky live_render beside the page, e.g. a
widget in the layout): LiveView merges the cookie session into the session of every
LiveView, nested ones included, both in the connected mount (the socket's
connect_info: [session: ...]) and in the static render (the parent's conn session).
The reference read in a static render is the same one, but act on it (call the
executor) only in the connected mount.
Do not have a host page read the reference and pass it in the nested LiveView's
:session: a widget rendered by the core layout has no host that holds the cookie
session. Like PhoenixKitWeb.Users.MultiSession.impersonated?/1, call it where the
session is signed and trusted, never with data from params or events.
@spec scope_for_session(term(), term()) :: {:ok, PhoenixKit.Users.Auth.Scope.t()} | {:error, refusal()}
The scope of the live session session_token_uuid, which must belong to user_uuid.
Returns {:ok, scope} narrowed to the session's active role, or {:error, reason}
(refusal/0). Never raises on a malformed reference.