# Sign In with Apple

How to add Sign In with Apple to your Instant app.



Instant supports Sign In with Apple on the Web and in native applications.


- **Web Popup (recommended)**: Use Apple-provided popup to authenticate users
- **Web Redirect**: Use redirect flow to authenticate users
- **React Native**: Authenticating in React Native app


## Step 1: Create App ID

- Navigate to [Certificates, Identifiers & Profiles](https://developer.apple.com/account/resources/identifiers/list)
- Select _Identifiers_
- Click _+_
- _Register a new identifier_ → Select _App IDs_
- _Select a type_ → Select _App_
- _Capabilities_ → _Sign In with Apple_ → Check
- Fill in _Bundle ID_ and _Description_
- Click _Register_

## Step 2: Create Services ID

- Navigate to [Services IDs](https://developer.apple.com/account/resources/identifiers/list/serviceId)
- Click _+_
- _Register a new identifier_ → Select _Services IDs_
- Fill in _Description_ and _Identifier_. You’ll need this _Identifier_ later
- Click _Register_



## Step 3: Configure Services ID (Web Popup flow)

- Select newly created Services ID
- Enable _Sign In with Apple_
- Click _Configure_
- Select _Primary App ID_ from Step 1
- To _Domains_, add your app domain (e.g. `myapp.com`)
- To _Return URLs_, add URL of your app where authentication happens (e.g. `https://myapp.com/signin`)
- Click _Continue_ → _Save_





## Step 3: Configure Services ID (Web Redirect flow)

- Select newly created Services ID
- Enable _Sign In with Apple_
- Click _Configure_
- Select _Primary App ID_ from Step 1
- To _Domains_, add `api.instantdb.com`
- To _Return URLs_, add `https://api.instantdb.com/runtime/oauth/callback`
- Click _Continue_ → _Save_

## Step 3.5: Generate Private Key (Web Redirect flow only)

- Navigate to [Keys](https://developer.apple.com/account/resources/authkeys/list)
- Click _+_
- Fill in _Name_ and _Description_
- Check _Sign in with Apple_
- Configure → select _App ID_ from Step 1
- _Continue_ → _Register_
- Download key file





## Step 3: Configure Services ID (React Native flow)

This step is not needed for Expo.


## Step 4: Register your OAuth client with Instant





**From the dashboard**


From the [Auth](/dash?s=main&t=auth) tab on the Instant dashboard:

- Click "Add Apple Client"
- Select a unique client name (`apple` by default, used in `db.auth` calls)
- Fill in your "Services ID" from Step 2
- Fill in your "Team ID" from [Membership details](https://developer.apple.com/account#MembershipDetailsCard)
- Fill in your "Key ID" from Step 3.5
- Fill in your "Private Key" by copying the file content from Step 3.5
- Click "Add Apple Client"



**From the terminal**


```shell
npx instant-cli@latest auth client add \
  --type apple --name apple \
  --services-id <services-id> \
  --team-id <team-id> --key-id <key-id> \
  --private-key-file <path-to-key.p8>
```









**From the dashboard**


From the [Auth](/dash?s=main&t=auth) tab on the Instant dashboard:

- Click "Add Apple Client"
- Select a unique client name (`apple` by default, used in `db.auth` calls)
- Fill in your "Services ID" from Step 2
- Click "Add Apple Client"



**From the terminal**


```shell
npx instant-cli@latest auth client add \
  --type apple --name apple --services-id <services-id>
```










## Step 4.5: Whitelist your domain in Instant (Web Redirect flow only)



**From the dashboard**


From the [Auth](/dash?s=main&t=auth) tab on the Instant dashboard:

- Click "Redirect Origins" → "Add an origin"
- Add your app's domain (e.g. `myapp.com`)



**From the terminal**


```shell
npx instant-cli@latest auth origin add --type website --url myapp.com
```









## Step 5: Add Sign In code to your app (Web Popup flow)

Add Apple Sign In library to your app:

```
https://appleid.cdn-apple.com/appleauth/static/jsapi/appleid/1/en_US/appleid.auth.js
```

Initialize with `Services ID` from Step 2:

```jsx 
AppleID.auth.init({
  clientId: '<Services ID>',
  scope: 'name email',
  redirectURI: window.location.href,
});
```

Implement `signInPopup` using `clientName` from Step 4:

```jsx 
async function signInPopup() {
  let nonce = crypto.randomUUID();

  // authenticate with Apple
  let resp = await AppleID.auth.signIn({
    nonce: nonce,
    usePopup: true,
  });

  // authenticate with Instant
  await db.auth.signInWithIdToken({
    clientName: '<clientName>',
    idToken: resp.authorization.id_token,
    nonce: nonce,
  });
}
```

Add Sign In button:

```jsx 
<button onClick={signInPopup}>Sign In with Apple</button>
```





## Step 5: Add Sign In code to your app (Web Popup flow)

Create Sign In link using `clientName` from Step 4:

```
const authUrl = db.auth.createAuthorizationURL({
  clientName: '<clientName>',
  redirectURL: window.location.href,
});
```

Add a link uses `authUrl`:

```
<a href={ authUrl }>Sign In with Apple</a>
```

That’s it!




## Step 5: Add Sign In code to your app (React Native flow)

Instant comes with support for [Expo AppleAuthentication library](https://docs.expo.dev/versions/latest/sdk/apple-authentication/).

Add dependency:

```shell 
npx expo install expo-apple-authentication
```

Update `app.json` by adding:

```json 
{
  "expo": {
    "ios": {
      "usesAppleSignIn": true
    }
  }
}
```

Add `exp://` as a redirect origin for development with Expo:



**From the dashboard**


From the [Auth](/dash?s=main&t=auth) tab on the Instant dashboard:

- Click "Redirect Origins" → "Add an origin"
- Add `exp://`



**From the terminal**


```shell
npx instant-cli@latest auth origin add --type custom-scheme --scheme exp://
```





Authenticate with Apple and then pass identityToken to Instant along with `clientName` from Step 4:

```jsx 
const [nonce] = useState('' + Math.random());
try {
  // sign in with Apple
  const credential = await AppleAuthentication.signInAsync({
    requestedScopes: [
      AppleAuthentication.AppleAuthenticationScope.FULL_NAME,
      AppleAuthentication.AppleAuthenticationScope.EMAIL,
    ],
    nonce: nonce,
  });

  // pass identityToken to Instant
  db.auth
    .signInWithIdToken({
      clientName: '<clientName>',
      idToken: credential.identityToken,
      nonce: nonce,
    })
    .catch((err) => {
      console.log('Error', err.body?.message, err);
    });
} catch (e) {
  if (e.code === 'ERR_REQUEST_CANCELED') {
    // handle that the user canceled the sign-in flow
  } else {
    // handle other errors
  }
}
```

Sign out code:

```jsx 
<Button
  title="Sign Out"
  onPress={async () => {
    await db.auth.signOut();
  }}
/>
```

Full example:

```jsx 
import React, { useState } from 'react';
import { Button, View, Text, StyleSheet } from 'react-native';
import { init, tx } from '@instantdb/react-native';
import * as AppleAuthentication from 'expo-apple-authentication';

const APP_ID = '__APP_ID__';
const db = init({ appId: APP_ID });

function App() {
  return (
    <>
      <db.SignedIn>
        <UserInfo />
      </db.SignedIn>
      <db.SignedOut>
        <Login />
      </db.SignedOut>
    </>
  );
}

function UserInfo() {
  const user = db.useUser();
  return (
    <View style={styles.container}>
      <Text>Hello {user.email}!</Text>
      <Button
        title="Sign Out"
        onPress={async () => {
          await db.auth.signOut();
        }}
      />
    </View>
  );
}

function Login() {
  const [nonce] = useState('' + Math.random());
  return (
    <View style={styles.container}>
      <AppleAuthentication.AppleAuthenticationButton
        buttonType={AppleAuthentication.AppleAuthenticationButtonType.SIGN_IN}
        buttonStyle={AppleAuthentication.AppleAuthenticationButtonStyle.BLACK}
        cornerRadius={5}
        style={styles.button}
        onPress={async () => {
          try {
            const credential = await AppleAuthentication.signInAsync({
              requestedScopes: [
                AppleAuthentication.AppleAuthenticationScope.FULL_NAME,
                AppleAuthentication.AppleAuthenticationScope.EMAIL,
              ],
              nonce: nonce,
            });
            // signed in
            db.auth
              .signInWithIdToken({
                clientName: 'apple',
                idToken: credential.identityToken,
                nonce: nonce,
              })
              .catch((err) => {
                console.log('Error', err.body?.message, err);
              });
          } catch (e) {
            if (e.code === 'ERR_REQUEST_CANCELED') {
              // handle that the user canceled the sign-in flow
            } else {
              // handle other errors
            }
          }
        }}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  button: {
    width: 200,
    height: 44,
  },
});

export default App;
```




