Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
3951757
Add Sign in with Apple guide and `useAppleSignIn()` hook documentation
chriscanin Oct 21, 2025
576b3c8
Refactor Sign in with Apple guide: streamline prerequisites and enhan…
chriscanin Oct 22, 2025
5ecdac0
Update link for customizing session tokens in Sign in with Apple guide.
chriscanin Oct 22, 2025
0e2e51e
Fix list numbering in prerequisites and flow description for `useAppl…
chriscanin Oct 22, 2025
0dd6af1
Merge branch 'main' into chris/mobile-279-expo-sign-in-with-apple
chriscanin Oct 22, 2025
06285d9
docs review
SarahSoutoul Oct 22, 2025
aa1a5f9
Merge branch 'main' into chris/mobile-279-expo-sign-in-with-apple
SarahSoutoul Oct 24, 2025
2477e58
Rename `useAppleSignIn()` to `useSignInWithApple()` across documentat…
chriscanin Oct 27, 2025
df3a925
Apply suggestion from @manovotny
chriscanin Oct 28, 2025
85c6d9e
Replace prerequisites section with a TutorialHero component linking t…
chriscanin Oct 28, 2025
9ee1f79
Merge branch 'main' into chris/mobile-279-expo-sign-in-with-apple
chriscanin Oct 28, 2025
280c19f
docs review pt2
SarahSoutoul Oct 29, 2025
76a1c22
Merge branch 'main' into chris/mobile-279-expo-sign-in-with-apple
chriscanin Oct 29, 2025
75c9865
Add apple sign in note to ios guide
SarahSoutoul Oct 29, 2025
5e3383c
Merge branch 'main' into chris/mobile-279-expo-sign-in-with-apple
alexisintech Oct 29, 2025
eddb2e7
docs review
alexisintech Oct 29, 2025
de32d49
Fix partial src
NWylynko Oct 30, 2025
506b70e
Merge branch 'main' into chris/mobile-279-expo-sign-in-with-apple
chriscanin Nov 4, 2025
dfdd0a9
Refactor Sign in with Apple guide to update partial path and restore …
chriscanin Nov 4, 2025
90e009b
Fix path for reusable component example in useSignInWithApple documen…
chriscanin Nov 4, 2025
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
1 change: 1 addition & 0 deletions docs/_tooltips/transfer-flow.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
A **transfer flow** allows a user to both sign in and sign up in the same process. If a user tries signing up, but already exists, the flow will transfer the user to the sign-in flow. If a user tries signing in, but doesn't exist, the flow will transfer the user to the sign-up flow.
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
---
title: Sign in with Apple
description: Learn how to use Clerk to natively Sign in with Apple in your Expo app.
sdk: expo
---

This guide will teach you how to add native [Sign in with Apple](https://developer.apple.com/sign-in-with-apple/) to your Clerk Expo application.

> [!NOTE]
> Apple Sign-In works on both iOS Simulators and physical devices. However, physical devices provide full functionality including biometric authentication (Face ID/Touch ID), while simulators have limited support. Always test on a physical device before releasing to production.

<Steps>
## Add your Native Application

Add your iOS application to the [**Native Applications**](https://dashboard.clerk.com/last-active?path=native-applications) page in the Clerk Dashboard. You will need your iOS app's **App ID Prefix** (Team ID) and **Bundle ID**.

## Enable Apple as a social connection

1. In the Clerk Dashboard, navigate to the [**SSO Connections**](https://dashboard.clerk.com/last-active?path=user-authentication/sso-connections) page.
1. Select **Add connection** and select **For all users**.
1. In the **Choose provider** dropdown, select **Apple**.
1. Ensure that **Enable for sign-up and sign-in** is toggled on.

> [!NOTE]
> Apple provides a privacy feature called [Hide My Email](https://support.apple.com/en-us/HT210425#hideemail), allowing users to sign in to your app with Apple without disclosing their actual email addresses. Instead, your instance receives an app-specific email address that forwards any emails to the user's real address. To be able to send emails properly to users with hidden addresses, you must configure an additional setting in the Apple Developer portal. See [Configure Email Source for Apple Private Relay](/docs/guides/configure/auth-strategies/social-connections/apple#configure-email-source-for-apple-private-relay){{ target: '_blank' }} for more information.

## Install dependencies

The [Expo Apple Authentication library](https://docs.expo.dev/versions/latest/sdk/apple-authentication/) provides access to Apple's native Sign in with Apple functionality from your Expo app.

Run the following command to install the library:

```npm {{ filename: 'terminal' }}
npx expo install expo-apple-authentication
```

## Add `expo-apple-authentication` to your app config

Add the `expo-apple-authentication` plugin to your `app.json` or `app.config.js`.

<CodeBlockTabs options={["app.json", "app.config.js"]}>
```json {{ filename: 'app.json' }}
{
"expo": {
"plugins": ["expo-apple-authentication"]
}
}
```

```js {{ filename: 'app.config.js' }}
export default {
expo: {
plugins: ['expo-apple-authentication'],
},
}
```
</CodeBlockTabs>

## Build your authentication flow

<Include src="../../../reference/expo/_partials/use-sign-in-with-apple-example" />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure a relative partial makes sense if its gonna be used here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alexisintech would like your input on using a relative partial like this. My thought is the purpose of them is for partials that will only be used in certain sections of the docs. In this case I think putting the partial in the top level _partials folder makes more sense, maybe under a _partials/expo folder. But If you're happy for them to be references & managed like this then I've got no problem with that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a top level _partials/expo would be a good solution, that way all partials are in one place (root _partials), but can also be separated by package if need be.

I am still working with SDK team to resolve some issues on the JS PR, so no worries for the moment.


## Create a native build

Create a native build with EAS Build or a local prebuild, since Apple Authentication is not supported in Expo Go.

```bash {{ filename: 'terminal' }}
# Using EAS Build
eas build --platform ios

