diff --git a/docs/_partials/expo/use-sign-in-with-apple-example.mdx b/docs/_partials/expo/use-sign-in-with-apple-example.mdx new file mode 100644 index 0000000000..144a234fd7 --- /dev/null +++ b/docs/_partials/expo/use-sign-in-with-apple-example.mdx @@ -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 ( + <> + + Sign in with Apple + + + {showDivider && ( + + + OR + + + )} + + ) +} + +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', + }, +}) +``` diff --git a/docs/_tooltips/transfer-flow.mdx b/docs/_tooltips/transfer-flow.mdx new file mode 100644 index 0000000000..f2eaef5829 --- /dev/null +++ b/docs/_tooltips/transfer-flow.mdx @@ -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. diff --git a/docs/guides/configure/auth-strategies/sign-in-with-apple.expo.mdx b/docs/guides/configure/auth-strategies/sign-in-with-apple.expo.mdx new file mode 100644 index 0000000000..1f4715f023 --- /dev/null +++ b/docs/guides/configure/auth-strategies/sign-in-with-apple.expo.mdx @@ -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. + + + ## 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`. + + + ```json {{ filename: 'app.json' }} + { + "expo": { + "plugins": ["expo-apple-authentication"] + } + } + ``` + + ```js {{ filename: 'app.config.js' }} + export default { + expo: { + plugins: ['expo-apple-authentication'], + }, + } + ``` + + + ## Build your authentication flow + + + + ## 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 + ``` + diff --git a/docs/guides/configure/auth-strategies/sign-in-with-apple.mdx b/docs/guides/configure/auth-strategies/sign-in-with-apple.mdx index 81996a5604..6161dbe332 100644 --- a/docs/guides/configure/auth-strategies/sign-in-with-apple.mdx +++ b/docs/guides/configure/auth-strategies/sign-in-with-apple.mdx @@ -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. ## Add your Native Application diff --git a/docs/manifest.json b/docs/manifest.json index c935a3fba2..1ffc12ed69 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -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" diff --git a/docs/reference/expo/overview.mdx b/docs/reference/expo/overview.mdx index 14e593656d..6d003ef0ef 100644 --- a/docs/reference/expo/overview.mdx +++ b/docs/reference/expo/overview.mdx @@ -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) diff --git a/docs/reference/expo/use-sign-in-with-apple.mdx b/docs/reference/expo/use-sign-in-with-apple.mdx new file mode 100644 index 0000000000..883002b2ed --- /dev/null +++ b/docs/reference/expo/use-sign-in-with-apple.mdx @@ -0,0 +1,149 @@ +--- +title: '`useSignInWithApple()`' +description: Clerk's useSignInWithApple() hook provides native Sign in with Apple functionality for iOS devices. +sdk: expo +--- + + + +> [!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 +``` + +#### Parameters + +`startAppleAuthenticationFlow()` accepts the following parameters (`StartAppleAuthenticationFlowParams`): + + + - `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). + + +#### Returns + +`startAppleAuthenticationFlow()` returns the following: + + + - `createdSessionId` + - `string | null` + + The ID of the session that was created, if authentication is successful. + + --- + + - `setActive?` + - `(params: SetActiveParams) => Promise` + + A method used to set the active session and/or organization. Accepts a [`SetActiveParams`](/docs/reference/javascript/types/set-active-params) object. + + --- + + - `signIn?` + - [SignIn](/docs/reference/javascript/sign-in) | undefined + + 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?` + - [SignUp](/docs/reference/javascript/sign-up) | undefined + + 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. + + +## Examples + +### Reusable component + + + +### 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 ( + + Sign in with Apple + + ) +} +``` + +## Error handling + +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.