Implementing OAuth and Social Login
1. Using Auth Session
Example: Setting up AuthSession
import * as AuthSession from 'expo-auth-session';
import { Redirect } from 'expo-router';
const discovery = {
authorizationEndpoint: 'https://accounts.google.com/o/oauth2/v2/auth',
tokenEndpoint: 'https://oauth2.googleapis.com/token',
revocationEndpoint: 'https://oauth2.googleapis.com/revoke',
};
const config = {
clientId: 'your-client-id',
clientSecret: 'your-client-secret',
redirectUrl: AuthSession.makeRedirectUri({ useProxy: true }),
scopes: ['openid', 'profile', 'email'],
};
export default function OAuthScreen() {
const [request, response, promptAsync] = AuthSession.useAuthRequest(
config,
discovery
);
if (response) {
if (response.error) {
console.log('OAuth error:', response.error);
} else if (response.type === 'success') {
const { access_token } = response.params;
// Handle the token
}
}
return (
<Redirect href={request ? request.url : '#'} />
);
}
| Property | Description | Values |
|---|---|---|
| authorizationEndpoint | URL to redirect user for authorization | https://accounts.google.com/o/oauth2/v2/auth |
| tokenEndpoint | URL to exchange authorization code for token | https://oauth2.googleapis.com/token |
| revocationEndpoint | URL to revoke access token | https://oauth2.googleapis.com/revoke |
Note: Ensure that your redirect URL is properly configured in the OAuth provider's dashboard.
Warning: Never expose client secrets in client-side code in production. Use a backend service to handle sensitive credentials.
2. Implementing OAuth Flow
Example: Setting up Expo Auth with Google
import * as AuthSession from 'expo-auth-session';
import { Google } from 'expo-auth-session/providers/google';
const config = {
clientId: 'your-google-client-id',
redirectUri: AuthSession.makeRedirectUri({ useProxy: true }),
scopes: ['profile', 'email'],
};
const { authUrl, ...rest } = await AuthSession.getAuthSessionInfoAsync({
provider: Google,
config,
});
// Redirect user to authUrl
console.log('Redirect to:', authUrl);
| Property | Description | Values |
|---|---|---|
| clientId | OAuth client ID from provider | 'your-google-client-id' |
| redirectUri | Redirect URI for auth callback | AuthSession.makeRedirectUri({ useProxy: true }) |
| scopes | Permissions requested | ['profile', 'email'] |
Note: Use
useProxy: true for local development to handle redirects correctly.
Warning: Never hardcode client secrets in production apps.
OAuth Flow Steps
- User initiates login via social provider
- Redirects to OAuth authorization URL
- User grants permissions
- Provider redirects back to app with auth code
- Exchange code for access token
3. Configuring Auth Request
| Property | Description | Example |
|---|---|---|
| authUrl | URL to initiate OAuth flow | https://accounts.google.com/o/oauth2/v2/auth |
| clientId | Client ID from OAuth provider | 1234567890-abcde.apps.googleusercontent.com |
| redirectUrl | Redirect URI registered with provider | expohost://auth/callback |
| scopes | Permissions requested from user | ["email", "profile"] |
Example: Configure Auth Request
import * as WebBrowser from 'expo-web-browser';
const config = {
authUrl: 'https://accounts.google.com/o/oauth2/v2/auth',
clientId: '1234567890-abcde.apps.googleusercontent.com',
redirectUrl: 'expohost://auth/callback',
scopes: ['email', 'profile']
};
WebBrowser.openAuthSessionAsync(config.authUrl, config.redirectUrl);
4. Using Auto Discovery
| Property | Description | Value |
|---|---|---|
| autoDiscovery | Enables automatic discovery of OAuth endpoints | true |
| issuer | OAuth issuer URL for auto-discovery | https://accounts.google.com |
| authorizationEndpoint | OAuth authorization endpoint URL | https://accounts.google.com/o/oauth2/v2/auth |
| tokenEndpoint | OAuth token endpoint URL | https://oauth2.googleapis.com/token |
| userEndpoint | OAuth user info endpoint URL | https://www.googleapis.com/oauth2/v2/userinfo |
Example: Auto Discovery Configuration
import { AuthSession } from 'expo-auth-session';
const config = {
autoDiscovery: true,
issuer: 'https://accounts.google.com',
clientId: 'your-client-id',
clientSecret: 'your-client-secret',
redirectUrl: AuthSession.getRedirectUrl(),
scopes: ['profile', 'email']
};
const discovery = await AuthSession.fetchDiscoveryAsync(config.issuer);
console.log('Discovered endpoints:', discovery);
5. Using Apple Sign In
| Property | Description | Values |
|---|---|---|
| provider | Identifier for the Apple provider | apple |
| clientId | App ID from Apple Developer account | com.example.app |
| clientSecret | Private key from Apple Developer account | secret-key-here |
Example: Configure Apple Sign In
import { AppleAuth } from '@invertase/react-native-apple-auth';
const appleAuth = new AppleAuth({
clientId: 'com.example.app',
clientSecret: 'secret-key-here',
redirectUri: 'https://example.com/auth/apple',
scope: ['name', 'email'],
responseType: 'code',
});
export default appleAuth;
Note: Ensure that your app is registered with Apple Developer and the
Sign in with Apple capability is enabled.
Warning: The
clientSecret must be securely stored and not exposed in client-side code in production.
Apple Sign In Flow Summary
- User taps "Sign in with Apple"
- Expo handles the OAuth flow
- Apple returns an authorization code
- Server exchanges code for token
- User profile is retrieved and session created
6. Implementing Google Sign In
Example: Setting up Google Sign-In with Expo
import { Google } from 'expo-auth-session/providers/google';
import * as WebBrowser from 'expo-web-browser';
const config = {
clientId: 'YOUR_GOOGLE_CLIENT_ID',
scopes: ['profile', 'email'],
redirectUrl: WebBrowser.makeRedirectUri({
scheme: 'your-app-scheme'
}),
discoveryUrl: 'https://accounts.google.com/.well-known/openid-configuration'
};
const handleGoogleLogin = async () => {
const result = await Google.logInAsync(config);
if (result.type === 'success') {
console.log('Google login successful', result);
} else {
console.log('Google login failed', result);
}
};
| Property | Description | Value |
|---|---|---|
| clientId | Google OAuth client ID | string |
| scopes | Permissions requested from the user | string[] |
| redirectUrl | Redirect URI for OAuth callback | string |
| discoveryUrl | OpenID Connect discovery URL | string |
Note: Make sure to configure your Google OAuth client in the Google Cloud Console with the correct redirect URIs.
Warning: The
redirectUrl must match exactly what is configured in the Google Cloud Console.
Google Sign-In Flow Summary
- User taps "Sign in with Google"
- Expo opens a web browser to Google's OAuth endpoint
- User authenticates and grants permissions
- Google redirects back to your app with an auth code
- Your app exchanges the code for tokens
7. Using Facebook Login
Example: Setting up Facebook Login
import * as Facebook from 'expo-facebook';
export default function FacebookLoginButton() {
const handleLogin = async () => {
await Facebook.initializeAsync({
appId: 'YOUR_FACEBOOK_APP_ID',
});
const { type, token } = await Facebook.logInAsync({
permissions: ['public_profile', 'email'],
});
if (type === 'success') {
// Send token to your backend for validation
console.log('Login successful with token:', token);
} else {
console.log('Login failed');
}
};
return (
<TouchableOpacity onPress={handleLogin}>
<Text>Login with Facebook</Text>
</TouchableOpacity>
);
}
| Property | Description | Values |
|---|---|---|
| appId | Facebook App ID from Facebook Developer Console | string |
| permissions | Permissions requested from the user | string[] |
Note: Ensure your Facebook App ID is correctly configured in
app.json or app.config.js.
Warning: Facebook login requires proper setup in the Facebook Developer Console, including valid redirect URIs.
Facebook Login Setup Steps
- Create a Facebook App in the Facebook Developer Console
- Configure the app with valid redirect URIs (e.g.,
exp://127.0.0.1:8081) - Install
expo-facebookpackage - Initialize with your Facebook App ID
- Request appropriate permissions from the user
8. Opening Auth Browser
Example: Opening Auth Browser
import * as WebBrowser from 'expo-web-browser';
const handleAuth = async () => {
const result = await WebBrowser.openAuthSessionAsync(
'https://example.com/oauth/authorize',
'https://example.com/callback'
);
console.log(result);
};
| Property | Description | Notes |
|---|---|---|
| authUrl | URL to initiate OAuth flow | Must be a valid HTTPS URL |
| redirectUrl | URL to redirect to after auth | Must match registered redirect URL |
| options | Optional configuration object | See below for supported keys |
Note: The redirect URL must be registered with the OAuth provider.
Warning: Ensure all URLs are properly encoded to prevent security issues.
const options = {
presentationStyle: 'pageSheet',
preferredBarTintColor: '#000000',
preferredControlTintColor: '#ffffff'
};
9. Storing Auth Tokens
Example: Secure Storage with Expo SecureStore
import * as SecureStore from 'expo-secure-store';
// Store auth token
await SecureStore.setItemAsync('authToken', token);
// Retrieve auth token
const token = await SecureStore.getItemAsync('authToken');
// Delete auth token
await SecureStore.deleteItemAsync('authToken');
| Method | Description | Notes |
|---|---|---|
| setItemAsync(key, value) | Stores a key-value pair securely | Values are encrypted and stored in secure storage |
| getItemAsync(key) | Retrieves a value by key | Returns null if key does not exist |
| deleteItemAsync(key) | Deletes a key-value pair | Removes the stored value entirely |
Note: SecureStore is not available on all platforms. On web, it falls back to using
localStorage, which is less secure.
Warning: Never store sensitive tokens in
AsyncStorage or localStorage as they are not encrypted.
Best Practices for Token Storage
- Use
Expo SecureStorefor sensitive data like auth tokens - Clear tokens from storage when the user logs out
- Implement token refresh logic to handle expiration
- Use secure storage for tokens even in development mode
10. Handling Token Refresh
| Property | Description | Example |
|---|---|---|
| accessToken | The current access token used for API requests | "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." |
| refreshToken | Token used to obtain a new access token | "rJ3kLmNpQrStUvWxYzAbCdEfGhIjKlMnOpQrStUvWxYz" |
| expiresAt | Timestamp when the current access token expires | 1698765432000 |
Example: Refreshing Token with Expo Auth
import { AuthSession } from 'expo-auth-session';
const refreshAccessToken = async (refreshToken) => {
const response = await fetch('https://api.example.com/auth/refresh', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ refreshToken }),
});
const { accessToken, expiresAt } = await response.json();
return { accessToken, expiresAt };
};
// Usage
const newTokenData = await refreshAccessToken(refreshToken);
Note: Implement automatic token refresh before expiration to avoid auth errors.
11. Managing Session State
| Property | Description | Values |
|---|---|---|
| accessToken | Token used to access protected resources | string |
| refreshToken | Token used to obtain a new access token | string |
| expiresAt | Timestamp when the access token expires | number |
| user | User profile information | object |
Example: Storing Session State
import * as SecureStore from 'expo-secure-store';
const saveSession = async (session) => {
await SecureStore.setItemAsync('accessToken', session.accessToken);
await SecureStore.setItemAsync('refreshToken', session.refreshToken);
await SecureStore.setItemAsync('expiresAt', session.expiresAt.toString());
};
const loadSession = async () => {
const accessToken = await SecureStore.getItemAsync('accessToken');
const refreshToken = await SecureStore.getItemAsync('refreshToken');
const expiresAt = await SecureStore.getItemAsync('expiresAt');
return {
accessToken,
refreshToken,
expiresAt: parseInt(expiresAt),
};
};
Example: Refreshing Session
const refreshSession = async (refreshToken) => {
const response = await fetch('https://api.example.com/auth/refresh', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ refreshToken }),
});
const newSession = await response.json();
await saveSession(newSession);
return newSession;
};
Note: Use
expo-secure-store to securely store session tokens on device.
12. Implementing Logout Flow
Example: Logout with Expo Auth
import { AuthSession } from 'expo-auth-session';
import * as WebBrowser from 'expo-web-browser';
const handleLogout = async () => {
try {
// Clear auth state
await AuthSession.clearSessionAsync();
// Optionally clear local storage or tokens
// AsyncStorage.removeItem('authToken');
// Redirect to login screen
// navigation.navigate('Login');
} catch (error) {
console.error('Logout error:', error);
}
};
| Method | Description | Notes |
|---|---|---|
| AuthSession.clearSessionAsync() | Clears the session data for the current provider | Removes cached tokens and session info |
| AsyncStorage.removeItem() | Removes stored auth tokens | Optional but recommended for security |
Note: Ensure all session data is cleared to prevent token reuse.