Authentication
Token exchange flow, API key usage, ID token requirements, and IdP-specific notes.
Rialto uses a token exchange pattern: your users authenticate with your identity provider, and you exchange their ID token for Rialto tokens.
Token Exchange Flow
User ──► Your App ──► Your IdP ──► ID Token
│
POST /identity/auth/exchange
X-API-Key: rialto_ak_...
{ "token": "<id_token>" }
│
▼
Rialto Identity
(validates JWT via OIDC/JWKS)
│
▼
{ access_token, refresh_token }- User authenticates with your IdP
- Your backend obtains the user's ID token
- Your backend calls
POST /identity/auth/exchangewith the ID token and yourX-API-Key - Rialto validates the JWT signature via OIDC discovery and JWKS
- User is auto-created on first exchange (linked by email)
- Rialto returns an
access_tokenandrefresh_token
API Key
Your API key identifies your organization on every token exchange request.
| Detail | Value |
|---|---|
| Header | X-API-Key |
| Format | rialto_ak_<org_id>_<random> |
| Issued | Once, during IdP registration |
| Retrievable | No -- store it securely at issuance |
The API key identifies your organization. The ID token identifies the user. Both are required.
Token Exchange Request
curl -X POST https://api.rialto.com/identity/auth/exchange \
-H "Content-Type: application/json" \
-H "X-API-Key: <your_api_key>" \
-d '{ "token": "<id_token>" }'Response (200):
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 300,
"refresh_token": "eyJhbGciOiJIUzI1NiIs...",
"refresh_expires_in": 1800,
"scope": "openid email profile aud:identity aud:primary-issuance"
}Rialto Token Claims
The access_token includes these custom claims:
| Claim | Type | Description |
|---|---|---|
org_id | string (UUID) | Your organization ID |
org_name | string | Your organization name |
user_id | string (UUID) | Rialto user ID |
email | string | User's email |
onboarding_completed | boolean | User finished onboarding |
kyc_completed | boolean | User passed KYC verification |
accredited | boolean | User is accredited |
accreditation_verified | boolean | Accreditation has been verified |
Use these claims to gate UI features. For example, check kyc_completed and accredited before allowing a user to subscribe to offerings.
Token Lifetimes
| Token | Lifetime | What to do when expired |
|---|---|---|
| Access token | ~5 minutes | Use the refresh token |
| Refresh token | ~30 minutes | Exchange a fresh ID token from your IdP |
ID Token Requirements
Your ID token must meet these requirements:
| Requirement | Detail |
|---|---|
| Format | JWT (three dot-separated base64 parts) |
sub claim | Required -- the user's external ID |
| Email claim | Required -- one of: email, preferred_username, or upn |
iss claim | Must match the IdP URL registered for your organization |
| Signature | Valid against your IdP's JWKS (fetched via OIDC discovery) |
| Expiry | Must not be expired |
IdP-Specific Notes
AWS Cognito
Use the ID token (token_use: "id"), not the access token (token_use: "access"). Cognito access tokens do not contain the email claim.
- Issuer URL:
https://cognito-idp.<region>.amazonaws.com/<user-pool-id> - The token exchange endpoint will detect access tokens and include a hint in the error message
Auth0
Auth0 ID tokens are always JWTs and include email by default.
- Issuer URL:
https://<tenant>.auth0.com/ - Ensure the
emailscope is requested during authentication
Okta
- Issuer URL:
https://<org>.okta.com/oauth2/default - If using a custom authorization server, the issuer URL will differ
Azure AD / Entra ID
- Issuer URL:
https://login.microsoftonline.com/<tenant-id>/v2.0 - Ensure the
emailclaim is included (may require configuring optional claims)
- Issuer URL:
https://accounts.google.com - ID tokens always include
emailwhen theemailscope is requested
Audience Scopes
When your organization was registered, audience scopes were configured (default: ["identity"]). The exchanged Rialto token includes these as aud:<scope> in the scope field:
scope: "openid email profile aud:identity aud:primary-issuance"To update your audience scopes, contact Rialto staff or use:
PATCH /identity/auth/idp/<org_id>
{ "audience": ["identity", "primary-issuance", "custom-scope"] }The "identity" audience is always included.
Error Reference
| Error Code | Status | Description | Fix |
|---|---|---|---|
missing_api_key | 401 | X-API-Key header not provided | Add the X-API-Key header |
invalid_api_key | 401 | Key is invalid or does not match an org | Check the key value; contact Rialto if lost |
invalid_token | 400 | Token is not a JWT, or missing sub/email | Use an ID token with required claims |
invalid_signature | 401 | JWT signature verification failed | Ensure token is freshly issued; JWKS keys may have rotated |
token_expired | 401 | The ID token has expired | Exchange a fresh token from your IdP |
invalid_issuer | 401 | Token issuer is not trusted | Ensure the iss claim matches your registered IdP URL |
discovery_failed | 502 | Could not fetch OIDC configuration from issuer | Verify your IdP's discovery endpoint is accessible |
issuer_not_registered | 403 | IdP not registered with Rialto | Contact Rialto to register your IdP |
org_mismatch | 403 | API key org doesn't match the token's issuer org | Use the correct API key for your IdP |