expo-apple-authentication
Version: 55.0.6
Provides ‘Sign in with Apple’ capability for Expo and React Native apps. Allows users to authenticate using their Apple ID with Face ID or Touch ID on iOS 13+.
Installation
npx expo install expo-apple-authentication
Usage
import * as AppleAuthentication from 'expo-apple-authentication';
import { View, StyleSheet } from 'react-native';
export default function App() {
const handleAppleSignIn = async () => {
try {
const credential = await AppleAuthentication.signInAsync({
requestedScopes: [
AppleAuthentication.AppleAuthenticationScope.FULL_NAME,
AppleAuthentication.AppleAuthenticationScope.EMAIL,
],
});
// Signed in successfully
console.log('User ID:', credential.user);
console.log('Email:', credential.email);
console.log('Name:', credential.fullName);
} catch (e: any) {
if (e.code === 'ERR_CANCELED') {
// User canceled
} else {
// Other error
}
}
};
return (
<View style={styles.container}>
<AppleAuthentication.AppleAuthenticationButton
buttonType={AppleAuthentication.AppleAuthenticationButtonType.SIGN_IN}
buttonStyle={AppleAuthentication.AppleAuthenticationButtonStyle.BLACK}
cornerRadius={5}
style={styles.button}
onPress={handleAppleSignIn}
/>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
button: { width: 200, height: 44 },
});
API Reference
Methods
Checks if Sign in with Apple is availableReturns true on iOS 13+ devices, false otherwiseconst available = await AppleAuthentication.isAvailableAsync();
if (available) {
// Show Sign in with Apple button
}
signInAsync(options)
(options: AppleAuthenticationSignInOptions) => Promise<AppleAuthenticationCredential>
Initiates Sign in with Apple flowOpens native authentication dialogconst credential = await AppleAuthentication.signInAsync({
requestedScopes: [
AppleAuthentication.AppleAuthenticationScope.FULL_NAME,
AppleAuthentication.AppleAuthenticationScope.EMAIL,
],
});
refreshAsync(options)
(options: AppleAuthenticationRefreshOptions) => Promise<AppleAuthenticationCredential>
Refreshes user credentialsconst credential = await AppleAuthentication.refreshAsync({
user: userId,
});
signOutAsync(options)
(options: AppleAuthenticationSignOutOptions) => Promise<void>
Signs out the userawait AppleAuthentication.signOutAsync({
user: userId,
});
getCredentialStateAsync(user)
(user: string) => Promise<AppleAuthenticationCredentialState>
Checks credential state for a userReturns one of: AUTHORIZED, REVOKED, NOT_FOUND, TRANSFERREDconst state = await AppleAuthentication.getCredentialStateAsync(userId);
if (state === AppleAuthentication.AppleAuthenticationCredentialState.AUTHORIZED) {
// User is authenticated
}
Components
AppleAuthenticationButton
Native Apple Sign In button componentProps:
buttonType: Button type (SIGN_IN, SIGN_UP, CONTINUE)
buttonStyle: Visual style (WHITE, WHITE_OUTLINE, BLACK)
cornerRadius: Corner radius in pixels
onPress: Callback when button is pressed
<AppleAuthentication.AppleAuthenticationButton
buttonType={AppleAuthentication.AppleAuthenticationButtonType.SIGN_IN}
buttonStyle={AppleAuthentication.AppleAuthenticationButtonStyle.BLACK}
cornerRadius={5}
style={{ width: 200, height: 44 }}
onPress={handleSignIn}
/>
Types
AppleAuthenticationCredential
User’s email (only on first sign-in)
fullName
AppleAuthenticationFullName | null
User’s full name (only on first sign-in)
realUserStatus
AppleAuthenticationUserDetectionStatus
Indicates if user appears to be real person
AppleAuthenticationFullName
Name prefix (e.g., “Dr.”)
Name suffix (e.g., “Jr.”)
Enums
enum AppleAuthenticationScope {
FULL_NAME,
EMAIL,
}
enum AppleAuthenticationButtonType {
SIGN_IN,
SIGN_UP,
CONTINUE,
}
enum AppleAuthenticationButtonStyle {
WHITE,
WHITE_OUTLINE,
BLACK,
}
enum AppleAuthenticationCredentialState {
AUTHORIZED,
REVOKED,
NOT_FOUND,
TRANSFERRED,
}
Examples
Basic Sign In
import * as AppleAuthentication from 'expo-apple-authentication';
async function signInWithApple() {
try {
const credential = await AppleAuthentication.signInAsync({
requestedScopes: [
AppleAuthentication.AppleAuthenticationScope.FULL_NAME,
AppleAuthentication.AppleAuthenticationScope.EMAIL,
],
});
console.log('User ID:', credential.user);
// Email and name only available on first sign-in
if (credential.email) {
console.log('Email:', credential.email);
}
if (credential.fullName) {
console.log('Name:', credential.fullName.givenName, credential.fullName.familyName);
}
// Identity token for backend verification
console.log('Identity Token:', credential.identityToken);
} catch (e: any) {
if (e.code === 'ERR_CANCELED') {
console.log('User canceled sign in');
} else {
console.error('Sign in failed:', e);
}
}
}
Check Availability
import * as AppleAuthentication from 'expo-apple-authentication';
import { useState, useEffect } from 'react';
function AppleSignInButton() {
const [isAvailable, setIsAvailable] = useState(false);
useEffect(() => {
checkAvailability();
}, []);
async function checkAvailability() {
const available = await AppleAuthentication.isAvailableAsync();
setIsAvailable(available);
}
if (!isAvailable) {
return null; // Don't show button on unsupported devices
}
return (
<AppleAuthentication.AppleAuthenticationButton
buttonType={AppleAuthentication.AppleAuthenticationButtonType.SIGN_IN}
buttonStyle={AppleAuthentication.AppleAuthenticationButtonStyle.BLACK}
cornerRadius={5}
style={{ width: 200, height: 44 }}
onPress={handleSignIn}
/>
);
}
Verify Credential State
import * as AppleAuthentication from 'expo-apple-authentication';
async function checkUserStatus(userId: string) {
const state = await AppleAuthentication.getCredentialStateAsync(userId);
switch (state) {
case AppleAuthentication.AppleAuthenticationCredentialState.AUTHORIZED:
console.log('User is authorized');
break;
case AppleAuthentication.AppleAuthenticationCredentialState.REVOKED:
console.log('User revoked authorization');
// Sign out user
break;
case AppleAuthentication.AppleAuthenticationCredentialState.NOT_FOUND:
console.log('No credential found');
break;
}
}
Complete Auth Example
import * as AppleAuthentication from 'expo-apple-authentication';
import { useState, useEffect } from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
import * as SecureStore from 'expo-secure-store';
export default function AppleAuthExample() {
const [user, setUser] = useState<any>(null);
const [isAvailable, setIsAvailable] = useState(false);
useEffect(() => {
checkAppleAuth();
loadStoredUser();
}, []);
async function checkAppleAuth() {
const available = await AppleAuthentication.isAvailableAsync();
setIsAvailable(available);
}
async function loadStoredUser() {
const userId = await SecureStore.getItemAsync('appleUserId');
if (userId) {
const state = await AppleAuthentication.getCredentialStateAsync(userId);
if (state === AppleAuthentication.AppleAuthenticationCredentialState.AUTHORIZED) {
const userData = await SecureStore.getItemAsync('userData');
if (userData) {
setUser(JSON.parse(userData));
}
}
}
}
async function handleSignIn() {
try {
const credential = await AppleAuthentication.signInAsync({
requestedScopes: [
AppleAuthentication.AppleAuthenticationScope.FULL_NAME,
AppleAuthentication.AppleAuthenticationScope.EMAIL,
],
});
// Store user data
const userData = {
userId: credential.user,
email: credential.email,
name: credential.fullName?.givenName,
};
await SecureStore.setItemAsync('appleUserId', credential.user);
await SecureStore.setItemAsync('userData', JSON.stringify(userData));
setUser(userData);
// Send to backend for verification
await verifyWithBackend(credential.identityToken);
} catch (e: any) {
if (e.code !== 'ERR_CANCELED') {
alert('Sign in failed');
}
}
}
async function handleSignOut() {
await SecureStore.deleteItemAsync('appleUserId');
await SecureStore.deleteItemAsync('userData');
setUser(null);
}
async function verifyWithBackend(identityToken: string | null) {
if (!identityToken) return;
// Send identity token to your backend for verification
await fetch('https://your-api.com/auth/apple', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ identityToken }),
});
}
if (!isAvailable) {
return (
<View style={styles.container}>
<Text>Sign in with Apple is not available on this device</Text>
</View>
);
}
if (user) {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
Welcome{user.name ? `, ${user.name}` : ''}!
</Text>
{user.email && <Text>{user.email}</Text>}
<Button title="Sign Out" onPress={handleSignOut} />
</View>
);
}
return (
<View style={styles.container}>
<AppleAuthentication.AppleAuthenticationButton
buttonType={AppleAuthentication.AppleAuthenticationButtonType.SIGN_IN}
buttonStyle={AppleAuthentication.AppleAuthenticationButtonStyle.BLACK}
cornerRadius={5}
style={styles.button}
onPress={handleSignIn}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
gap: 10,
},
button: {
width: 200,
height: 44,
},
welcome: {
fontSize: 20,
fontWeight: 'bold',
},
});
| Platform | Supported |
|---|
| iOS 13+ | ✅ |
| Android | ❌ |
| Web | ❌ |
Sign in with Apple is only available on iOS 13 and later. Always check availability before showing the button.
Configuration
app.json
{
"expo": {
"ios": {
"bundleIdentifier": "com.yourcompany.yourapp",
"usesAppleSignIn": true
}
}
}
Apple Developer Account
- Enable “Sign in with Apple” capability in your app identifier
- Create a service ID for web authentication (if needed)
- Configure domains and redirect URLs
Best Practices
- Check Availability: Always check
isAvailableAsync() before showing button
- First Sign-In: Email and name are only provided on first sign-in, store them
- Verify Server-Side: Send
identityToken to backend for verification
- Credential State: Check credential state on app launch
- Handle Revocation: Monitor for
REVOKED state and sign user out
User email and full name are only provided during the first sign-in. Store this information as it won’t be provided again.
App Store Requirements
If your app uses third-party sign-in (Google, Facebook, etc.), Apple requires you to also offer Sign in with Apple as an option.
Resources