# Or using local prebuild
npx expo prebuild && npx expo run:ios --device
```
</Steps>
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ description: Learn how to use Clerk to natively Sign in with Apple.
sdk: ios
---

This guide will teach you how to add native Sign in with Apple to your Clerk apps on Apple platforms.
This guide will teach you how to add native [Sign in with Apple](https://developer.apple.com/sign-in-with-apple/) to your Clerk apps on Apple platforms.

> [!NOTE]
> Apple Sign-In works on both iOS Simulators and physical devices. However, physical devices provide full functionality including biometric authentication (Face ID/Touch ID), while simulators have limited support. Always test on a physical device before releasing to production.

<Steps>
## Add your Native Application
Expand Down
4 changes: 4 additions & 0 deletions docs/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -1578,6 +1578,10 @@
"title": "`useOAuth()` (deprecated)",
"href": "/docs/reference/expo/use-oauth"
},
{
"title": "`useSignInWithApple()`",
"href": "/docs/reference/expo/use-sign-in-with-apple"
},
{
"title": "`useSSO()`",
"href": "/docs/reference/expo/use-sso"
Expand Down
95 changes: 95 additions & 0 deletions docs/reference/expo/_partials/use-sign-in-with-apple-example.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
The following example demonstrates how to use the [`useSignInWithApple()`](/docs/reference/expo/use-sign-in-with-apple) hook to manage the Apple authentication flow. Because the `useSignInWithApple()` hook automatically manages the [transfer flow](!transfer-flow) between sign-up and sign-in, you can use this component for both your sign-up and sign-in pages.

```tsx {{ filename: 'components/AppleSignInButton.tsx', collapsible: true }}
import { useSignInWithApple } from '@clerk/clerk-expo'
import { useRouter } from 'expo-router'
import { Alert, Platform, StyleSheet, Text, TouchableOpacity, View } from 'react-native'

