Authenticate users with their Cardano wallet over OpenID Connect. This page covers the OIDC identity layer and the public-client flow with PKCE (no client secret) — for SPAs and mobile apps. For a classic server-side app with a client secret, see the OAuth2 page.
Building a server-side app with a client secret? Use the classic OAuth2 flow. OAuth2 (server-side, with secret)
API identifiers and code samples are shown in English — they are the literal wire format.
Avatarada's OIDC engine is Ory Hydra. Point your OIDC client at the issuer and let it read every endpoint from the discovery document — do not hard-code endpoint paths.
<issuer>/.well-known/openid-configurationThe discovery document lists the authorization, token, userinfo, JWKS and revocation endpoints. Read them from there; the authorize and token URLs below are examples only.
<issuer>/.well-known/jwks.jsonUse the Authorization Code flow with PKCE (code_challenge_method=S256). Send the user to the authorize endpoint, then exchange the returned code at the token endpoint.
<issuer>/oauth2/auth
?client_id=YOUR_CLIENT_ID
&response_type=code
&scope=openid%20identity%20wallet:read
&audience=https%3A%2F%2Fapi.avatarada.io
&redirect_uri=https%3A%2F%2Fyour.app%2Fcallback
&state=RANDOM_STATE
&code_challenge=BASE64URL_SHA256_OF_VERIFIER
&code_challenge_method=S256To call the wallet-data API you must request BOTH the wallet:read scope AND the wallet-API audience (https://api.avatarada.io) on the authorize request. Selecting wallet:read in the developer console already adds that audience to the client's allow-list, but the authorize request must still ask for it — otherwise the access token carries no audience and the wallet-data API rejects it with 403 insufficient_scope.
curl -X POST "<issuer>/oauth2/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "code=AUTHORIZATION_CODE" \
-d "redirect_uri=https://your.app/callback" \
-d "client_id=YOUR_CLIENT_ID" \
-d "code_verifier=YOUR_PKCE_VERIFIER"
# Confidential clients authenticate with the client secret instead of (or in
# addition to) PKCE, e.g. -u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET".Register and manage clients in the developer console — there is no public dynamic registration. The console talks to Hydra on your behalf.
Request only the scopes you need. A scope you request that Avatarada does not grant is shown on the consent screen but dropped from the grant.
| Scope | Effect |
|---|---|
openid | Standard OpenID Connect — enables ID token issuance. |
identity | Adds the user's stake address to the ID token as the stake_address claim. |
wallet:read | Audience-only — grants no token claim, but unlocks the wallet-data API. Requesting it adds the wallet-API audience to the access token. |
inbox:send | Lets a service with a verified sending domain deliver mail to the user's inbox. Not a token claim. |
The ID token sub is the user's mainnet stake address (stake1…) — that is their Avatarada identity. With the identity scope, the same address is repeated in the explicit stake_address claim. No wallet or on-chain data is ever placed in a token.
{
"iss": "<issuer>",
"aud": "YOUR_CLIENT_ID",
"sub": "stake1u9...", // the user's mainnet stake address = their identity
"stake_address": "stake1u9..." // present only when the "identity" scope is granted
}Wallet data is never embedded in a token: it is served by the wallet-data API against the access token's audience (https://api.avatarada.io).
Each user also has an inbound-only inbox at stake1…@avatarada.io (the stake address from the sub). A service with a verified sending domain and the inbox:send scope can deliver mail there; everything else is silently dropped.
Avatarada's own session is the sole basis for single sign-on, and the user can revoke a connection at any time.
Once you hold a wallet:read access token, call the wallet-data API to read the user's public on-chain data.
Wallet-data API