The classic path, set up just like "Sign in with Google": a confidential OAuth2 client with a Client ID and secret, using the Authorization Code flow. Best for server-side web apps.
This is OpenID Connect under the hood — the openid scope returns an ID token with the user’s stake address. For browser or mobile apps that cannot keep a secret, use OpenID Connect with PKCE instead. OpenID Connect (PKCE, no secret)
API identifiers and code samples are shown in English — they are the literal wire format.
Three steps — the same as integrating any OAuth2 / OpenID Connect provider.
Send the user to the authorize endpoint, then exchange the returned code for tokens at the token endpoint, authenticating with your client secret.
<issuer>/.well-known/openid-configurationDo not hard-code these paths — read the authorize, token, userinfo and JWKS endpoints from the discovery document.
<issuer>/oauth2/auth
?client_id=YOUR_CLIENT_ID
&response_type=code
&scope=openid%20identity
&redirect_uri=https%3A%2F%2Fyour.app%2Fcallback
&state=RANDOM_STATEcurl -X POST "<issuer>/oauth2/token" \
-u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "code=AUTHORIZATION_CODE" \
-d "redirect_uri=https://your.app/callback"Request only what you need; the openid scope is what enables sign-in.
| Scope | Effect |
|---|---|
openid | Standard OpenID Connect — enables ID token issuance and sign-in. |
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. |
With the openid scope you receive an ID token whose sub is the user’s mainnet stake address — their identity. The identity scope repeats it as the 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
}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.
To call the wallet-data API, also request the wallet:read scope AND the wallet-API audience (https://api.avatarada.io) on the authorize request. Selecting wallet:read in the console adds that audience to the client allow-list, but the authorize request must still ask for it.
Wallet-data APIAvatarada's own session is the sole basis for single sign-on, and the user can revoke a connection at any time.
Browser and mobile apps that cannot keep a secret use a public client with PKCE — the same Authorization Code flow, without a secret.
OpenID Connect with PKCEOnce you hold a wallet:read access token, call the wallet-data API to read the user’s public on-chain data.
Wallet-data API