Skip to content
Developer docs
Menu

Concepts

Tokens and claims

What the ID token and access token contain, and how to validate them.

A sign-in gives your app two tokens. The ID token tells your app who signed in. The access token lets your app call Aldelo APIs for the person. Both are JSON Web Tokens signed with RS256.

ID token

ID token claims
ClaimMeaning
acrHow strongly the person authenticated: one of the acr values below.
amrHow the person authenticated, as a list of the amr values below.
at_hashA hash of the access token issued with this ID token.
audWho the token is for: an array containing your client_id.
auth_timeWhen the person last authenticated, in seconds since the Unix epoch.
expWhen the token expires, in seconds since the Unix epoch.
iatWhen the token was issued, in seconds since the Unix epoch.
issThe issuer. Always the discovery document's issuer.
jtiA unique identifier for this token.
organization_idThe organization this sign-in is for. See Organizations.
ratWhen the authorization request was received, in seconds since the Unix epoch. Informational.
sidThe app session this token belongs to. See Sessions and logout.
subThe person's stable identifier. Use it, never the email address, as your user key.

When your authorization request sends a nonce, the ID token carries it too. The claims are then: acr, amr, at_hash, aud, auth_time, exp, iat, iss, jti, nonce, organization_id, rat, sid, sub.

JSON
{
  "acr": "urn:aldelo:sso:acr:pwd",
  "amr": [
    "pwd"
  ],
  "at_hash": "77QmUPtjPfzWtF2AnpK9RQ",
  "aud": [
    "example-app"
  ],
  "auth_time": 1700000000,
  "exp": 1700000600,
  "iat": 1700000000,
  "iss": "https://sso.aldelo.com",
  "jti": "00000000-0000-0000-0000-000000000000",
  "nonce": "n-0S6_WzA2Mj",
  "organization_id": "00000000-0000-0000-0000-000000000000",
  "rat": 1700000000,
  "sid": "00000000-0000-0000-0000-000000000000",
  "sub": "00000000-0000-0000-0000-000000000000"
}

Validate an ID token

  1. Check the signature with the key whose kid matches the token header, from https://sso.aldelo.com/.well-known/jwks.json. Accept only RS256.
  2. Check that iss is exactly https://sso.aldelo.com.
  3. Check that aud contains your client_id: it is an array, not a bare string.
  4. Check that exp is in the future. Allow a few seconds of clock skew, no more.
  5. If you sent a nonce, check that the token carries the same value.
  6. Check that acr is strong enough for what the person is about to do.

Then key the person on sub, never on their email address, and scope everything they do to organization_id.

Access token

Access token claims
ClaimMeaning
acrHow strongly the person authenticated: one of the acr values below.
amrHow the person authenticated, as a list of the amr values below.
audAlways empty: access tokens are not audience-restricted. See below.
auth_timeWhen the person last authenticated, in seconds since the Unix epoch.
expWhen the token expires, in seconds since the Unix epoch.
iatWhen the token was issued, in seconds since the Unix epoch.
issThe issuer. Always the discovery document's issuer.
jtiA unique identifier for this token.
organization_idThe organization this sign-in is for. See Organizations.
scpThe scopes this access token grants, as a list.
sidThe app session this token belongs to. See Sessions and logout.
subThe person's stable identifier. Use it, never the email address, as your user key.
JSON
{
  "acr": "urn:aldelo:sso:acr:pwd",
  "amr": [
    "pwd"
  ],
  "aud": [],
  "auth_time": 1700000000,
  "exp": 1700000600,
  "iat": 1700000000,
  "iss": "https://sso.aldelo.com",
  "jti": "00000000-0000-0000-0000-000000000000",
  "organization_id": "00000000-0000-0000-0000-000000000000",
  "scp": [
    "openid",
    "email"
  ],
  "sid": "00000000-0000-0000-0000-000000000000",
  "sub": "00000000-0000-0000-0000-000000000000"
}

Its aud is always empty: nothing in this service ever grants an access token an audience. A locally validated access token — signature, iss and exp checked — proves only that Aldelo SSO issued it; it does not say which app it was issued to, and does not carry a client_id or azp claim either.

Access tokens are not audience-restricted

Do not accept an access token just because its signature and issuer check out, and do not validate it "the same way as an ID token": there is no aud to compare against your client_id. An API that must know the calling app, or must reject a token minted for a different app, introspects instead.

Call POST /oauth2/introspect with your own client credentials. Its client_id names the token's minting app, and active reflects revocation and the app session's state. Introspection answers active only for tokens issued to the calling app, or to another app owned by the same organization, and allows 5 requests per 60 seconds per client and IP address. Local validation alone tells you only that SSO issued the token; introspect whenever you need to know the calling app, reject a token minted for a different app, or get a live answer.

A locally validated token outlives sign-out

A token you validate yourself stays valid until exp, even after the person signs out. Access tokens last 600 seconds unless your registration sets another value. When you need to know the session is still live, call /userinfo or introspect the token: both stop accepting it once its app session ends.

Authentication strength

Both tokens always carry acr, set from how the person actually signed in:

acr values
acrMeaning
urn:aldelo:sso:acr:mfaTwo different methods: a passkey or an authenticator-app code, plus however the person opened their session — a password or a text-message sign-in. It does not necessarily include a password. A recovery code doesn't count toward it.
urn:aldelo:sso:acr:pwdA password.
urn:aldelo:sso:acr:phoneA code sent by text message, with no password. The weakest level: never treat it as a password sign-in.

To require a level, send acr_values in the authorization request, for example acr_values=urn:aldelo:sso:acr:mfa. A person whose session is weaker is asked for their second factor before SSO returns to your app. If they haven't set up a second factor, your redirect URI receives error=access_denied with error_description=strong authentication required; no second factor enrolled. An acr_values entry SSO doesn't recognize returns error=invalid_request.

amr lists how the person authenticated, using these values: pwd, hwk, otp, rcv, sms.

Userinfo

GET or POST /userinfo, with the access token as a bearer token, returns sub and organization_id. With the email scope it also returns email and email_verified, read live from the person's account. The profile scope is accepted but adds no claims today. Every failure returns the same 401; see Errors.

Signing keys

The key set at https://sso.aldelo.com/.well-known/jwks.json is served with Cache-Control: public, max-age=300. Keys rotate. A retired key stays in the set for at least 60 minutes after a rotation, so a token whose lifetime is at most that long keeps validating; your registration can set a longer token lifetime than that floor, and a token that outlives the floor may not. Pick the key by kid; if a token names a kid you don't have, fetch the key set once more before you reject the token.