AUTH4D-265 ID token and userinfo claims: email, email_verified and idp

The ID token (from POST /oauth/token, for both the authorization_code and the refresh_token grant) and GET /oauth/userinfo now follow one rule for the address and both name the sign-in method. The wire shapes are the AUTH4D-258 contracts (auth4d-258.md (repository file docs/api-spec/auth4d-258.md)). sub is unchanged.

email and email_verified

Both appear only when the email scope was granted and the user has a provider email identity with a verified address: one proven to auth4.dev by an email code, magic link or password sign-in, by the add-email intent (auth4d-149.md (repository file docs/api-spec/auth4d-149.md)) or by the verified email step (auth4d-262.md (repository file docs/api-spec/auth4d-262.md)).

  • email is the stored address, lower-cased.
  • email_verified is always true.
  • Otherwise both keys are left out. Neither is ever null, and email_verified is never false.
  • An address a social provider sent (Discord, Google, an SSO connection) never becomes a claim, even when the provider marked it verified.

The ID token takes the address when the authorization code is issued. Since AUTH4D-332 a refresh reads the user’s verified email identity again, so a refreshed ID token carries a changed address, or none once it was removed or moved (auth4d-332.md (repository file docs/api-spec/auth4d-332.md)). Userinfo reads the user’s identities on every call.

{
  "sub": "usr_...",
  "email": "player@example.com",
  "email_verified": true,
  "idp": "steam"
}

A Steam player with no verified address gets { "sub": "usr_...", "idp": "steam" }.

idp

idp names the method used for the sign-in that created the session, from the IdpSchema list in auth4d-258.md (repository file docs/api-spec/auth4d-258.md):

Sign-in idp
Hosted login with a provider proof what the proof recorded: email, password, magic_link, steam, etc.
prompt=none reusing a hosted-login session session
Hosted guest start (/t/{tenantId}/guest/authorize) guest
  • It is stored with the authorization code and then with the session, so a refreshed ID token, and userinfo called with a refreshed access token, still name the original method.
  • The verified email step does not change it: a Steam sign-in that passed the step stays steam.
  • Userinfo returns the idp of the session behind the access token. It needs no scope.
  • A session created before this release has no stored method, so its tokens and userinfo omit idp until the player signs in again.
  • Tokens from the device grant and from the add-email intent’s replacement session don’t carry idp yet.

Access tokens

Access tokens do not carry idp. The access-token contract accepts the claim, but server SDKs released before AUTH4D-258 check access tokens against a strict claim list and would refuse it. Resource servers that need the sign-in method can call userinfo.

Flows

Flows token patches only reach auth4_roles and auth4_meta, so they never touch the top-level claims above. idp joins FLOW_RESERVED_CLAIMS beside email and email_verified (already reserved through RESERVED_CLAIM_NAMES), so a flow can’t name a claim idp either: a hook decision or step that does is refused like any other reserved name.

Source: docs/api-spec/auth4d-265.md