Skip to content

Apple

Apple clients are OIDC providers: the token response always includes an id_token for the code flow. They also reproduce Apple’s less-standard corners, which are the behaviors most likely to surprise an integration.

The authorization response is delivered as an auto-submitting form POST to your redirect URI, not a 302 redirect with query parameters. Your callback must read the code from the POST body. response_mode=query is rejected, as real Apple rejects it for code flow.

The deny path follows the same shape: when the user denies the consent screen, the error is delivered as a form POST to your redirect URI rather than a redirect.

  • email_verified is the string "true" or "false" in the ID token, not a boolean — a real Apple quirk, kept on purpose. Claim parsing that expects a boolean will fail here exactly as it would against Apple.
  • Privacy claims — the ID token carries is_private_email and real_user_status.
  • No name in the token — Apple puts the user’s name in the user field of the form POST body, as JSON, only on the first authorization:
{
"name": { "firstName": "Tricia", "lastName": "McMillan" },
"email": "tricia@minutemail.cc"
}

Your app must capture the name from that first response and store it — subsequent authorizations carry no name, same as real Apple.

The token response returns token_type: "Bearer" and includes expires_in (3600). The code is exchanged with a form-encoded POST to the token endpoint.

Known deviation — Apple requires a client secret in the form of an ES256-signed JWT; the mock does not enforce this and accepts its standard shared client secret (cs_...). This keeps setup simple. Everything else about the Apple flow follows the real wire behavior.

Apple clients serve the standard /oauth/userinfo endpoint — call it with the access token as a Bearer token.

PKCE (S256) is optional for Apple clients.