Skip to main content

Token & Session Lifecycle

How Curupira issues, refreshes, validates, and ends tokens and sessions. This is the reference for resource-server authors and anyone integrating beyond the basic login flow — for the initial wiring see Add login to your app.

The OpenID discovery document is the authoritative source for every endpoint and supported capability — fetch it rather than hard-coding.

Tokens at a glance

TokenFormatDefault lifetimePurpose
Access tokensigned JWT1 hour (DEFAULT_ACCESS_TTL_SECS, 3600)Sent to your APIs; carries sub and the permissions claim.
Refresh tokenopaque string30 days (DEFAULT_REFRESH_TTL_MINS, 43200)Exchanged for a new access token; rotated on every use.
ID tokensigned JWT (OIDC)issued with the access tokenIdentity of the user for the client app.

Lifetimes are deployment-configurable; always trust the expires_in in the token response rather than assuming a fixed value.

Getting tokens (authorization code grant)

Tokens are issued at POST /oauth2/token:

  • Public clients (SPAs, mobile) use PKCE — no client secret.
  • Confidential clients (server-side) authenticate with their API key.

The full authorize → callback → token exchange is covered in Add login to your app.

Refreshing an access token

When the access token expires, exchange the refresh token for a new one:

curl -X POST https://auth.curupira.api.br/oauth2/token \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'grant_type=refresh_token' \
-d 'refresh_token=<current-refresh-token>' \
-d 'client_id=<your-client-id>' \
-d 'api_key=<your-api-key>'

:::warning Refresh tokens are rotated Every refresh returns a new refresh token and invalidates the one you just used. Your client must store the new refresh token from each response and discard the old one — reusing a consumed refresh token fails. Rotation limits the damage of a leaked token. (The refresh_token grant requires the application's API key, so it's for confidential clients.) :::

An unsupported grant_type returns unsupported_grant_type; an invalid/expired refresh token returns invalid_grant.

Validating tokens (resource servers)

Your API can validate Curupira access tokens two ways:

Fetch Curupira's public signing keys from the JWKS endpoint and verify the token's signature, exp, iss, and aud, then authorize on the permissions claim:

GET https://auth.curupira.api.br/.well-known/jwks.json

Each application signs with its own key, identified by the token header's kid — select the matching key from the JWKS by kid. Fetch the JWKS dynamically and cache it (don't pin a single key): signing keys can rotate, and a client that hard-codes a key breaks on rotation.

2 · Token introspection (RFC 7662)

For opaque validation, ask Curupira directly:

curl -X POST https://auth.curupira.api.br/oauth2/introspect \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'token=<access-token>'

The response is always 200; an unknown, expired, or malformed token is reported as {"active": false} (per spec), and a valid one as {"active": true, …} with its claims. Prefer local JWT verification for hot paths — introspection adds a network round-trip per check.

Ending a session

GET https://auth.curupira.api.br/oauth2/logout

Terminates the user's Curupira session. If you registered post-logout redirect URIs for the application, you can return the user to one of them afterwards (see Tenant Onboarding for registering them).

User info

GET /oauth2/userinfo (with a valid access token) returns the standard OIDC claims for the authenticated user — useful when you want profile data without decoding the ID token yourself.

Next steps