apistockdocs
v0.4 GitHub apistock.dev
Guides/Set up sign-in

Apple sign-in

Let people sign in with their Apple Account, on your website and in your iOS apps. This page starts from nothing. You need a paid Apple Developer Program membership (a yearly fee), and about 30 minutes.

If your iOS app offers Google sign-in, the App Store generally requires Sign in with Apple as well.

What you'll end up with#

Variable Required? Secret? Looks like How you get it
APPLE_TEAM_ID Yes No ABCDE12345 Copied from your Apple Developer account
APPLE_KEY_ID Yes No XYZ987WVU6 Copied from Apple, when you create the key
APPLE_PRIVATE_KEY_FILE Yes (or APPLE_PRIVATE_KEY) Yes, the file it points to /run/secrets/AuthKey_XYZ987WVU6.p8 A .p8 file you download from Apple, once
APPLE_SERVICES_ID For sign-in on websites No com.example.web You choose it, and register it at Apple
APPLE_BUNDLE_IDS For sign-in inside iOS apps No com.example.app Your iOS app's bundle ID
APP_PUBLIC_URL For websites No https://api.example.com Your API's own https address

You need at least one of APPLE_SERVICES_ID and APPLE_BUNDLE_IDS, and all of the first three.

Note

There is no Apple client secret to create. Apple's own guides tell you to make a "client secret" by signing a token with your key and to renew it every few months. Your API does this for you: each time it talks to Apple, it creates a fresh one from APPLE_TEAM_ID, APPLE_KEY_ID and the .p8 key, valid for 5 minutes. You never generate or store one, and there's no variable for it.

Words on this page#

Word What it is
Team ID Your developer account's 10-character ID
App ID Your app's registration at Apple, named by its bundle ID such as com.example.app. Apple ties web sign-in to an App ID too, even if you only have a website
Services ID The registration for signing in on websites, such as com.example.web. It says which domains and return URLs Apple may send people back to
Key (.p8 file) A private key that proves requests come from your team. Apple lets you download it once
Key ID The key's 10-character name
Return URL The address on your API where Apple sends people back: Apple's name for a redirect URI

How it works#

  1. Your website sends the browser to your API: /v1/auth/apple/start.
  2. Your API sends the browser on to Apple, with your Services ID.
  3. The person signs in at Apple. The first time, they choose whether to share their email address or hide it behind a relay address.
  4. Apple sends the browser back with a form POST to your API's return URL, /v1/auth/apple/callback.
  5. Your API checks Apple's answer with a client secret it signs with your key, signs the person in and sends them back to your website.

Your URLs#

