Apple Sign In Setup
This guide covers setting up Sign in with Apple, providing users with a secure and privacy-focused authentication option.
Prerequisites
- Apple Developer account ($99/year membership)
- Access to Apple Developer Portal
- Xcode installed (for iOS apps)
Step 1: Create an App ID
- Go to Apple Developer Portal
- Navigate to Certificates, Identifiers & Profiles
- Click Identifiers in the sidebar
- Click the + button to add a new identifier
- Select App IDs and click Continue
- Select App type and click Continue
- Fill in the details:
- Description: Your app name
- Bundle ID: Explicit (e.g.,
com.yourcompany.myapp)
- In Capabilities, enable Sign in with Apple
- Click Continue, then Register
Step 2: Create a Services ID (For Web)
For web applications, you need a Services ID:
- Go to Identifiers
- Click + and select Services IDs
- Click Continue
- Enter details:
- Description: "MyApp Web Login"
- Identifier:
com.yourcompany.myapp.web
- Click Continue, then Register
Configure Web Authentication
- Find your Services ID in the list and click on it
- Enable Sign in with Apple
- Click Configure
- Set up the Web Authentication:
| Field | Value |
|---|---|
| Primary App ID | Your main App ID |
| Domains | yourdomain.com |
| Return URLs | https://yourdomain.com/auth/apple/callback |
- Click Next, then Done, then Continue, then Save
Step 3: Create a Sign in with Apple Key
- Go to Keys in the sidebar
- Click + to create a new key
- Enter a Key Name (e.g., "MyApp Sign in with Apple")
- Enable Sign in with Apple
- Click Configure
- Select your Primary App ID
- Click Save, then Continue, then Register
- Download the key file (.p8 file) immediately
Important: You can only download the key once. Store it securely.
- Note down:
- Key ID - Displayed on the key details page
- Team ID - Found in Membership details
Step 4: Generate Client Secret
Apple uses a JWT token as the client secret. You'll need to generate this:
Required Information
| Item | Where to Find |
|---|---|
| Team ID | Membership > Team ID |
| Key ID | Keys > Your Key |
| Services ID | Identifiers > Services IDs |
| Private Key | The .p8 file you downloaded |
Generate JWT Token
Use this Node.js code to generate the client secret:
const jwt = require('jsonwebtoken');
const fs = require('fs');
const privateKey = fs.readFileSync('AuthKey_XXXXXXXXXX.p8');
const clientSecret = jwt.sign({}, privateKey, {
algorithm: 'ES256',
expiresIn: '180d',
audience: 'https://appleid.apple.com',
issuer: 'YOUR_TEAM_ID',
subject: 'com.yourcompany.myapp.web', // Services ID
keyid: 'YOUR_KEY_ID'
});
console.log(clientSecret);
Note: Client secrets expire. Generate a new one before expiration (max 6 months).
Step 5: Configure in OpenDev
- Log in to OpenDev Platform
- Navigate to OAuth Channels
- Add or edit the Apple OAuth channel
- Enter your configuration:
{
"provider": "apple",
"clientId": "com.yourcompany.myapp.signin",
"teamId": "YOUR_TEAM_ID",
"keyId": "YOUR_KEY_ID",
"privateKey": "[encrypted_private_key]",
"callbackUrl": "https://yourdomain.com/auth/callback?provider=apple",
"scopes": ["email", "name"]
}
Note:
- The
privateKeyshould be encrypted using the project's encryption utility before storing in the database.- The unified callback URL format is
/auth/callback?provider=apple(POST method).- For Web popup mode,
teamId,keyId,privateKey, andcallbackUrlare optional.- The
appSecretfield name is also supported as an alias forprivateKey.
Configuration Fields
| Field | Web Popup | Server Auth | Description |
|---|---|---|---|
| Client ID | Required | Required | Services ID (e.g., com.company.app.signin) |
| Team ID | Optional | Required | Your Apple Developer Team ID |
| Key ID | Optional | Required | The Sign in with Apple key ID |
| Private Key | Optional | Required | Encrypted content of the .p8 file |
| Callback URL | Optional | Required | Your return URL for authorization code flow |
| Scopes | Optional | Optional | Data to request (email, name) |
Authentication Modes
Web Popup Mode (Recommended for Web)
- Uses Apple JS SDK with
usePopup: true - Only requires
clientId - Returns
id_tokendirectly to frontend - Backend verifies token using Apple's public JWKS
Server Authorization Code Mode
- Uses redirect-based OAuth flow
- Requires all configuration fields
privateKeyused to generate JWT client secret- More control over token exchange
Encrypt Private Key
Before storing the private key, encrypt it using:
cd backend
node scripts/tools/encrypt-apple-key.js ../configure/AuthKey_XXXXXXXXXX.p8
Copy the encrypted output to the privateKey field in the database configuration.
Step 6: iOS Implementation
For iOS apps, add the capability in Xcode:
- Open your project in Xcode
- Select your target
- Go to Signing & Capabilities
- Click + Capability
- Add Sign in with Apple
Info.plist Configuration
No additional Info.plist entries needed for basic Sign in with Apple.
Code Implementation
import AuthenticationServices
// Create the request
let request = ASAuthorizationAppleIDProvider().createRequest()
request.requestedScopes = [.fullName, .email]
// Create the authorization controller
let controller = ASAuthorizationController(authorizationRequests: [request])
controller.delegate = self
controller.presentationContextProvider = self
controller.performRequests()
Step 7: Test the Integration
Testing on iOS
- Use a real device (Sign in with Apple doesn't work in simulator for your own apps)
- Sign in with your Apple ID
- Verify the authentication flow
Testing on Web
- Visit your web application
- Click "Sign in with Apple"
- Complete the Apple authentication
- Verify the callback receives user data
Sample OAuth Response
{
"provider": "apple",
"providerId": "001234.abc123def456.0123",
"email": "user@privaterelay.appleid.com",
"name": "John D.",
"emailVerified": true
}
Note: Apple only sends name and email on first authorization. Store them immediately.
Privacy Considerations
Hide My Email
Users can choose to hide their email. You'll receive a private relay email:
- Format:
random@privaterelay.appleid.com - Emails sent to this address forward to the user's real email
Name Handling
- Users can modify or hide their name
- Name is only provided on first authorization
- Store the name immediately as it won't be sent again
Troubleshooting
Error: invalid_client
Solutions:
- Verify Services ID matches Client ID
- Check that private key is correctly formatted
- Ensure client secret JWT hasn't expired
Error: invalid_grant
Solutions:
- Authorization code may have expired (5 minutes validity)
- Code may have already been used
- Check redirect URI matches exactly
User Info Not Received
Solution: Name and email are only sent on first authorization. Check if user previously authorized your app.
JWT Signature Verification Failed
Solutions:
- Ensure private key matches the Key ID
- Check algorithm is ES256
- Verify key hasn't been revoked
Security Best Practices
- Secure Private Key - Never expose in client-side code
- Validate ID Token - Verify Apple's JWT signature
- Check Nonce - Prevent replay attacks
- Handle Revocation - Listen for credential revoked notifications
- Rotate Secrets - Generate new client secrets before expiration