Authentication
Ledgerline uses session-based authentication with HTTP-only cookies. Two auth flows are supported: Discord OAuth 2.0 and local credential-based login.
Prerequisites
- Network access to the Ledgerline instance.
- For Discord OAuth: a Discord account and a configured Discord auth provider on the target tenant.
- For local auth:
LOCAL_AUTH_ENABLED=trueon the server and valid credentials.
Session Cookie
All authenticated API requests use the ledgerline_auth cookie. This cookie is:
- HTTP-only — not accessible via JavaScript.
- Signed — tamper-resistant.
- Cache-backed — session state is stored server-side (Valkey/Redis in production, in-memory in dev).
- TTL — 8 hours by default, or matching the Discord token lifetime for OAuth sessions.
Discord OAuth Flow
1. Initiate the flow
GET /auth/discord/start?tenantId=default&mode=redirect&postLoginRedirect=/admin
| Parameter | Required | Description |
|---|---|---|
tenantId | Yes | Target tenant (also accepted via x-tenant-id header). |
mode | No | json (default) returns { authorizationUrl, state }. redirect issues a 302 to Discord. |
redirectUri | No | Override the OAuth redirect URI. Falls back to server config. |
postLoginRedirect | No | Relative path to redirect to after login completes. |
JSON response (mode=json):
{
"authorizationUrl": "https://discord.com/oauth2/authorize?client_id=...&state=...",
"state": "opaque-state-token"
}
2. Handle the callback
After the user authorizes, Discord redirects to:
GET /auth/discord/callback?code=AUTHORIZATION_CODE&state=OPAQUE_STATE
The server:
- Validates and consumes the one-time state token.
- Exchanges the authorization code for Discord tokens.
- Creates or retrieves a user and identity record.
- Issues a session cookie.
- Returns JSON
{ session }or redirects topostLoginRedirect.
First-user bootstrap: The first Discord user to log in for a tenant is automatically granted the TenantAdmin role.
Local Auth Flow
Login with credentials
POST /auth/local/login
Content-Type: application/json
{
"username": "admin",
"password": "secret",
"tenantId": "default"
}
Response (200):
{
"session": {
"sessionId": "...",
"user": {
"userId": "...",
"tenantId": "default",
"displayName": "...",
"email": "..."
},
"permissionKeys": ["TenantAdmin"]
}
}
The session user includes displayName and email for UI display. The ledgerline_auth cookie is set on the response.
Check Current Session
GET /auth/me
Cookie: ledgerline_auth=...
Response (200):
{
"session": {
"sessionId": "...",
"user": { "userId": "...", "tenantId": "default" },
"permissionKeys": ["TenantAdmin"]
}
}
Returns 401 if no active session.
Switch Tenant
POST /auth/tenant
Content-Type: application/json
Cookie: ledgerline_auth=...
{
"tenantId": "other-tenant"
}
Re-issues the session with permissions for the new tenant.
Logout
POST /auth/logout
Cookie: ledgerline_auth=...
Clears the session cookie and removes the server-side session record.
For redirect-based logout (e.g., from a browser):
GET /auth/logout?redirectTo=/
Error Responses
| Status | Condition |
|---|---|
400 | Missing parameters, invalid credentials, provider misconfiguration. |
401 | No active session (for endpoints requiring auth). |
404 | Auth provider not found for the given tenant. |