Your first verified sign-in
Sandbox onlyThe runnable path: authorize, approve as a test identity, exchange, read.
Before you start
You need three things: a sandbox application, at least one test identity on it (see Test identities), and your own developer account signed in, because *you* are the one who will approve the consent screen on the test identity's behalf.
1. Send the person to authorize
With no prompt, an adequate session and unchanged permissions continue automatically. Explicit prompt=login, consent, select_account and none are supported, together with max_age and login_hint. See How the flow works.
Query parameters
response_typecode- The only response type offered.
client_id- The sandbox client id, exactly as the quickstart endpoint returned it.
redirect_uri- Must match a registered redirect URI character for character.
scope- Space-separated scope codes. Every one must exist in the catalogue and be inside the application's
allowedClaimScopes. state- Cryptographic randomness you will check on the way back.
nonce- Bound into the ID token. Check it on the way back.
code_challenge- The S256 challenge for your verifier.
code_challenge_methodS256- The only method the discovery document offers.
purpose- One sentence, at most 200 characters, shown to the person. If you send nothing they see
Sign in to <your application name>. display- Only the value
popupis forwarded; anything else is dropped.
GET /v1/oauth/authorize
?response_type=code
&client_id=<your sandbox client id>
&redirect_uri=https%3A%2F%2Flocalhost%3A8443%2Fcallback
&scope=openid%20profile%20email%20age_over_18
&state=<32+ bytes of randomness>
&nonce=<random>
&code_challenge=<base64url(sha256(verifier))>
&code_challenge_method=S256
&purpose=Confirm%20you%20are%20over%2018%20before%20checkout2. Approve as a test identity
On the consent screen, a member of the sandbox application's team can choose Sign in as a test identity. It is a control on the hosted screen, not something you call from your own code: it needs your signed-in developer account plus a fresh sign-in confirmation for exactly this authorization request.
Approving is one decision covering everything the screen listed. There are no per-claim toggles. The alternative is Cancel, which sends error=access_denied to your redirect URI.
3. Exchange the code
POST /v1/oauth/token. Client authentication is client_secret_basic, client_secret_post, or none for a public client — all three are advertised in the discovery document. The code verifier is always required if you sent a challenge.
curl -s -X POST "$UNIFYID_API/v1/oauth/token" \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-H "content-type: application/x-www-form-urlencoded" \
-d grant_type=authorization_code \
-d code="$CODE" \
-d redirect_uri="https://localhost:8443/callback" \
-d code_verifier="$CODE_VERIFIER"What the token endpoint returns
access_token- Bearer it at
/v1/userinfo. token_typeBearer- Always.
expires_in- Seconds. Read it; do not hard-code a lifetime.
id_token- Present when
openidwas granted. RS256, verified against the JWKS. Besides the standard claims it carriesidentity_verified,consent_id(bind your own sessions to this consent),first_sign_in(trueon the sign-in that created the connection) andconnected_at(ISO 8601, when the person first connected your application). scope- Space-separated — what was actually granted, which can be narrower than what you asked for.
4. Read what you were given
Use the verified issuer/client/sub to reuse the application account, even after reconnection. Bind your own session to the verified ID token's consent_id and handle signed consent.revoked webhooks to end those sessions. Never create duplicate users from first_sign_in. See Revocation and webhooks for the atomic storage and sign-out pattern.
GET /v1/userinfo returns a flat map of claim values, not a wrapped envelope and not a metadata structure.
curl -s "$UNIFYID_API/v1/userinfo" \
-H "authorization: Bearer $ACCESS_TOKEN"An unauthenticated or expired call answers 401 with a recorded shape:
{
"error": {
"code": "invalid_token",
"message": "A valid OAuth access token is required.",
"requestId": "7015cb0d-ac1a-427c-9b4f-0dacb1a2235e"
}
}What this does not do
- A claim you asked for can come back absent. Absent is not
falseand notnull— the key simply is not there. Handling the callback explains why that is unavoidable and what to do about it. - This sign-in starts a 30-day clock. It satisfies the
sandbox_sign_ingo-live check, and that check goes stale: if 30 days pass with no sandbox sign-in, it fails again. - A test identity never reaches the disclosure ledger. Signing in as one writes no disclosure event, though it does count as a token issuance for the checklist.
Verified against API commit 234dba22 on 2026-10-18. The routes, scopes, checklist, events and limits on these pages are read from that export, not typed.