AppleSiwaServer

The server half of Sign in with Apple on Android, written down because there is no client-only path and pretending otherwise is how this ships broken.

Why a server is unavoidable. Apple forces response_mode=form_post whenever scope is requested (name/email). form_post means Apple sends an HTTP POST with a form body to the redirect_uri. An Android App Link cannot receive a POST — the intent system delivers a GET-shaped VIEW intent and the body is dropped. So redirect_uri must point at a server endpoint that:

  1. accepts POST with application/x-www-form-urlencoded;

  2. reads code, id_token, state, and user (first authorization only) from the body;

  3. verifies state against the one it issued — this is the CSRF check, and the client's second check in AppleWebFlow.complete is a belt, not the braces;

  4. replies 302 to your app link, e.g. https://yourdomain.example/auth/apple/callback?code=…&id_token=…&state=…&user=…, percent-encoding every value;

  5. ideally exchanges the code server-side and bounces only an opaque session handle, so the Apple code never rides through a URL an installed browser extension could read.

Skipping the scope request is the one escape hatch: with no scope, Apple allows response_mode=query and will 302 straight to an app link, no server. You then never learn the user's name or email — and Apple only ever sends the name once, on first authorization, so "we'll ask for it later" does not exist. AppleSignInConfig supports both; the empty-scope form is honest about what it gives up.

The client secret. Token exchange and revocation authenticate with a JWT signed ES256 by the .p8 key (APPLE_SIWA_KEY_ID), iss = APPLE_TEAM_ID, sub = APPLE_SERVICES_ID. Apple caps its lifetime at 6 months, so a rotation job is mandatory — the failure mode is every sign-in breaking at once, six months after a launch nobody remembers. The .p8 is downloadable exactly once, is a private key, and must never enter this repo; it belongs in a secret manager.

Properties

Link copied to clipboard

Values the server needs. Listed here so provisioning/provision.sh check reports them.

Functions

Link copied to clipboard

True when the three server-side values have all been provisioned.