// Example props that you could pass to your button
interface AppleSignInButtonProps {
// Callback function that is called when the sign-in is complete
onSignInComplete?: () => void
// Whether to show a divider between the button and the text
showDivider?: boolean
}

export function AppleSignInButton({
onSignInComplete,
showDivider = true,
}: AppleSignInButtonProps) {
const { startAppleAuthenticationFlow } = useSignInWithApple()
const router = useRouter()

// Only render on iOS
if (Platform.OS !== 'ios') {
return null
}

const handleAppleSignIn = async () => {
try {
const { createdSessionId, setActive } = await startAppleAuthenticationFlow()

if (createdSessionId && setActive) {
// Set the created session as the active session
await setActive({ session: createdSessionId })

// Once the session is set as active,
// if a callback function is provided, call it.
// Otherwise, redirect to the home page.
onSignInComplete ? onSignInComplete() : router.replace('/')
}
} catch (err: any) {
// User canceled the sign-in flow
if (err.code === 'ERR_REQUEST_CANCELED') return

Alert.alert('Error', err.message || 'An error occurred during Apple Sign-In')
console.error('Apple Sign-In error:', JSON.stringify(err, null, 2))
}
}

return (
<>
<TouchableOpacity style={styles.appleButton} onPress={handleAppleSignIn}>
<Text style={styles.appleButtonText}>Sign in with Apple</Text>
</TouchableOpacity>

{showDivider && (
<View style={styles.divider}>
<View style={styles.dividerLine} />
<Text style={styles.dividerText}>OR</Text>
<View style={styles.dividerLine} />
</View>
)}
</>
)
}

const styles = StyleSheet.create({
appleButton: {
backgroundColor: '#000',
padding: 15,
borderRadius: 8,
alignItems: 'center',
marginBottom: 10,
},
appleButtonText: {
color: '#fff',
fontSize: 16,
fontWeight: '600',
},
divider: {
flexDirection: 'row',
alignItems: 'center',
marginVertical: 20,
},
dividerLine: {
flex: 1,
height: 1,
backgroundColor: '#ccc',
},
dividerText: {
marginHorizontal: 10,
color: '#666',
},
})
```
1 change: 1 addition & 0 deletions docs/reference/expo/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ The Expo SDK gives you access to the following resources:

The Expo SDK provides the following hooks:

- [`useSignInWithApple()`](/docs/reference/expo/use-sign-in-with-apple)
- [`useSSO()`](/docs/reference/expo/use-sso)
- [`useLocalCredentials()`](/docs/reference/expo/use-local-credentials)

Expand Down
149 changes: 149 additions & 0 deletions docs/reference/expo/use-sign-in-with-apple.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
---
title: '`useSignInWithApple()`'
description: Clerk's useSignInWithApple() hook provides native Sign in with Apple functionality for iOS devices.
sdk: expo
---

<TutorialHero
beforeYouStart={[
{
title: "Add native Sign in with Apple to your Expo app",
link: "/docs/guides/configure/auth-strategies/sign-in-with-apple",
icon: "clerk",
}
]}
/>

> [!NOTE]
> This hook is only available on iOS devices and requires a native build. It will not work with Expo Go.

The `useSignInWithApple()` hook provides native [Sign in with Apple](https://developer.apple.com/sign-in-with-apple/) functionality for iOS devices. It handles the ID token exchange with Clerk's backend and automatically manages the [transfer flow](!transfer-flow) between sign-up and sign-in.

## Returns

The `useSignInWithApple()` hook returns the `startAppleAuthenticationFlow()` method, which you can use to initiate the native Apple authentication flow.

### `startAppleAuthenticationFlow()`

`startAppleAuthenticationFlow()` has the following function signature:

```ts
function startAppleAuthenticationFlow(
startAppleAuthenticationFlowParams?: StartAppleAuthenticationFlowParams,
): Promise<StartAppleAuthenticationFlowReturnType>
```

#### Parameters

`startAppleAuthenticationFlow()` accepts the following parameters (`StartAppleAuthenticationFlowParams`):

<Properties>
- `unsafeMetadata?`
- [`SignUpUnsafeMetadata`](/docs/reference/javascript/types/metadata#sign-up-unsafe-metadata)

Metadata that can be read and set from the frontend and the backend. Once the authentication process is complete, the value of this field will be automatically copied to the created user's unsafe metadata (`User.unsafeMetadata`). One common use case is to collect custom information about the user during the authentication process and store it in this property. Read more about [unsafe metadata](/docs/guides/users/extending#unsafe-metadata).
</Properties>

#### Returns

`startAppleAuthenticationFlow()` returns the following:

<Properties>
- `createdSessionId`
- `string | null`

The ID of the session that was created, if authentication is successful.

---

- `setActive?`
- `(params: SetActiveParams) => Promise<void>`

A method used to set the active session and/or organization. Accepts a [`SetActiveParams`](/docs/reference/javascript/types/set-active-params) object.

---

- `signIn?`
- <code>[SignIn](/docs/reference/javascript/sign-in) | undefined</code>

The [`SignIn`](/docs/reference/javascript/sign-in) object that was created, which holds the state of the current sign-in and provides helper methods to navigate and complete the sign-in process.

---

- `signUp?`
- <code>[SignUp](/docs/reference/javascript/sign-up) | undefined</code>

The [`SignUp`](/docs/reference/javascript/sign-up) object that was created, which holds the state of the current sign-up and provides helper methods to navigate and complete the sign-up process.
</Properties>

## Examples

### Reusable component

<Include src="./_partials/use-sign-in-with-apple-example" />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chriscanin this is causing the build to fail - should it be using the partial?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Working on it!


### With custom metadata

The following example demonstrates how to pass custom metadata that will be saved to the user's [unsafe metadata](/docs/guides/users/extending#unsafe-metadata) during sign-up.

```tsx {{ filename: 'app/(auth)/sign-in.tsx' }}
import { useRouter } from 'expo-router'
import { useSignInWithApple } from '@clerk/clerk-expo'
import { Alert, Platform, TouchableOpacity, Text } from 'react-native'

