docs/content/docs/authentication/apple.mdx
To use Apple sign in, you need a client ID and client secret. You can get them from the [Apple Developer Portal](https://developer.apple.com/account/resources/authkeys/list).
You will need an active **Apple Developer account** to access the developer portal and generate these credentials.
Follow these steps to set up your App ID, Service ID, and generate the key needed for your client secret:
1. **Navigate to Certificates, Identifiers & Profiles:**
In the Apple Developer Portal, go to the "Certificates, Identifiers & Profiles" section.
2. **Create an App ID:**
* Go to the `Identifiers` tab.
* Click the `+` icon next to Identifiers.
* Select `App IDs`, then click `Continue`.
* Select `App` as the type, then click `Continue`.
* **Description:** Enter a name for your app (e.g., "My Awesome App"). This name may be displayed to users when they sign in.
* **Bundle ID:** Set a bundle ID. The recommended format is a reverse domain name (e.g., `com.yourcompany.yourapp`). Using a suffix like `.ai` (for app identifier) can help with organization but is not required (e.g., `com.yourcompany.yourapp.ai`).
* Scroll down to **Capabilities**. Select the checkbox for `Sign In with Apple`.
* Click `Continue`, then `Register`.
3. **Create a Service ID:**
* Go back to the `Identifiers` tab.
* Click the `+` icon.
* Select `Service IDs`, then click `Continue`.
* **Description:** Enter a description for this service (e.g., your app name again).
* **Identifier:** Set a unique identifier for the service. Use a reverse domain format, distinct from your App ID (e.g., `com.yourcompany.yourapp.si`, where `.si` indicates service identifier - this is for your organization and not required). **This Service ID will be your `clientId`.**
* Click `Continue`, then `Register`.
4. **Configure the Service ID:**
* Find the Service ID you just created in the `Identifiers` list and click on it.
* Check the `Sign In with Apple` capability, then click `Configure`.
* Under **Primary App ID**, select the App ID you created earlier (e.g., `com.yourcompany.yourapp.ai`).
* Under **Domains and Subdomains**, list all the root domains you will use for Sign In with Apple (e.g., `example.com`, `anotherdomain.com`).
* Under **Return URLs**, enter the callback URL. `https://yourdomain.com/api/auth/callback/apple`. Add all necessary return URLs.
* Click `Next`, then `Done`.
* Click `Continue`, then `Save`.
5. **Create a Client Secret Key:**
* Go to the `Keys` tab.
* Click the `+` icon to create a new key.
* **Key Name:** Enter a name for the key (e.g., "Sign In with Apple Key").
* Scroll down and select the checkbox for `Sign In with Apple`.
* Click the `Configure` button next to `Sign In with Apple`.
* Select the **Primary App ID** you created earlier.
* Click `Save`, then `Continue`, then `Register`.
* **Download the Key:** Immediately download the `.p8` key file. **This file is only available for download once.** Note the Key ID (available on the Keys page after creation) and your Team ID (available in your Apple Developer Account settings).
6. **Generate the Client Secret (JWT):**
Apple requires a JSON Web Token (JWT) to be generated dynamically using the downloaded `.p8` key, the Key ID, and your Team ID. This JWT serves as your `clientSecret`.
You can use the guide below from [Apple's documentation](https://developer.apple.com/documentation/accountorganizationaldatasharing/creating-a-client-secret) to understand how to generate this client secret. You can also use our built in generator [below](#generate-apple-client-secret-jwt) to generate the client secret JWT required for 'Sign in with Apple'.
**Note:** Apple allows a maximum expiration of 6 months (180 days) for the client secret JWT. You will need to regenerate the client secret before it expires to maintain uninterrupted authentication.
According to [Apple's documentation](https://developer.apple.com/documentation/accountorganizationaldatasharing/creating-a-client-secret), these JWTs:
* Have a maximum expiration of 6 months
* Must be cryptographically signed with your private key
Install `jose` package to generate the client secret JWT.
```package-install
npm install jose
```
To configure the provider, you need to add it to the `socialProviders` option of the auth instance.
You also need to add `https://appleid.apple.com` to the `trustedOrigins` array in your auth instance configuration to allow communication with Apple's authentication servers.
```ts title="auth.ts"
import { betterAuth } from "better-auth"
import { importPKCS8, SignJWT } from "jose";
// Generate the client secret JWT required for 'Sign in with Apple'.
async function generateAppleClientSecret(clientId, teamId, keyId, privateKey) {
const key = await importPKCS8(privateKey, "ES256");
const now = Math.floor(Date.now() / 1000);
return new SignJWT({})
.setProtectedHeader({ alg: "ES256", kid: keyId })
.setIssuer(teamId)
.setSubject(clientId)
.setAudience("https://appleid.apple.com")
.setIssuedAt(now)
.setExpirationTime(now + 180 * 24 * 60 * 60)
.sign(key);
}
export const auth = betterAuth({
socialProviders: {
apple: { // [!code highlight]
clientId: process.env.APPLE_CLIENT_ID as string, // [!code highlight]
clientSecret: await generateAppleClientSecret(
process.env.APPLE_CLIENT_ID!, // [!code highlight]
process.env.APPLE_TEAM_ID!, // [!code highlight]
process.env.APPLE_KEY_ID!, // [!code highlight]
process.env.APPLE_PRIVATE_KEY!, // [!code highlight]
), // [!code highlight]
// Optional
appBundleIdentifier: process.env.APPLE_APP_BUNDLE_IDENTIFIER as string, // [!code highlight]
}, // [!code highlight]
},
// Add appleid.apple.com to trustedOrigins for Sign In with Apple flows
trustedOrigins: ["https://appleid.apple.com"], // [!code highlight]
})
```
On native iOS, it doesn't use the service ID but the app ID (bundle ID) as client ID, so if using the service ID as `clientId` in `signIn.social` with `idToken`, it throws an error: `JWTClaimValidationFailed: unexpected "aud" claim value`. So you need to provide the `appBundleIdentifier` when you want to sign in with Apple using the ID Token.
<Callout type="warn">
Apple emits the `email` claim only on the first authorization; every subsequent sign-in omits it, and Apple provides no user-info endpoint to fetch it later. See [Handling Providers Without Email](/docs/concepts/oauth#handling-providers-without-email) for persistence guidance and the `mapProfileToUser` fallback.
</Callout>
Apple Sign In does not support localhost or non-HTTPS URLs. During development:
http://localhost as a return URLThis limitation is enforced by Apple's security requirements and cannot be bypassed. </Callout>
To sign in with Apple, you can use the signIn.social function provided by the client. The signIn function takes an object with the following properties:
provider: The provider to use. It should be set to apple.import { createAuthClient } from "better-auth/client"
const authClient = createAuthClient()
const signIn = async () => {
const data = await authClient.signIn.social({
provider: "apple"
})
}
To sign in with Apple using the ID Token, you can use the signIn.social function to pass the ID Token.
This is useful when you have the ID Token from Apple on the client-side and want to use it to sign in on the server.
<Callout> If ID token is provided no redirection will happen, and the user will be signed in directly. </Callout>await authClient.signIn.social({
provider: "apple",
idToken: {
token: // Apple ID Token,
nonce: // Nonce (optional)
accessToken: // Access Token (optional)
}
})