Authenticate with ScaiKey
ScaiWave uses ScaiKey (the ScaiLabs OIDC provider) for identity. Every authenticated request to ScaiWave carries a Bearer token; the token is a ScaiKey-issued JWT whose claims tell ScaiWave who you are and what tenant you belong to.
The auth modes#
ScaiWave supports three:
mock— for dev. ScaiWave returns a hard-coded test user; every Bearer token is accepted as long as it's non-empty. SetSCAIWAVE_AUTH_MODE=mock.scaikey— production. ScaiWave validates tokens against ScaiKey's JWKS and resolves the tenant from thetenant_idclaim.hybrid— accepts both. Used in some staging environments where ScaiKey isn't always available.
Sign in (browser flow)#
The OIDC authorization-code flow:
- Client redirects to
https://scaikey.<your-host>/auth?…&redirect_uri=<scaiwave-host>/v1/auth/login. - User authenticates with ScaiKey.
- ScaiKey redirects back to ScaiWave with a
code. - ScaiWave POSTs
codeto/v1/auth/login, which exchanges it for{access_token, refresh_token, expires_in}. - The client stores the tokens (HttpOnly cookie or memory),
includes the access token as
Authorization: Bearer <jwt>on every subsequent request.
The POST /v1/auth/login endpoint is the only place the code is
exchanged. Don't call ScaiKey directly from the browser.
Get a token programmatically#
For automation:
1 2 3 4 5 | |
Returns {access_token, refresh_token, expires_in, token_type}.
Not available in all deployments. Some tenants disable the password grant entirely and require browser OAuth. Check with your admin.
Refresh#
Access tokens have a short TTL (default 1 hour). When ~80% of TTL has elapsed, refresh proactively:
1 2 3 | |
Returns a new access token (and a rotated refresh token if your config has rotation enabled).
The ScaiWave web client refreshes automatically when it detects 80% of TTL has passed; you don't need to do anything in the browser.
Register#
A first-time user needs to be registered against ScaiWave:
1 2 | |
This:
- Validates the token with ScaiKey.
- Creates a
Participantrow in ScaiWave's DB if one doesn't exist. - Auto-creates a personal workspace for the new user.
- Returns the participant id.
The web client calls this once after first sign-in. Automation should call it idempotently before its first real request.
Token exchange (RFC 8693)#
Some plugins (ScaiDrive, ScaiVault, etc.) need a different token scope. ScaiWave uses RFC 8693 token exchange:
- ScaiWave POSTs to ScaiKey's
/oauth/tokenwith:grant_type=urn:ietf:params:oauth:grant-type:token-exchangesubject_token=<user's ScaiWave token>subject_token_type=urn:ietf:params:oauth:token-type:access_tokenrequested_token_type=urn:ietf:params:oauth:token-type:jwtaudience=scaidrive(orscaivault, etc.)
- ScaiKey returns a new JWT scoped to the requested audience.
- ScaiWave caches it in Redis (TTL =
expires_in - 30s).
This happens automatically inside TokenExchangeService — you
won't call it directly unless writing a new plugin.
Service tokens (background tasks)#
Background workers (indexers, scheduled summaries) have no
end-user context, so they mint a service token via
client_credentials grant. As of 2026-05-16, ScaiKey populates
sub=client_id on these tokens so ScaiGrid accepts them — see
docs/integrations/scaigrid/service-token-401-response.md
in the source repo for the history.
You access these via:
1 | |
What's in the JWT#
Decode any ScaiWave token (don't verify) to see the claims:
1 2 3 4 5 6 7 8 9 10 11 | |
ScaiWave's TenantMiddleware reads tenant_id on every request
and resolves it to a Tenant row.