Field at Apple Value (replace api.example.com with your API's host)
Domains and Subdomains api.example.com (host only: no https://, no path)
Return URLs https://api.example.com/v1/auth/apple/callback
Server-to-Server Notification Endpoint https://api.example.com/v1/auth/apple/notifications

Apple accepts only https addresses, and never localhost or IP addresses. For staging, register its host and return URL as well: a Services ID can hold several.

Step 1: Find your Team ID#

  1. Open your developer account

    Sign in at developer.apple.com/account with the Apple Account that belongs to your developer membership.

  2. Copy the Team ID

    Scroll to Membership details. Copy Team ID, 10 letters and digits:

    terminal
    APPLE_TEAM_ID=ABCDE12345
    

Step 2: Create or update the App ID#

  1. Open Identifiers

    In your account, open Certificates, IDs & ProfilesIdentifiers.

  2. If your app already has an App ID

    Click it, tick Sign in with Apple under Capabilities, and click Save. Skip to step 5 below.

  3. Otherwise, register one

    Click +App IDsContinueAppContinue. Description: your app's name. Bundle ID: Explicit, such as com.example.app. If you have an iOS app, use its exact bundle ID from Xcode.

  4. Turn on Sign in with Apple

    Under Capabilities, tick Sign in with Apple, leave it as a primary App ID, then ContinueRegister.

  5. Add the notification endpoint

    Open the App ID again. Next to Sign in with Apple, click Edit (or Configure). In Server-to-Server Notification Endpoint, enter https://api.example.com/v1/auth/apple/notifications and save. Apple then tells your API when someone stops using Sign in with Apple with your app or deletes their Apple Account, and your API acts on it.

  6. If you have an iOS app

    Put its bundle ID in APPLE_BUNDLE_IDS, and in Xcode add the Sign in with Apple capability to your target (Signing & Capabilities+ Capability).

Step 3: Create the Services ID (websites)#

Skip this step if you only need sign-in inside iOS apps.

  1. Register it

    Identifiers+Services IDsContinue. Description: shown to people on Apple's sign-in page, so use your app's name. Identifier: a reverse-domain name that differs from the App ID, such as com.example.web. Click ContinueRegister.

  2. Configure Sign in with Apple

    Click the new Services ID, tick Sign in with Apple, and click Configure.

  3. Fill in the website details

    • Primary App ID: the App ID from step 2.
    • Domains and Subdomains: api.example.com.
    • Return URLs: https://api.example.com/v1/auth/apple/callback.

    Click NextDoneContinueSave.

  4. Copy the identifier

    terminal
    APPLE_SERVICES_ID=com.example.web
    

Step 4: Create the key#

  1. Start a key

    Certificates, IDs & ProfilesKeys+.

  2. Name and enable it

    Key Name: such as acme Sign in with Apple. Tick Sign in with AppleConfigure → choose your primary App ID from step 2 → SaveContinueRegister.

  3. Download the file

    Click Download. You get a file named like AuthKey_XYZ987WVU6.p8. Apple lets you download it only once. If you lose it, revoke the key on the same page and create a new one.

  4. Copy the Key ID

    It's on the page, and in the file name after AuthKey_:

    terminal
    APPLE_KEY_ID=XYZ987WVU6
    

The file is plain text. It should look like this, with different characters in the middle:

output
-----BEGIN PRIVATE KEY-----
MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQg…
-----END PRIVATE KEY-----

Step 5: Store the key#

The .p8 file is the most sensitive value on this page: with it, anyone can act as your app at Apple. Keep it out of your repository.

On your computer, move it to a folder outside your app and point to it with a full path:

terminal
mkdir -p ~/.config/acme-api
mv ~/Downloads/AuthKey_XYZ987WVU6.p8 ~/.config/acme-api/
chmod 600 ~/.config/acme-api/AuthKey_XYZ987WVU6.p8
terminal
# in .env: a full path, not ~ and not a path inside the repository
APPLE_PRIVATE_KEY_FILE=/Users/you/.config/acme-api/AuthKey_XYZ987WVU6.p8

In production, use one of these:

  • A secret file (preferred): upload the .p8 as a secret that your platform mounts as a file (Kubernetes secrets, Docker secrets, Fly.io or Render secret files), and set APPLE_PRIVATE_KEY_FILE to where it's mounted, such as /run/secrets/apple_private_key.
  • A secret value: if your platform only has environment variables, set APPLE_PRIVATE_KEY to the whole contents of the file, including the BEGIN and END lines and the line breaks.

Set one or the other, never both: the app refuses to start when both are set.

Step 6: Let Apple forward emails#

People can hide their email address. Apple then gives your app an address like abc123@privaterelay.appleid.com, and forwards email sent to it, but only from senders you've registered. Without this step, those people never receive their sign-up codes.

  1. Open the email settings

    Certificates, IDs & ProfilesServicesSign in with Apple for Email CommunicationConfigure.

  2. Add your sender

    Click + and add the domain you send email from (the domain of mail.from_email), or the exact sender address.

  3. Check your domain's records

    Apple checks that the domain has an SPF record (and DKIM, ideally). Your email provider showed you these when you verified the domain (Email sending).

Step 7: Put the values in your app#

A website and an iOS app, in production:

terminal
APP_PUBLIC_URL=https://api.example.com
APPLE_TEAM_ID=ABCDE12345
APPLE_SERVICES_ID=com.example.web
APPLE_KEY_ID=XYZ987WVU6
APPLE_PRIVATE_KEY_FILE=/run/secrets/apple_private_key
APPLE_BUNDLE_IDS=com.example.app

Only an iOS app: leave APPLE_SERVICES_ID empty. APP_PUBLIC_URL is then only needed for other providers.

Restart the app after changing them.

Step 8: Try it#

Inside your iOS app#

This works on your computer without anything extra, because it doesn't use return URLs.

  1. Get a one-time value from POST /v1/auth/apple/nonce.
  2. With ASAuthorizationAppleIDProvider, set request.nonce to the SHA-256 of that value, as lowercase hex.
  3. Send identityToken, authorizationCode, the original value, and the person's name (Apple only gives it the first time) to POST /v1/auth/apple/token with "transport": "bearer".
  4. Your API answers with a session token, or with a second-factor challenge if the account has one. Store the token in the Keychain.

On a website, from your computer#

Apple needs a public https address, so give your computer one with a tunnel. For example, with Cloudflare's free tunnel tool (install cloudflared):

terminal
cloudflared tunnel --url http://localhost:8080

It prints an address such as https://random-words.trycloudflare.com. Then:

  1. Add that host to the Services ID's Domains and Subdomains, and https://random-words.trycloudflare.com/v1/auth/apple/callback to its Return URLs.
  2. In .env, set APP_PUBLIC_URL=https://random-words.trycloudflare.com and restart.
  3. In your browser, open https://random-words.trycloudflare.com/v1/auth/apple/start, using the tunnel address throughout rather than localhost.
  4. Sign in at Apple. You come back to /docs on the tunnel address, signed in; /v1/auth/me shows the account.

A quick tunnel's address changes each time you start it, so remove old ones from the Services ID when you're done. ngrok http 8080 works the same way.

Check it works#

  • go run ./cmd/api auth-providers shows ✓ Apple sign-in (websites) and ✓ Apple sign-in in iOS apps.
  • GET /ops/auth/providers shows the same to administrators.
  • Signing in through /v1/auth/apple/start ends back on your site, signed in.

If something goes wrong#

What you see What it means Fix
The app won't start: sign-in with Apple also needs APPLE_KEY_ID, … Some Apple values are set and others are missing Set the variables it lists, or empty every APPLE_ variable to turn Apple off
The app won't start: APPLE_PRIVATE_KEY_FILE: … isn't a PEM private key The file isn't the .p8, or the value lost its BEGIN/END lines Point to the downloaded .p8; with APPLE_PRIVATE_KEY, paste the whole file including line breaks
The app won't start: the Apple key must be a P-256 key A different kind of key, such as an APNs certificate Create a key with Sign in with Apple ticked
The app won't start: read APPLE_PRIVATE_KEY_FILE The path is wrong or not readable by the app Use a full path, and check the file's permissions
The app won't start: both variable and _FILE variant are set: APPLE_PRIVATE_KEY Both ways of giving the key are set Keep one
Apple shows invalid_request or Invalid redirect_uri The return URL isn't registered exactly, or isn't https Add <APP_PUBLIC_URL>/v1/auth/apple/callback to the Services ID exactly
Apple's sign-in fails with invalid_client The Services ID, Team ID or Key ID doesn't match, or the key was revoked or belongs to another App ID Check all three values, and that the key is enabled for Sign in with Apple with the Services ID's primary App ID
People with hidden emails never get codes Apple doesn't relay from your sender Do step 6
iOS: sign-in fails at your API The app's bundle ID isn't in APPLE_BUNDLE_IDS, or the nonce wasn't hashed Add the bundle ID; send the original value, and give Apple its SHA-256
esc
↑↓ move↵ openesc close