Skip to main content

Tokens

Overview

The Tokens module manages popout tokens for overlay and popout page access. Popout tokens are non-expiring access tokens designed for use in OBS browser sources and other embedded contexts where cookie-based authentication is not practical. Each token has configurable permissions, an optional user assignment, and a label for identification. The full token is only shown once at creation time; afterward, only a prefix is stored for identification. Tokens are hashed before storage for security.

Architecture

Backend

  • GraphQL (apps/api/src/graphql/tokens.rs) -- Queries for listing tokens. Mutations for creating, updating, and deleting tokens.
  • Token Generation (crates/lo-auth/src/popout_token.rs) -- generate_popout_token() produces a cryptographically random token, its hash (for storage), and a prefix (for display).
  • Authentication -- Popout tokens are one of the 5 auth types in Lumio. When a request includes a token query parameter, the auth middleware hashes it and looks it up in the database. The token's permissions are used for authorization.

Frontend

  • Token management page listing all tokens with their labels, permissions, and prefixes.
  • Token creation form with permission selector and optional user/label fields.
  • Copy-to-clipboard for the full token at creation (shown only once).
  • Overlay popout URLs use format: /overlay/[key]?token=xxx.

API

GraphQL Queries

QueryPermissionDescription
popoutTokenstokens:readList all popout tokens for the account. Shows id, account_id, user_id, token_prefix, label, permissions, created_at. Never exposes the token hash.

GraphQL Mutations

MutationPermissionDescription
createPopoutToken(input: CreatePopoutTokenInput)tokens:createCreate a new popout token. Returns the full token string (shown only once) and the token metadata. If no user_id is provided, binds to the authenticated user.
updatePopoutToken(input: UpdatePopoutTokenInput)tokens:editUpdate a token's label, permissions, or user binding. Uses double-option semantics: absent = don't change, null = clear, value = set.
deletePopoutToken(id: UUID)tokens:deleteDelete/revoke a popout token. Verifies account ownership.

REST Endpoints

All paths live under /v1/tokens. Bodies are snake_case and mirror the GraphQL inputs. The full token string is returned only from POST /v1/tokens and cannot be retrieved later.

MethodPathPermissionDescription
GET/v1/tokenstokens:readList popout tokens for the account (hash never exposed)
POST/v1/tokenstokens:createCreate a popout token; returns full token once
PATCH/v1/tokens/{id}tokens:editUpdate label, permissions, or user binding
DELETE/v1/tokens/{id}tokens:deleteRevoke a popout token
GET/v1/tokens/meAuthReturn the permissions granted to the current token (self-introspection)

Input Types

CreatePopoutTokenInput:

FieldTypeDescription
labelString?Human-readable label for the token
permissions[String]List of permission strings the token grants
userIdUUID?User to bind the token to (defaults to authenticated user)

UpdatePopoutTokenInput:

FieldTypeDescription
idUUIDToken ID to update
labelOption<Option<String>>Double-option: absent = no change, null = clear, value = set
permissionsOption<[String]>Replace permissions list (absent = no change)
userIdOption<Option<UUID>>Double-option: absent = no change, null = unassign, value = assign

Permissions

PermissionDescription
tokens:readView popout tokens (prefix, label, permissions)
tokens:createCreate new popout tokens
tokens:editUpdate token label, permissions, user binding
tokens:deleteDelete/revoke popout tokens

Database

TableDatabaseDescription
popout_tokensPostgreSQLid, account_id, user_id (nullable), token_hash (bcrypt/sha256 hash), token_prefix (first N chars for display), label, permissions (text array), created_at

Security

  • The full token is generated using generate_popout_token() from lo-auth.
  • Only the hash is stored in the database; the full token cannot be recovered.
  • The full token is returned exactly once at creation time.
  • The token_prefix (first few characters) is stored for identification in the UI.
  • Tokens are non-expiring by design (for OBS browser sources that run unattended).

Data Flow

  1. User creates a popout token, selecting which permissions it should grant.
  2. generate_popout_token() creates a random token, computes its hash, and extracts a prefix.
  3. The hash, prefix, permissions, and metadata are stored in popout_tokens.
  4. The full token is returned to the user (shown once, must be copied).
  5. User adds the token to an OBS browser source URL: /overlay/[key]?token=xxx.
  6. When the overlay loads, the auth middleware:
    • Extracts the token query parameter.
    • Hashes it and looks up the hash in popout_tokens.
    • If found, authenticates the request with the token's account_id, user_id, and permissions.

Key Files

PathDescription
apps/api/src/graphql/tokens.rsGraphQL queries and mutations
apps/api/src/db/tokens.rsDatabase operations for token CRUD
crates/lo-auth/src/popout_token.rsToken generation (random token, hash, prefix)
crates/lo-auth/src/Popout token authentication type