Platform
ScaiWave ScaiGrid ScaiCore ScaiBot ScaiDrive ScaiKey Modellen Tools & Services
Oplossingen
Organisaties Ontwikkelaars Internet Service Providers Managed Service Providers AI-in-a-Box
Kenniscentrum
Ondersteuning Documentation Blog Downloads
Bedrijf
Over ons Onderzoek Vacatures Investeren Contact
Inloggen

Authenticate with ScaiKey

json
1
2
3
4
5
6
{
  "title": "Authenticate with ScaiKey",
  "audience": "developer",
  "summary": "OIDC sign-in, RFC 8693 token exchange, refresh tokens, and the mock-mode escape hatch.",
  "sort_order": 1
}

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. Set SCAIWAVE_AUTH_MODE=mock.
  • scaikey — production. ScaiWave validates tokens against ScaiKey's JWKS and resolves the tenant from the tenant_id claim.
  • hybrid — accepts both. Used in some staging environments where ScaiKey isn't always available.

Sign in (browser flow)#

The OIDC authorization-code flow:

  1. Client redirects to https://scaikey.<your-host>/auth?…&redirect_uri=<scaiwave-host>/v1/auth/login.
  2. User authenticates with ScaiKey.
  3. ScaiKey redirects back to ScaiWave with a code.
  4. ScaiWave POSTs code to /v1/auth/login, which exchanges it for {access_token, refresh_token, expires_in}.
  5. 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:

bash
1
2
3
4
5
curl -X POST "$BASE/v1/auth/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=password" \
  -d "username=$USER" \
  -d "password=$PASS"

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:

bash
1
2
3
curl -X POST "$BASE/v1/auth/refresh" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "refresh_token=$REFRESH"

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:

bash
1
2
curl -X POST "$BASE/v1/auth/register" \
  -H "Authorization: Bearer $TOKEN"

This:

  1. Validates the token with ScaiKey.
  2. Creates a Participant row in ScaiWave's DB if one doesn't exist.
  3. Auto-creates a personal workspace for the new user.
  4. 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:

  1. ScaiWave POSTs to ScaiKey's /oauth/token with:
    • grant_type=urn:ietf:params:oauth:grant-type:token-exchange
    • subject_token=<user's ScaiWave token>
    • subject_token_type=urn:ietf:params:oauth:token-type:access_token
    • requested_token_type=urn:ietf:params:oauth:token-type:jwt
    • audience=scaidrive (or scaivault, etc.)
  2. ScaiKey returns a new JWT scoped to the requested audience.
  3. 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:

python
1
token = await token_exchange.get_service_token(tenant_id)

What's in the JWT#

Decode any ScaiWave token (don't verify) to see the claims:

json
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{
  "sub": "<user-id-on-scaikey>",
  "tenant_id": "<scaikey-tenant-id>",
  "aud": "scaiwave",
  "preferred_username": "alice",
  "email": "alice@example.com",
  "roles": ["docs_internal", "tenant_admin"],
  "groups": ["group-eng", "group-sre"],
  "exp": 1778939467,
  "iat": 1778935867
}

ScaiWave's TenantMiddleware reads tenant_id on every request and resolves it to a Tenant row.

Where to go next#

Updated 2026-05-27 22:31:33 View source (.md) rev 2