Skip to main content

Connections

Overview

The Connections module manages platform OAuth connections for Lumio accounts. It handles two layers of credentials: app credentials (client_id/client_secret for the user's own Twitch/YouTube/Spotify/etc. app) and channel connections (OAuth access/refresh tokens for the specific channel). The module supports the full OAuth flow with PKCE, CSRF protection via Redis-stored state parameters, automatic token refresh, and encrypted credential storage. All secrets are encrypted at rest using AES-256 derived from a master key.

Architecture

Backend

  • GraphQL (apps/api/src/graphql/connections.rs) -- Queries for listing credentials, channel connections, and connection status. Mutations for saving/deleting credentials, initiating OAuth authorization, and disconnecting channels.
  • REST (apps/api/src/routes/connections.rs) -- REST endpoints for credential CRUD, channel connection listing, OAuth authorization initiation, OAuth callback handling, and code exchange. REST routes provide the full OAuth flow with redirect handling.
  • Crypto (apps/api/src/crypto.rs) -- AES-256 encryption/decryption for client_id, client_secret, access_token, and refresh_token. Key derived from config.auth.token_encryption_key.
  • Platforms (apps/api/src/platforms.rs) -- Platform registry with OAuth configuration (authorize URL, token URL, scopes, extra params) per platform. SUPPORTED_PLATFORMS constant and is_valid_platform() validation.

Frontend

  • Connection settings page with cards per platform showing connection status.
  • App credential form for entering client_id/client_secret.
  • "Connect" button initiates OAuth flow, redirects to platform, callback saves tokens.

API

GraphQL Queries

QueryPermissionDescription
appCredentialsconnections:readList app credentials for the account. Secrets are masked; only last 4 chars of client_id shown as hint.
channelConnectionsconnections:readList channel connections for the account. Tokens are never exposed. Shows platform, channel ID, channel name, scopes, expiry.
connectionStatusesconnections:readConnection status overview for all supported platforms (has_credentials, is_connected, channel_name, enabled)
enabledProviders(connectionType)Public (no auth)List enabled provider slugs for a given connection type (login, channel, or bot)

GraphQL Mutations

MutationPermissionDescription
saveAppCredentials(input: SaveCredentialsInput)connections:createSave (upsert) app credentials for a platform. Encrypts client_id and client_secret before storage.
deleteAppCredentials(platform)connections:deleteDelete app credentials and disconnect the channel connection
authorizeChannel(platform)connections:createStart OAuth authorization flow. Generates a state parameter (stored in Redis with 10-min TTL), builds the authorization URL with scopes, and returns the redirect URL.
disconnectChannel(platform)connections:deleteDisconnect a channel connection (keeps app credentials)

REST Endpoints

MethodPathPermissionDescription
GET/v1/connections/credentialsconnections:readList all app credentials
PUT/v1/connections/credentials/{platform}connections:createSave app credentials
DELETE/v1/connections/credentials/{platform}connections:deleteDelete app credentials + stop platform worker
GET/v1/connections/channelconnections:readList all channel connections
DELETE/v1/connections/channel/{platform}connections:deleteDisconnect a channel
GET/v1/connections/channel/{platform}/authorizeconnections:createInitiate OAuth flow (redirect URL)
GET/v1/connections/channel/{platform}/callback--OAuth callback handler
POST/v1/connections/channel/{platform}/exchange--Exchange OAuth code for tokens

Permissions

PermissionDescription
connections:readView credentials (masked) and channel connections
connections:createSave app credentials, initiate OAuth flows
connections:editEdit connection settings
connections:deleteDelete credentials, disconnect channels

Database

TableDatabaseDescription
app_credentialsPostgreSQLid, account_id, platform, client_id (encrypted), client_secret (encrypted), created_at, updated_at. Unique on (account_id, platform).
channel_connectionsPostgreSQLid, account_id, platform, platform_channel_id, channel_name, access_token (encrypted), refresh_token (encrypted), scopes (text array), expires_at, created_at, updated_at. Unique on (account_id, platform).

Data Flow

OAuth Connection Flow

  1. User enters app credentials (client_id/client_secret) for a platform.
  2. Credentials are encrypted with AES-256 and stored in app_credentials.
  3. User clicks "Connect" which calls authorizeChannel(platform).
  4. Server generates a CSRF state parameter (account_id:uuid), stores it in Redis with 10-minute TTL.
  5. Server builds the OAuth authorization URL with the platform's authorize endpoint, scopes, redirect URI, and state.
  6. Client redirects to the platform's authorization page.
  7. User authorizes on the platform. Platform redirects back to the callback URL with code and state.
  8. Callback handler validates the state against Redis, exchanges the code for tokens via the platform's token endpoint.
  9. Access token and refresh token are encrypted and stored in channel_connections.
  10. Platform worker is started for the new connection.

Security

  • Encryption at rest: All client_id, client_secret, access_token, and refresh_token values are encrypted using AES-256 with a key derived from config.auth.token_encryption_key.
  • CSRF protection: OAuth state parameter stored in Redis with 10-minute TTL.
  • Secret masking: Client secrets and tokens are never exposed in API responses. Only a 4-character hint of the client_id is shown.
  • Spotify localhost workaround: For development, Spotify requires 127.0.0.1 instead of localhost in redirect URIs.

Key Files

PathDescription
apps/api/src/graphql/connections.rsGraphQL queries and mutations
apps/api/src/routes/connections.rsREST endpoints including OAuth callback/exchange
apps/api/src/crypto.rsAES-256 encryption/decryption utilities
apps/api/src/platforms.rsPlatform OAuth configuration registry
apps/api/src/db/connections.rsDatabase operations for credentials and connections