NKAMA does not issue its own tokens. It is an OAuth2 resource server: it trusts access
tokens issued by Keycloak (the nkama realm at https://auth.yvnn.is). Every secured
endpoint expects:
Authorization: Bearer <access_token>
What the API validates
- Signature & issuer — the token must be signed by the realm and carry
iss = https://auth.yvnn.is/realms/nkama.
- Audience — the token must include the
nkama-api audience (the nkama-mobile
client injects it).
- Expiry — expired tokens are rejected; refresh and retry.
- Roles — realm roles (
TENANT, OWNER, MANAGER, ADMIN) become Spring authorities
and gate endpoints. org_id scopes data to one organization.
Clients
| Client | Type | Use |
|---|
nkama-mobile | public, PKCE | Mobile/web apps. Authorization Code + PKCE. |
nkama-api | bearer-only | The audience the resource server requires. |
nkama-admin | confidential | Backend-only service account (not for app clients). |
Mobile / web (Authorization Code + PKCE)
discovery = https://auth.yvnn.is/realms/nkama/.well-known/openid-configuration
client_id = nkama-mobile
scopes = openid profile email
pkce = S256 (required)
Run the standard PKCE flow against the discovery document; the resulting access token is the
bearer you send to the API.
Scripts / testing
For non-interactive testing you can exchange credentials at the token endpoint directly:
ACCESS_TOKEN=$(curl -s \
-d grant_type=password -d client_id=nkama-mobile -d scope=openid \
--data-urlencode username="+241…" \
--data-urlencode password="<password>" \
https://auth.yvnn.is/realms/nkama/protocol/openid-connect/token | jq -r .access_token)
The password grant (ROPC) is for testing only and is disabled on the production client.
Real apps must use Authorization Code + PKCE.
Public (no token) endpoints
Sign-up (POST /api/v1/registration/**), password reset
(POST /api/v1/account/password-reset/**), the legal versions
(GET /api/v1/legal/**) and vendor webhooks (POST /webhooks/**) are deliberately
pre-authentication. Everything else requires a bearer token.