Apple
WIPSign in with Apple, its per-request client secret, the form-posted callback, and the name Apple sends once
- In Certificates, Identifiers & Profiles, create a Services ID with Sign in with Apple enabled. Its identifier is your client id.
- Configure it with your domain and the return URL
https://app.example.com/auth/providers/oauth:apple/callback. - Under Keys, create a key with Sign in with Apple enabled and download the
AuthKey_<keyId>.p8. Note the key id and your team id.
import { apple } from '@gentleduck/auth/providers/oauth/apple'
apple({
clientId: 'com.example.app.web',
teamId: process.env.APPLE_TEAM_ID ?? '',
keyId: process.env.APPLE_KEY_ID ?? '',
privateKey: process.env.APPLE_PRIVATE_KEY ?? '', // the .p8 contents
redirectUri: 'https://app.example.com/auth/providers/oauth:apple/callback',
stateSigningSecret: process.env.OAUTH_STATE_SECRET ?? '',
nonceStore,
profileToIdentityProfile: (p) => (p.email ? { email: p.email, username: `apple:${p.sub}`, name: p.name } : null),
})
The provider id is oauth:apple, and the default scopes are name email.
- There is no
clientSecret. Each token exchange signs a fresh ES256 client secret from the key, valid for 30 minutes.generateClientSecretis exported if you need one elsewhere. - Apple posts the callback as a form, so it needs the POST callback route on the OAuth page, and a
securestate cookie: the cookie goes outSameSite=None, and a non-secure one throwsAUTH_MISCONFIGURED. Test over https. - The profile is read from the id_token.
emailVerifiedis set when it saysemail_verifiedistrueor"true". A user who hides their address signs in with an@privaterelay.appleid.comone. - Apple sends the name only on the first authorization. Store it from
profileToIdentityProfile, since no later sign-in carries it.
The options every provider shares and account linking are on the OAuth page.