Authentication and API Keys
Koldan uses token-based authentication for all API requests. Every call to a protected endpoint must include a valid credential in the request headers. Two authentication methods are supported: OAuth2 Bearer tokens (JWT) and API Keys.
Authentication Methods
OAuth2 Bearer Token (JWT)
Interactive applications (web UIs, desktop clients) authenticate using OAuth2 / OpenID Connect. After a user signs in through the configured identity provider, the client receives a JWT access token.
Include the token in the Authorization header:
GET /api/v1/transcriptions HTTP/1.1
Host: koldan.dixilang.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
Token lifecycle
- Tokens are issued and refreshed through your identity provider (IdP).
- Koldan validates the token signature against the tenant's OIDC JWK set - it never stores passwords.
- When a token expires, use the refresh-token flow to obtain a new one.
API Key
For server-to-server integrations, scripts, and automated workflows, API Keys provide a simpler alternative that doesn't require an OAuth flow.
Include the key in the X-API-Key header:
POST /api/v1/transcriptions HTTP/1.1
Host: koldan.dixilang.com
X-API-Key: kk-a1b2c3d4e5f6g7h8i9j0...
API Key takes priority
If both Authorization and X-API-Key headers are present in the same request, the API Key is used and the Bearer token is ignored.
Authentication API Reference
Authenticate users with the Koldan platform via username/password credentials, OAuth 2.0 authorization codes (with optional PKCE), token refresh, and logout. All endpoints delegate to the configured OIDC identity provider and return standard OAuth 2.0 token responses.
Public - No authentication required
Base path: /api/v1/auth
Integrating without user interaction?
If you are building a service-to-service integration, a background worker, or any automation that runs without a user present, skip the authentication flows below and use an API Key instead. API Keys are long-lived, do not require token refresh, and can be scoped to specific permissions - making them the recommended approach for non-interactive access to the Koldan API.
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/v1/auth/exchange-auth-code |
Exchange Authorization Code |
POST |
/api/v1/auth/refresh-token |
Refresh Token |
POST |
/api/v1/auth/logout |
Logout |
POST |
/api/v1/auth/login |
Login |
Login
POST /api/v1/auth/login
Deprecated
The direct login endpoint is deprecated and will be removed in a future release.
Use instead:
- Authorization Code flow with PKCE - for browser-based and mobile applications where a user is present.
- API Keys - for service-to-service integrations, background workers, and automations that run without user interaction.
Authenticate a user with username and password credentials. On success, the OIDC provider issues an OAuth 2.0 token set (access token, refresh token, and optional ID token). The access token should be used as a Bearer token in subsequent API requests.
LoginRequest
| Field | Type | Required | Description |
|---|---|---|---|
username |
string |
Yes | The user's username. |
password |
string |
Yes | The user's password. |
OAuthTokenResponse
| Field | Type | Nullable | Description |
|---|---|---|---|
access_token |
string |
No | The JWT access token used for authenticating API requests. |
refresh_token |
string |
No | The refresh token used to obtain a new access token when the current one expires. |
token_type |
string |
No | The token type. Always "Bearer". |
expires_in |
integer |
No | Number of seconds until the access token expires. |
id_token |
string |
Yes | The OIDC ID token containing user identity claims. May be null depending on the provider configuration. |
scope |
string |
Yes | Space-separated list of scopes granted to the token. |
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 300,
"id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"scope": "openid profile email"
}
| Status | Description |
|---|---|
200 OK |
Authentication successful. The response body contains the OAuth 2.0 token set. |
401 Unauthorized |
Invalid credentials. Check username and password. |
409 Conflict |
User data conflict (e.g., duplicate username detected during post-login synchronization). |
503 Service Unavailable |
The identity provider is unreachable. Retry later. |
Logout
POST /api/v1/auth/logout
Log out a user by invalidating their refresh token with the OIDC provider. After logout, the refresh token can no longer be used to obtain new access tokens.
LogoutRequest
| Field | Type | Required | Description |
|---|---|---|---|
refreshToken |
string |
Yes | The refresh token to invalidate. |
Response
| Status | Description |
|---|---|
200 OK |
User successfully logged out. The refresh token has been invalidated. |
400 Bad Request |
Logout failed. The refresh token may be invalid, already expired, or the identity provider rejected the request. |
Exchange Authorization Code
POST /api/v1/auth/exchange-auth-code
Exchange an OAuth 2.0 authorization code for an access token using the Authorization Code with PKCE flow. The codeVerifier is required to complete the token exchange securely.
When to use this endpoint
Use this endpoint after completing an OAuth 2.0 / OIDC authorization flow in a browser or mobile application. The authorization server redirects the user back to your application with a code parameter - send that code here to obtain tokens.
UnifiedLoginRequest
| Field | Type | Required | Description |
|---|---|---|---|
code |
string |
Yes | The authorization code received from the OIDC provider's authorization endpoint. |
redirectUri |
string |
Yes | The redirect URI that was used in the original authorization request. Must match exactly. |
codeVerifier |
string |
Yes | The PKCE code verifier. Must match the code_challenge sent in the original authorization request. |
{
"code": "abc123-authorization-code",
"redirectUri": "https://app.example.com/callback",
"codeVerifier": "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
}
OAuthTokenResponse
Returns an OAuthTokenResponse object (see field table in Login).
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 300,
"id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"scope": "openid profile email"
}
| Status | Description |
|---|---|
200 OK |
Token exchange successful. The response body contains the OAuth 2.0 token set. |
401 Unauthorized |
Token exchange failed. The authorization code may be invalid, expired, or the redirect URI does not match. |
409 Conflict |
User data conflict during post-login synchronization. |
503 Service Unavailable |
The identity provider is unreachable. Retry later. |
Refresh Token
POST /api/v1/auth/refresh-token
Refresh an expired access token using a valid refresh token. Returns a new OAuth 2.0 token set with a fresh access token.
Token Lifecycle
Access tokens are short-lived (typically 5 minutes). Use this endpoint to seamlessly obtain a new access token without requiring the user to re-authenticate. The refresh token itself may also be rotated - always store the latest refresh_token from the response.
RefreshTokenRequest
| Field | Type | Required | Description |
|---|---|---|---|
refreshToken |
string |
Yes | The refresh token obtained from a previous login or token refresh response. |
OAuthTokenResponse
Returns an OAuthTokenResponse object (see field table in Login).
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 300,
"id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"scope": "openid profile email"
}
| Status | Description |
|---|---|
200 OK |
Access token successfully refreshed. The response body contains the new OAuth 2.0 token set. |
400 Bad Request |
Failed to refresh access token. The refresh token may be invalid, expired, or already revoked. |
Authentication Flows
Koldan supports multiple authentication flows depending on your application type:
Direct Login (Resource Owner Password)
The simplest flow for server-side applications and scripts. Send the user's credentials directly to the Login endpoint.
sequenceDiagram
participant C as Client
participant K as Koldan API
participant O as OIDC Provider
C->>K: POST /api/v1/auth/login<br>{ username, password }
K->>O: Token Request
O-->>K: OAuthTokenResponse
K-->>C: 200 OK { access_token, … }
Authorization Code Flow with PKCE
Recommended for browser-based and mobile applications. The user authenticates directly with the OIDC provider and your application exchanges the resulting authorization code for tokens using PKCE.
sequenceDiagram
participant U as User
participant A as Client App
participant O as OIDC Provider
participant K as Koldan API
U->>A: Login
A->>A: Generate code_verifier & code_challenge
A->>O: Redirect to /authorize<br>with code_challenge
U->>O: Authenticate with provider
O-->>U: Authenticated
O-->>A: Redirect with ?code=abc123
A->>K: POST /api/v1/auth/exchange-auth-code<br>{ code, redirectUri, codeVerifier }
K-->>A: 200 OK { access_token, … }
PKCE is Required
All clients must use PKCE when using the Authorization Code flow. Generate a code_verifier and code_challenge before the authorization request, then include the codeVerifier when calling the Exchange Authorization Code endpoint.
Using Tokens
Once authenticated, include the access token in the Authorization header of all API requests:
For API key authentication as an alternative to Bearer tokens, see API Keys.
How API Keys Work
Key Format
All API keys share a recognizable format:
| Part | Example | Description |
|---|---|---|
| Prefix | kk- |
Fixed prefix that identifies the string as a Koldan API key |
| Secret | a1b2c3d4e5... (64 characters) |
Randomly generated secret, hashed before storage |
Full key example: kk-a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0e1f2
Store your key immediately
The plain-text key is returned only once - at creation time. Koldan stores a one-way hash and cannot retrieve the original key. If you lose it, revoke the old key and create a new one.
Key Lifecycle
When creating an API key, you provide a name and optionally a description, expiration date, and a set of scopes. The full API key is shown once in the response - store it securely.
An API key can be in one of three states:
| State | Can Authenticate? | Description |
|---|---|---|
| Active | Key is valid and has not expired or been revoked | |
| Expired | Current time is past the key's expiration date | |
| Revoked | Key was explicitly revoked by the user or an administrator |
Revocation is permanent - once revoked, the key can never be used again.
For the full API reference on creating, listing, and revoking API keys, see API Keys API.
Scoped Permissions
How Scopes Work
Scopes control what an API key is allowed to do. There are two types of API keys:
- Scoped key - created with an explicit list of scopes. The requested scopes must be a subset of the creator's own role permissions. At runtime, the key's effective permissions are the intersection of its scopes and the user's current role permissions.
- Identity-only key - created without specifying scopes. The key provides authentication only (identifies the caller) but carries no authorization scopes. Requests to endpoints that require a specific scope will be rejected with
403 Forbidden.
For the complete list of available scopes and how they map to roles, see Roles and Permissions.
Identity-only keys have no permissions
If you create an API key without specifying any scopes, it will not inherit your role's permissions. It will only authenticate your identity. Always specify the scopes your integration needs.
Koldan Authentication Best Practices
- Use scoped keys - always limit API keys to the minimum scopes required for the task.
- Set expiration dates - prefer short-lived keys for CI/CD pipelines and automated scripts.
- Rotate regularly - create a new key, update your integration, then revoke the old one.
- Never commit keys - store API keys in environment variables or a secrets manager, not in source code.
- One key per integration - if a key is compromised, you can revoke it without affecting other systems.
- Use Bearer tokens for interactive apps - API Keys are designed for non-interactive, server-side use cases.
- Never store tokens in local storage. Use secure, HTTP-only cookies or in-memory storage to protect tokens from XSS attacks.
- Refresh proactively. Refresh the access token before it expires to avoid request failures. Use the
expires_infield to schedule refreshes. - Handle token rotation. Always store the latest
refresh_tokenfrom each response - the OIDC provider may rotate refresh tokens. - Always use PKCE. The Authorization Code flow requires PKCE - generate a
code_verifierandcode_challengefor every authorization request. - Invalidate tokens on logout. Always call the Logout endpoint to revoke refresh tokens when a user signs out.
- Protect credentials in transit. All authentication requests must be made over HTTPS.
- Prefer authorization code flow. When possible, use the Exchange Authorization Code flow instead of direct login to avoid handling user passwords.