# `PhoenixKit.Users.Auth.SessionRef`
[🔗](https://github.com/BeamLabEU/phoenix_kit/blob/v2.64.1/lib/phoenix_kit/users/auth/session_ref.ex#L1)

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.

# `refusal`

```elixir
@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).

# `t`

```elixir
@type t() :: {String.t(), String.t()}
```

`{session_token_uuid, user_uuid}`.

# `from_session`

```elixir
@spec from_session(map()) :: {:ok, t()} | :error
```

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.

# `scope_for_session`

```elixir
@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}`
(`t:refusal/0`). Never raises on a malformed reference.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
