Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
183 changes: 147 additions & 36 deletions docs/06-concepts/11-authentication/04-providers/04-apple/01-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,91 @@

Sign-in with Apple requires that you have a subscription to the [Apple Developer Program](https://developer.apple.com/programs/), even if you only want to test the feature in development mode.

:::note
Right now, we have official support for iOS, macOS, Android, and Web for Sign in with Apple.
Comment thread
Swiftaxe marked this conversation as resolved.
:::

:::caution
You need to install the auth module before you continue, see [Setup](../../setup).
:::

## Create your credentials
## Get your credentials

All platforms require an App ID. Android and Web additionally require a Service ID.

| Platform | App ID | Service ID | Xcode capability | Android intent filter |
| --- | --- | --- | --- | --- |
| iOS / macOS | Required | Not needed | Required | — |
| Android | Required | Required | — | Required |
| Web | Required | Required | — | — |

### Register your App ID

1. In [Certificates, Identifiers & Profiles](https://developer.apple.com/account/resources/identifiers/list), click **Identifiers → +**.

2. Select **App IDs** and click **Continue**.

![Register a new identifier — App IDs selected](/img/authentication/providers/apple/4-app-id-create.png)

3. Select **App** as the type and click **Continue**.

4. Fill in a description and your app's **Bundle ID** (e.g. `com.example.app`).

5. Scroll down to **Capabilities**, find **Sign in with Apple**, and check it. Keep it set as a **primary App ID**.

![App ID capabilities — Sign in with Apple enabled](/img/authentication/providers/apple/5-app-id-capability.png)

6. Click **Continue**, then **Register**.

### Create a Service ID (Android and Web only)

Skip this section if you are building for iOS or macOS only.

1. In Certificates, Identifiers & Profiles, click **Identifiers → +**.

2. Select **Services IDs** and click **Continue**.

![Register a new identifier — Services IDs selected](/img/authentication/providers/apple/6-service-id-create.png)

3. Enter a description and a unique **Identifier** (e.g. `com.example.service`). This value becomes your `serviceIdentifier`. Click **Continue**, then **Register**.

4. Click on the Service ID you just created. Check **Sign in with Apple** and click **Configure**.

5. In the modal, set:
- **Primary App ID**: the App ID from the previous section
- **Domains and Subdomains**: your domain (e.g. `example.com`)
- **Return URLs**: your server's callback route (e.g. `https://example.com/auth/callback`)

![Web Authentication Configuration — Primary App ID, domains, and return URLs](/img/authentication/providers/apple/7-service-id-configure.png)

6. Click **Next**, then **Done**, then **Save**.

:::warning
All return URLs must use **HTTPS**. Apple rejects HTTP redirect URIs. For local development, expose your server over HTTPS using a tunnelling service.
:::

### Create a Sign in with Apple key

1. In Certificates, Identifiers & Profiles, click **Keys → +**.

2. Enter a key name, check **Sign in with Apple**, and click **Configure**. Select your primary App ID and click **Save**.

![Configure key — Sign in with Apple checked, primary App ID selected](/img/authentication/providers/apple/8-key-create.png)

3. Click **Continue**, then **Register**.

1. **Create a Service ID** in the [Apple Developer Portal](https://developer.apple.com/account/resources/identifiers/list/serviceId):
- Register a new Service ID
- Enable "Sign in with Apple"
- Configure domains and redirect URLs
- Note the Service Identifier (e.g., `com.example.service`)
![Register a New Key — review screen](/img/authentication/providers/apple/8-key-register.png)

2. **Create a Key** for Sign in with Apple:
- Go to Keys section in Apple Developer Portal
- Create a new key with "Sign in with Apple" enabled
- Download the key file (`.p8` file) - **you can only download this once**
- Note the Key ID
4. Download the `.p8` key file immediately — **you can only download it once**. Note the **Key ID** shown on this page.

3. **Store the credentials securely** in your `config/passwords.yaml`:
![Download Your Key — one-time download warning with Key ID visible](/img/authentication/providers/apple/8-key-download.png)

5. Find your **Team ID** in your [Apple Developer Account](https://developer.apple.com/account) under Membership.

:::note
Each primary App ID can have a maximum of two private keys. If you reach the limit, revoke an existing key before creating a new one. See [Create a Sign in with Apple private key](https://developer.apple.com/help/account/configure-app-capabilities/create-a-sign-in-with-apple-private-key/) for details.
:::

### Store your credentials

Add the credentials to `config/passwords.yaml`:

```yaml
development:
Expand All @@ -44,8 +106,11 @@ development:
```

:::warning
The Apple private key can only be downloaded once. Store it securely and never commit it to version control. Use environment variables or secure secret management in production.
:::
**Never commit your `.p8` key to version control.** Use environment variables or a secrets manager in production.

**Paste the raw `.p8` key contents** — the full text including `-----BEGIN PRIVATE KEY-----` and `-----END PRIVATE KEY-----`. Do not pre-generate a JWT from it. Serverpod generates the client secret JWT internally on every request.

**Carefully maintain correct indentation for YAML block scalars.** The `appleKey` block uses a `|`; any indentation error will silently break the key, resulting in authentication failures without helpful error messages.

## Server-side configuration

Expand Down Expand Up @@ -99,25 +164,26 @@ void run(List<String> args) async {
:::tip
You can use the `AppleIdpConfigFromPasswords` constructor in replacement of the `AppleIdpConfig` above to automatically load the credentials from the `config/passwords.yaml` file or environment variables. It will expect either the following keys on the file:

- `appleServiceIdentifier`
- `appleBundleIdentifier`
- `appleRedirectUri`
- `appleTeamId`
- `appleKeyId`
- `appleKey`
- `appleWebRedirectUri` (optional, for Web support when using server callback route)
- `appleAndroidPackageIdentifier` (optional, for Android support)
- `appleServiceIdentifier`
- `appleBundleIdentifier`
- `appleRedirectUri`
- `appleTeamId`
- `appleKeyId`
- `appleKey`
- `appleWebRedirectUri` (optional, for Web support when using server callback route)
- `appleAndroidPackageIdentifier` (optional, for Android support)

Or the following environment variables:

- `SERVERPOD_PASSWORD_appleServiceIdentifier`
- `SERVERPOD_PASSWORD_appleBundleIdentifier`
- `SERVERPOD_PASSWORD_appleRedirectUri`
- `SERVERPOD_PASSWORD_appleTeamId`
- `SERVERPOD_PASSWORD_appleKeyId`
- `SERVERPOD_PASSWORD_appleKey`
- `SERVERPOD_PASSWORD_appleWebRedirectUri` (optional, for Web support when using server callback route)
- `SERVERPOD_PASSWORD_appleAndroidPackageIdentifier` (optional, for Android support)
- `SERVERPOD_PASSWORD_appleServiceIdentifier`
- `SERVERPOD_PASSWORD_appleBundleIdentifier`
- `SERVERPOD_PASSWORD_appleRedirectUri`
- `SERVERPOD_PASSWORD_appleTeamId`
- `SERVERPOD_PASSWORD_appleKeyId`
- `SERVERPOD_PASSWORD_appleKey`
- `SERVERPOD_PASSWORD_appleWebRedirectUri` (optional, for Web support when using server callback route)
- `SERVERPOD_PASSWORD_appleAndroidPackageIdentifier` (optional, for Android support)

:::

Then, extend the abstract endpoint to expose it on the server:
Expand All @@ -128,7 +194,16 @@ import 'package:serverpod_auth_idp_server/providers/apple.dart';
class AppleIdpEndpoint extends AppleIdpBaseEndpoint {}
```

Finally, run `serverpod generate` to generate the client code and create a migration to initialize the database for the provider. More detailed instructions can be found in the general [identity providers setup section](../../setup#identity-providers-configuration).
Run `serverpod generate` to generate the client code, then create and apply a database migration to initialize the provider's tables:

```bash
serverpod generate
dart run bin/main.dart --apply-migrations
```

:::note
Skipping the migration will cause the server to crash at runtime when the Apple provider tries to read or write user data. More detailed instructions can be found in the general [identity providers setup section](../../setup#identity-providers-configuration).
:::

### Basic configuration options

Expand All @@ -150,7 +225,9 @@ For more details on configuration options, see the [configuration section](./con

The `serverpod_auth_idp_flutter` package implements the sign-in logic using [sign_in_with_apple](https://pub.dev/packages/sign_in_with_apple). The documentation for this package should in most cases also apply to the Serverpod integration.

_Note that Sign in with Apple may not work on some versions of the Simulator (iOS 13.5 works). This issue doesn't affect real devices._
:::note
Sign in with Apple may not work correctly on all Simulator versions. If you run into issues during development, test on a physical device to confirm whether the problem is Simulator-specific.
:::

### iOS and macOS

Expand All @@ -174,8 +251,24 @@ To enable this:

1. Add the `androidPackageIdentifier` to your `AppleIdpConfig` (or the `appleAndroidPackageIdentifier` key in `passwords.yaml`). This must match your app's Android package name (e.g., `com.example.app`).
2. Configure the redirect URI in your Apple Developer Portal to point to your server's callback route (e.g., `https://example.com/auth/callback`).
3. Register the `signinwithapple` URI scheme in your `AndroidManifest.xml`:

```xml
<activity
android:name="com.linusu.flutter_web_auth_2.CallbackActivity"
android:exported="true">
<intent-filter android:label="flutter_web_auth_2">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="signinwithapple" />
</intent-filter>
</activity>
```

No additional client-side Android configuration is needed beyond what the [sign_in_with_apple](https://pub.dev/packages/sign_in_with_apple) package requires.
:::warning
This intent filter is required. Without it, the OAuth callback never returns to your app and sign-in silently hangs.
:::

### Web

Expand All @@ -188,6 +281,10 @@ To enable this:

If `webRedirectUri` is not configured, Web callbacks to the server route will fail.

:::warning
All redirect URIs must use **HTTPS**. Apple rejects HTTP URLs, including `localhost`. For local development, expose your server over HTTPS using a tunnelling service, like ngrok or Cloudflare Tunnel.
:::

## Present the authentication UI

### Initializing the `AppleSignInService`
Expand Down Expand Up @@ -233,9 +330,23 @@ AppleSignInWidget(
)
```

This renders an Apple sign-in button like this:

![Apple sign-in button](/img/authentication/providers/apple/3-button.png)

The widget automatically handles:

- Apple Sign-In flow for iOS, macOS, Android, and Web.
- Token management.
- Underlying Apple Sign-In package error handling.

For details on how to customize the Apple Sign-In UI in your Flutter app, see the [customizing the UI section](./customizing-the-ui).

:::warning
**Apple sends the user's email address and full name only on the first sign-in.** On all subsequent sign-ins, neither is included in the response. If your server does not persist them during that first authentication, they cannot be retrieved later.

Use the `sub` claim (the stable user identifier) to identify users. Do not use the email address, as it may change when a user updates their "Hide My Email" settings. For more information, see [Authenticating users with Sign in with Apple](https://developer.apple.com/documentation/sign_in_with_apple/authenticating-users-with-sign-in-with-apple).

---

If you run into issues, see the [troubleshooting guide](./troubleshooting).
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,26 @@ Apple Sign-In requires web routes for handling callbacks and notifications. Thes
The `revokedNotificationRoutePath` is the path that Apple will call when a user revokes their authorization. The `webAuthenticationCallbackRoutePath` is the path that Apple will call when a user completes the sign-in process.

These routes are configured in the `pod.configureAppleIdpRoutes()` method:

```dart
pod.configureAppleIdpRoutes(
revokedNotificationRoutePath: '/hooks/apple-notification',
webAuthenticationCallbackRoutePath: '/auth/callback',
);
```

### Configuring Apple Sign-In on the App
- `revokedNotificationRoutePath` (default: `'/hooks/apple-notification'`): The path Apple calls when a user revokes authorization. Register this URL in your Apple Developer Portal for server-to-server notifications.
- `webAuthenticationCallbackRoutePath` (default: `'/auth/callback'`): The path Apple redirects to after the user completes web-based sign-in. Must match the return URL registered on your Service ID.

:::note
When a user revokes access from their Apple ID settings, Apple sends a notification to `revokedNotificationRoutePath`. Serverpod receives this notification automatically. You are responsible for invalidating any active sessions for that user in your own application logic.
:::

## Configuring Apple Sign-In on the app

Apple Sign-In requires additional configuration for web and Android platforms. On native Apple platforms (iOS/macOS), the configuration is automatically handled by the underlying `sign_in_with_apple` package through Xcode capabilities.
Apple Sign-In requires additional configuration for web and Android platforms. On native Apple platforms (iOS/macOS), the configuration is handled automatically by the underlying `sign_in_with_apple` package through Xcode capabilities.

#### Passing Configuration in Code
### Passing configuration in code

You can pass the configuration directly when initializing the Apple Sign-In service:

Expand All @@ -65,18 +73,23 @@ client.auth.initializeAppleSignIn(

The `serviceIdentifier` is your Apple Services ID (configured in Apple Developer Portal), and the `redirectUri` is the callback URL that Apple will redirect to after authentication (must match the URL configured on the server).

Both parameters are optional. If not supplied, the provider falls back to the corresponding `--dart-define` build variable:

- `serviceIdentifier` → `APPLE_SERVICE_IDENTIFIER`
- `redirectUri` → `APPLE_REDIRECT_URI`

Comment thread
Zfinix marked this conversation as resolved.
:::note
These parameters are only required for web and Android platforms. On native Apple platforms (iOS/macOS), they are ignored and the configuration from Xcode capabilities is used instead.
These parameters are only required for web and Android platforms. On native Apple platforms (iOS/macOS), they are ignored, and the configuration from Xcode capabilities is used instead.
:::

#### Using Environment Variables
### Using Environment Variables

Alternatively, you can pass configuration during build time using the `--dart-define` option. The Apple Sign-In provider supports the following environment variables:
Alternatively, you can pass configuration during build time using the `--dart-define` option:

- `APPLE_SERVICE_IDENTIFIER`: The Apple Services ID.
- `APPLE_REDIRECT_URI`: The redirect URI for authentication callbacks.

If `serviceIdentifier` and `redirectUri` values are not supplied when initializing the service, the provider will automatically fetch them from these environment variables.
If you do not supply `serviceIdentifier` and `redirectUri` values when initializing the service, the provider will automatically fetch them from these environment variables.

**Example usage:**

Expand All @@ -91,8 +104,34 @@ This approach is useful when you need to:

- Manage configuration separately for different platforms (Android, Web) in a centralized way.
- Avoid committing sensitive configuration to version control.
- Configure different credentials for different build environments (development, staging, production).
- Configure different credentials for different build environments, like development, staging, and production.

:::tip
You can also set these environment variables in your IDE's run configuration or CI/CD pipeline to avoid passing them manually each time.
:::

## `AppleIdpConfig` parameter reference

| Parameter | Type | Required | `passwords.yaml` key | Description |
| --- | --- | --- | --- | --- |
| `serviceIdentifier` | `String` | Yes (Android/Web) | `appleServiceIdentifier` | The Services ID identifier (e.g. `com.example.service`). Used as the OAuth client ID for Android and Web. Not required for iOS/macOS-only setups. |
| `bundleIdentifier` | `String` | Yes | `appleBundleIdentifier` | The App ID bundle identifier (e.g. `com.example.app`). Used as the client ID for native Apple platform sign-in. |
| `redirectUri` | `String` | Yes (Android/Web) | `appleRedirectUri` | The server callback route Apple redirects to after sign-in (e.g. `https://example.com/auth/callback`). Must be HTTPS and match the return URL registered on your Service ID. |
| `teamId` | `String` | Yes | `appleTeamId` | The 10-character Team ID from your Apple Developer account (e.g. `ABC123DEF4`). Used to sign the client secret JWT. |
| `keyId` | `String` | Yes | `appleKeyId` | The Key ID of the Sign in with Apple private key (e.g. `XYZ789ABC0`). |
| `key` | `String` | Yes | `appleKey` | The raw contents of the `.p8` private key file, including the `-----BEGIN PRIVATE KEY-----` header and footer. Serverpod uses this to generate a short-lived client secret JWT on each request. Do not pre-generate the JWT yourself. |
| `webRedirectUri` | `String?` | Web only | `appleWebRedirectUri` | The web app URL that the browser is redirected to after the server receives Apple's callback. This is required when using the server callback route for Web. |
| `androidPackageIdentifier` | `String?` | Android only | `appleAndroidPackageIdentifier` | The Android package name (e.g. `com.example.app`). When set, the callback route redirects Android clients back to the app via an intent URI using the `signinwithapple` scheme. |

### Environment Variable equivalents

All `passwords.yaml` keys can be set as environment variables by prefixing with `SERVERPOD_PASSWORD_`:

- `appleServiceIdentifier` → `SERVERPOD_PASSWORD_appleServiceIdentifier`
- `appleBundleIdentifier` → `SERVERPOD_PASSWORD_appleBundleIdentifier`
- `appleRedirectUri` → `SERVERPOD_PASSWORD_appleRedirectUri`
- `appleTeamId` → `SERVERPOD_PASSWORD_appleTeamId`
- `appleKeyId` → `SERVERPOD_PASSWORD_appleKeyId`
- `appleKey` → `SERVERPOD_PASSWORD_appleKey`
- `appleWebRedirectUri` → `SERVERPOD_PASSWORD_appleWebRedirectUri`
- `appleAndroidPackageIdentifier` → `SERVERPOD_PASSWORD_appleAndroidPackageIdentifier`
Loading
Loading