PhoenixKit.Users.Sessions (phoenix_kit v2.23.0)

Copy Markdown View Source

Context for managing user sessions in PhoenixKit.

This module provides functions for listing, viewing, and managing active user sessions. It's primarily used by the admin interface to monitor and control user sessions.

Functions

Session Information

Each session includes:

  • User information (id, email, status)
  • Session creation time
  • Session token (first 8 chars for identification)
  • Session age and validity status

Summary

Functions

Counts the total number of active sessions.

Broadcasts LiveView's "disconnect" message on live_socket_id.

Tells every LiveView socket authenticated by one of tokens to disconnect.

disconnect_tokens/1, but delay_ms later (default 5 s), from a task under PhoenixKit.TaskSupervisor.

Gets detailed information about a specific session by token ID.

Gets session statistics including total, unique users, expired sessions etc.

Lists all currently active sessions with user information.

One page of sessions for the admin list, filtered and paginated in SQL.

Lists a user's active sessions enriched with device info, for the self-service "Active Sessions" UI.

Lists all active sessions for a specific user.

The topic a LiveView socket authenticated by token is subscribed to.

Revokes all of a user's sessions except the one identified by current_token (kept so the acting browser stays signed in). Returns the number revoked. With a nil token, revokes every session for the user.

Revokes a specific session by token ID.

Revokes one of a user's own sessions by token uuid.

Revokes all sessions for a specific user.

Revokes every session of user that is acting as role_uuid (PhoenixKit.Users.ActiveRole) — called when that role is taken away from them, so the affected browser signs out instead of quietly continuing in another role. Sessions in another role, and sessions that never switched (NULL, the default role), are left alone. Returns the number revoked.

Functions

count_active_sessions()

Counts the total number of active sessions.

Examples

iex> count_active_sessions()
15

disconnect(live_socket_id)

@spec disconnect(String.t()) :: :ok

Broadcasts LiveView's "disconnect" message on live_socket_id.

disconnect_tokens(tokens)

@spec disconnect_tokens([binary()]) :: :ok

Tells every LiveView socket authenticated by one of tokens to disconnect.

Deleting a token row ends a session for the NEXT request; it does nothing to a socket that is already connected, which keeps its assigns — an authenticated scope included — and goes on serving events until it re-mounts. Revoking a session has to do both, so every path that drops session tokens calls this with the raw token values it deleted.

Raw values, because the topic is derived from the token itself: once the row is gone there is nothing left to address the socket with. Postgres' DELETE ... RETURNING is how the callers keep them.

disconnect_tokens_later(tokens, delay_ms \\ 5000)

@spec disconnect_tokens_later([binary()], non_neg_integer()) :: :ok

disconnect_tokens/1, but delay_ms later (default 5 s), from a task under PhoenixKit.TaskSupervisor.

For the one case where a token that is already deleted must NOT be disconnected right away: the browser that just changed its password. Its settings LiveView still needs the socket for the phx-trigger-action re-login POST, and the re-login page has to finish loading before the old socket's reconnect-and-redirect could race the navigation. Once the new page is up the old topic is held only by leftovers — including any stolen copy of the same cookie — and those are what the delayed broadcast closes.

get_session_info(token_uuid)

Gets detailed information about a specific session by token ID.

Examples

iex> get_session_info("019b5704-3680-7b95-...")
%{token_uuid: "019b5704-...", user: %User{}, created_at: ~N[...], ...}

iex> get_session_info("019b5704-0000-0000-...")
nil

get_session_stats()

Gets session statistics including total, unique users, expired sessions etc.

Examples

iex> get_session_stats()
%{
  total_active: 15,
  unique_users: 8,
  expired_sessions: 5,
  sessions_today: 3
}

list_active_sessions(scope \\ :active)

Lists all currently active sessions with user information.

Returns a list of maps containing session and user details.

Examples

iex> list_active_sessions()
[
  %{
    token_uuid: "019b5704-3680-7b95-...",
    token_preview: "abc12345",
    user: %User{uuid: "019b5704-...", email: "user@example.com"},
    created_at: ~N[2024-01-01 12:00:00],
    expires_at: ~N[2024-03-02 12:00:00],
    is_current: false
  }
]

list_sessions_paginated(opts \\ [])

One page of sessions for the admin list, filtered and paginated in SQL.

Options:

  • :scope:active (default), :today or :expired, as list_active_sessions/1
  • :search — case-insensitive match on the user's email or the token preview (the first 8 hex chars shown in the UI)
  • :user_status"all" (default), "active", "inactive", "confirmed" or "pending"
  • :page / :per_page — defaults 1 / 20

Returns %{sessions: [...], total_count: n, total_pages: n}. The page is clamped into [1, total_pages] before the offset is applied, so a stale link past the end shows the last page rather than an empty one — mirror the returned page back into the assigns.

list_user_device_sessions(user, current_token)

Lists a user's active sessions enriched with device info, for the self-service "Active Sessions" UI.

Each session's (ip_address, user_agent_hash) is matched against the user's KnownDevice history to recover browser/OS/location/last-active (session tokens store only the hashed UA, never the raw string). Sessions predating fingerprinting — or from a device never recorded as "known" — degrade gracefully to an "Unknown device" with nil fields.

current_token is the raw session token of the browser making the request (from the session's "user_token"); the matching row is flagged is_current: true so the UI can mark it and omit its "Sign out" button.

list_user_sessions(user)

Lists all active sessions for a specific user.

Examples

iex> list_user_sessions(%User{uuid: "019b5704-..."})
[%{token_uuid: "019b5704-...", user: %User{}, created_at: ~N[...], ...}]

live_socket_id(token)

@spec live_socket_id(binary()) :: String.t()

The topic a LiveView socket authenticated by token is subscribed to.

Mirrors what the session plugs write to :live_socket_id at login.

revoke_other_user_sessions(user, current_token)

Revokes all of a user's sessions except the one identified by current_token (kept so the acting browser stays signed in). Returns the number revoked. With a nil token, revokes every session for the user.

revoke_session(token_uuid)

Revokes a specific session by token ID.

Returns :ok if successful, {:error, :not_found} if session doesn't exist.

Examples

iex> revoke_session("019b5704-3680-7b95-...")
:ok

iex> revoke_session("019b5704-0000-0000-...")
{:error, :not_found}

revoke_user_session(user, token_uuid)

Revokes one of a user's own sessions by token uuid.

Scoped to user so a user can never revoke another user's session by guessing a token uuid. Returns :ok or {:error, :not_found}.

revoke_user_sessions(user)

Revokes all sessions for a specific user.

Returns the number of sessions revoked.

Examples

iex> revoke_user_sessions(%User{uuid: "019b5704-..."})
3

revoke_user_sessions_in_role(user, role_uuid)

@spec revoke_user_sessions_in_role(PhoenixKit.Users.Auth.User.t(), String.t()) ::
  non_neg_integer()

Revokes every session of user that is acting as role_uuid (PhoenixKit.Users.ActiveRole) — called when that role is taken away from them, so the affected browser signs out instead of quietly continuing in another role. Sessions in another role, and sessions that never switched (NULL, the default role), are left alone. Returns the number revoked.