Skip to main content

Apple Authentication

This guide explains how to set up Sign in with Apple for the Zero Kelvin application.

Prerequisites

Step 1: Create an App ID

  1. Go to the Apple Developer Portal
  2. Navigate to Certificates, Identifiers & Profiles
  3. Select Identifiers from the sidebar
  4. Click the + button to create a new identifier
  5. Select App IDs and click Continue
  6. Select App as the type and click Continue
  7. Fill in the details:
    • Description: Zero Kelvin
    • Bundle ID: Choose Explicit and enter com.example.web
  8. Scroll down to Capabilities and check Sign in with Apple
  9. Click Continue and then Register

Step 2: Create a Services ID

  1. In the Identifiers section, click the + button again
  2. Select Services IDs and click Continue
  3. Fill in the details:
    • Description: Zero Kelvin Web
    • Identifier: com.example.web.signin (this will be your AUTH_APPLE_ID)
  4. Click Continue and then Register
  5. Click on the newly created Services ID
  6. Check Sign in with Apple and click Configure
  7. Select your App ID as the Primary App ID
  8. Under Domains and Subdomains, add:
    • localhost (for development)
    • Your production domain (e.g., example.com)
  9. Under Return URLs, add:
    • http://localhost:3000/api/auth/callback/apple (for development)
    • Your production callback URL (e.g., https://example.com/api/auth/callback/apple)
  10. Click Save and then Continue and Save

Step 3: Create a Private Key

  1. Navigate to Keys in the sidebar
  2. Click the + button to create a new key
  3. Enter a Key Name (e.g., "Zero Kelvin Sign in with Apple")
  4. Check Sign in with Apple and click Configure
  5. Select your App ID as the Primary App ID
  6. Click Save and then Continue
  7. Click Register
  8. Important: Download the key file (.p8) immediately - you can only download it once!
  9. Note the Key ID displayed on the page

Step 4: Generate the Client Secret

Apple uses a JWT as the client secret. You need to generate this from your private key.

Option 1: Generate at Build Time

Create a script to generate the secret:

// scripts/generate-apple-secret.js
const jwt = require("jsonwebtoken");
const fs = require("fs");

const privateKey = fs.readFileSync("path/to/AuthKey_XXXXXXXX.p8");
const teamId = "YOUR_TEAM_ID"; // Found in Apple Developer account
const clientId = "com.example.web.signin"; // Your Services ID
const keyId = "YOUR_KEY_ID"; // From step 3

const token = jwt.sign({}, privateKey, {
algorithm: "ES256",
expiresIn: "180d",
audience: "https://appleid.apple.com",
issuer: teamId,
subject: clientId,
keyid: keyId,
});

console.log(token);

Option 2: Use Environment Variables

Store the private key content in an environment variable and generate the secret dynamically in auth.ts:

import Apple from "next-auth/providers/apple"
import jwt from "jsonwebtoken"

function getAppleSecret() {
const privateKey = process.env.AUTH_APPLE_PRIVATE_KEY?.replace(/\\n/g, '\n')

return jwt.sign({}, privateKey, {
algorithm: 'ES256',
expiresIn: '180d',
audience: 'https://appleid.apple.com',
issuer: process.env.AUTH_APPLE_TEAM_ID,
subject: process.env.AUTH_APPLE_ID,
keyid: process.env.AUTH_APPLE_KEY_ID,
})
}

// In your providers array:
Apple({
clientId: process.env.AUTH_APPLE_ID,
clientSecret: getAppleSecret(),
}),

Step 5: Configure Environment Variables

Add the following to your .env.local file:

AUTH_APPLE_ID="com.example.web.signin"
AUTH_APPLE_SECRET="your-generated-jwt-secret"

# Or if generating dynamically:
AUTH_APPLE_TEAM_ID="YOUR_TEAM_ID"
AUTH_APPLE_KEY_ID="YOUR_KEY_ID"
AUTH_APPLE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"

Step 6: Test the Integration

  1. Start the development server:
nx dev web
  1. Navigate to http://localhost:3000
  2. Click the Sign In button
  3. Select Continue with Apple

Important Notes

Email Privacy

  • Apple offers users the option to hide their email address
  • If the user chooses "Hide My Email", Apple provides a private relay email
  • Store both the Apple-provided email and any private relay addresses

Client Secret Expiration

  • The JWT client secret expires after 180 days (maximum)
  • Set up a process to regenerate the secret before expiration

HTTPS Required

  • Apple Sign In requires HTTPS in production
  • Development with localhost is allowed as an exception

Troubleshooting

"invalid_client"

  • Verify your Services ID matches AUTH_APPLE_ID
  • Check that the JWT secret is valid and not expired
  • Ensure the redirect URI matches exactly

"redirect_uri_mismatch"

  • The return URL must exactly match what's configured in the Services ID
  • Check for trailing slashes and protocol (http vs https)

User email not returned

  • Apple only returns the email on the first authentication
  • Store the user's email in your database on first sign-in