Skip to main content

Sessions

Overview

The sessions module provides user session management, allowing users to view their active sessions (with IP address, user agent, and expiry information) and revoke individual sessions or all other sessions. Sessions are created during OAuth login and are tied to refresh tokens via a SHA-256 token hash.

Architecture

ID App (NextAuth) ──> OAuth Login
|
v
exchangeToken mutation
|
+--> create_session (DB)
+--> cache_session (Redis)
+--> Issue JWT with session_id claim
|
v
Dashboard UI
|
v
GraphQL (SessionQuery / SessionMutation)
|
v
db::auth (PostgreSQL sessions table)

Session Lifecycle

  1. Creation -- During exchangeToken, a session row is created with the user ID, active account ID, SHA-256 hash of the refresh token's jti, client IP, user agent, and expiry timestamp. The session is also cached in Redis for fast lookup.
  2. JWT Embedding -- The session ID is embedded in the JWT via create_token_with_session, allowing the API to identify which session corresponds to the current request.
  3. Refresh -- During refreshToken, the session is looked up by token hash. A new JWT is issued with the same session ID. The refresh token itself is not rotated.
  4. Logout -- The logout mutation deletes the session from both PostgreSQL and Redis by token hash.
  5. Revocation -- Users can delete specific sessions or all sessions except the current one.

Session Filtering

Only non-expired sessions are returned by list_sessions_for_user (filtered by expires_at > now()).

API

GraphQL Queries

QueryArgsReturnsPermission
sessions--[Session]sessions:read

Returns all active (non-expired) sessions for the current user, ordered by created_at DESC.

GraphQL Mutations

MutationArgsReturnsPermission
deleteSessionid: UUIDBooleansessions:delete
deleteAllOtherSessions--Booleansessions:delete
  • deleteSession verifies the session belongs to the current user before deletion.
  • deleteAllOtherSessions uses the session_id from the JWT to identify the current session and deletes all others for the user.

GraphQL Types

type Session {
id: UUID!
userId: UUID!
activeAccountId: UUID
ipAddress: String
userAgent: String
expiresAt: String!
createdAt: String!
}

Note: The token_hash field is present in the database row but is not exposed via GraphQL.

REST Endpoints

Sessions are user-scoped; the REST handlers enforce ownership in code rather than via an RBAC permission string. All paths live under /v1/users/me/sessions.

MethodPathPermissionDescription
GET/v1/users/me/sessionsAuthList active (non-expired) sessions for the current user
DELETE/v1/users/me/sessions/{id}AuthRevoke one session (must belong to the current user)
DELETE/v1/users/me/sessionsAuthRevoke all other sessions (keeps the current one)

Permissions

PermissionDescription
sessions:readView active sessions (used by the GraphQL sessions query)
sessions:deleteRevoke sessions (used by the GraphQL deleteSession / deleteAllOtherSessions mutations)

Included in: Owner, Administrator, Moderator, Viewer roles.

Database

Table: sessions

ColumnTypeDescription
idUUID (PK)Session ID
user_idUUID (FK)Session owner
active_account_idUUID (FK)Currently selected account
token_hashTEXTSHA-256 hash of the refresh token's jti
ip_addressINETClient IP address (stored as inet, returned via host())
user_agentTEXTClient user agent string
expires_atTIMESTAMPTZSession expiry
created_atTIMESTAMPTZCreation timestamp

DB Functions

FunctionDescription
create_sessionInsert a new session with token hash, IP, user agent, and expiry
list_sessions_for_userList non-expired sessions ordered by created_at DESC
find_session_by_token_hashLookup by token hash (used during refresh)
delete_session_by_idDelete a specific session with user ownership check
delete_other_sessionsDelete all sessions except the specified one
delete_sessionDelete by token hash (used during logout)
delete_all_sessions_for_userDelete all sessions (used for account dissolution)

Key Files

FilePurpose
apps/api/src/graphql/sessions.rsGraphQL queries and mutations
apps/api/src/graphql/auth.rsToken exchange and refresh (session creation)
apps/api/src/db/auth.rsSession CRUD and token hash operations
crates/lo-auth/src/jwt.rsJWT creation with embedded session ID
crates/lo-auth/src/api_key.rshash_token function (SHA-256 hashing)