export default function SignInPage() {
const { startAppleAuthenticationFlow } = useSignInWithApple()
const router = useRouter()

// Only render on iOS
if (Platform.OS !== 'ios') {
return null
}

const onAppleSignInPress = async () => {
try {
const { createdSessionId, setActive } = await startAppleAuthenticationFlow({
// Add information about the user to their unsafe metadata
unsafeMetadata: {
referralSource: 'ios-app',
signupDate: new Date().toISOString(),
},
})

if (createdSessionId && setActive) {
// Set the created session as the active session
await setActive({ session: createdSessionId })
// Once the session is set as active,
// redirect the user to the home page
router.replace('/')
}
} catch (err: any) {
// User canceled the authentication flow
if (err.code === 'ERR_REQUEST_CANCELED') {
return
}

Alert.alert('Error', err.message || 'An error occurred during Apple Sign-In')
console.error('Apple Sign-In error:', JSON.stringify(err, null, 2))
}
}

return (
<TouchableOpacity onPress={onAppleSignInPress}>
<Text>Sign in with Apple</Text>
</TouchableOpacity>
)
}
```

## Error handling
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i love this section 😸💖


The `useSignInWithApple()` hook may throw errors in the following scenarios:

- **User cancellation**: The user cancels the authentication flow, resulting in an error with code `ERR_REQUEST_CANCELED`.
- **Platform error**: The hook is called on a non-iOS platform.
- **Missing package**: The `expo-apple-authentication` package is not installed.
- **Authentication failure**: Apple authentication fails or the returned ID token is invalid.

> [!IMPORTANT]
> Always wrap calls to `startAppleAuthenticationFlow()` in a `try/catch` block, and handle the `ERR_REQUEST_CANCELED` error separately.