@prpldev/payload-expo-oauth
v1.0.0
Published
OAuth authentication plugin for Payload CMS with Expo/React Native mobile support
Downloads
66
Maintainers
Readme
@prpldev/payload-expo-oauth
A Payload CMS plugin that adds OAuth/SSO authentication using Arctic.
Features
- Multiple OAuth Providers: Google, Microsoft Entra, GitHub, Apple, Discord, Facebook, LINE
- Mobile App Support: Works with Expo AuthSession and custom deep links
- Auto User Creation: Automatically create users on first OAuth login
- Account Linking: Link multiple OAuth providers to a single user
- Customizable: Hooks for user creation, login, and field mapping
- PKCE Support: Secure authentication with PKCE where supported
Installation
npm install @prpldev/payload-expo-oauth
# or
pnpm add @prpldev/payload-expo-oauth
# or
yarn add @prpldev/payload-expo-oauthQuick Start
import { buildConfig } from 'payload'
import { arcticOAuthPlugin, googleProvider, entraProvider } from '@prpldev/payload-expo-oauth'
export default buildConfig({
// ... your config
plugins: [
arcticOAuthPlugin({
providers: [
googleProvider({
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
}),
entraProvider({
clientId: process.env.ENTRA_CLIENT_ID!,
clientSecret: process.env.ENTRA_CLIENT_SECRET!,
tenantId: process.env.ENTRA_TENANT_ID!,
}),
],
}),
],
})OAuth buttons will automatically appear on the admin login page.
Configuration
Plugin Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| providers | OAuthProvider[] | Required | Array of OAuth providers |
| userCollection | string | 'users' | Collection to store users |
| autoCreateUsers | boolean | true | Auto-create users on first login |
| successRedirect | string | '/admin' | Redirect after successful login |
| failureRedirect | string | '/admin/login?error=oauth_failed' | Redirect on failure |
| disableLocalStrategy | boolean | false | Disable email/password login |
| enableMobileAuth | boolean | false | Enable mobile app authentication |
| allowedMobileRedirectUris | string[] | [] | Allowed redirect URIs for mobile |
Supported Providers
import {
googleProvider,
entraProvider,
githubProvider,
appleProvider,
discordProvider,
facebookProvider,
lineProvider,
} from '@prpldev/payload-expo-oauth'Each provider requires specific configuration. See docs/setup.md for detailed setup instructions.
Mobile App Authentication (Expo)
Full authentication support for Expo apps using expo-auth-session. Supports both OAuth providers and email/password login.
How It Works
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Expo App │ │ Payload CMS │ │ OAuth Provider │
└────────┬────────┘ └────────┬─────────┘ └────────┬────────┘
│ │ │
│ Open browser with │ │
│ ?redirect_uri=app:// │ │
│──────────────────────>│ │
│ │ │
│ Show login page (OAuth buttons + │
│ email/password form) │
│<──────────────────────│ │
│ │ │
│ User picks OAuth │ OAuth flow │
│ or enters email/pwd │──────────────────────> │
│ │<────────────────────── │
│ │ │
│ Redirect to app:// │ │
│ ?token=JWT&user_id= │ │
│<──────────────────────│ │
│ │ │
│ Parse token, done! │ │
└────────┴───────────────────────┴────────────────────────┘1. Configure the Plugin
arcticOAuthPlugin({
providers: [/* ... */],
enableMobileAuth: true,
allowedMobileRedirectUris: [
'myapp://auth/callback', // Production app
'exp://192.168.*', // Expo Go (local network)
'exp://localhost:*', // Expo Go (localhost)
],
})2. Expo App Code
import * as WebBrowser from 'expo-web-browser';
import * as AuthSession from 'expo-auth-session';
import * as Linking from 'expo-linking';
import * as SecureStore from 'expo-secure-store';
WebBrowser.maybeCompleteAuthSession();
const PAYLOAD_URL = 'https://your-payload-server.com';
const redirectUri = AuthSession.makeRedirectUri({
scheme: 'myapp',
path: 'auth/callback',
});
const login = async () => {
// Opens the Payload admin login page
// User can choose OAuth provider OR enter email/password
const loginUrl = `${PAYLOAD_URL}/admin/login?redirect_uri=${encodeURIComponent(redirectUri)}`;
const result = await WebBrowser.openAuthSessionAsync(loginUrl, redirectUri);
if (result.type === 'success' && result.url) {
const parsed = Linking.parse(result.url);
const { token, user_id, expires_in } = parsed.queryParams || {};
if (token) {
// Store token securely
await SecureStore.setItemAsync('payload_token', token as string);
console.log('Logged in! User ID:', user_id);
}
}
};
// Use token for authenticated requests
const fetchWithAuth = async (endpoint: string) => {
const token = await SecureStore.getItemAsync('payload_token');
return fetch(`${PAYLOAD_URL}${endpoint}`, {
headers: { Authorization: `JWT ${token}` },
});
};What Happens
- User opens login page - The Payload admin login page loads with OAuth buttons and email/password form
- OAuth login - Clicking an OAuth button preserves the
redirect_urithrough the OAuth flow - Email/password login - Form submission is intercepted and routed to the mobile-login endpoint
- Redirect with token - After authentication, user is redirected to
redirect_uri?token=...&user_id=...&expires_in=... - App receives token - The Expo app parses the URL and stores the JWT token
See docs/expo-auth.md for complete integration guide.
Customization
Hooks
arcticOAuthPlugin({
providers: [/* ... */],
// Called before creating a new user
beforeUserCreate: async ({ userInfo, provider }) => {
return {
email: userInfo.email,
name: userInfo.name,
role: 'member', // Add custom fields
}
},
// Called after successful login
afterLogin: async ({ user, userInfo, provider }) => {
console.log(`User ${user.email} logged in via ${provider}`)
},
// Map provider fields to user fields
mapUserFields: (userInfo, provider) => ({
email: userInfo.email,
firstName: userInfo.firstName,
lastName: userInfo.lastName,
avatar: userInfo.avatarUrl,
}),
})OAuth Accounts Field
The plugin automatically adds an oauthAccounts field to your user collection, storing linked OAuth providers:
{
oauthAccounts: [
{
provider: 'google',
providerId: '123456789',
email: '[email protected]',
connectedAt: '2024-01-15T10:30:00.000Z',
}
]
}API Endpoints
The plugin adds these endpoints to your user collection:
| Endpoint | Description |
|----------|-------------|
| GET /api/{collection}/oauth/{provider} | Start OAuth flow |
| GET /api/{collection}/oauth/{provider}/callback | OAuth callback |
| GET /api/{collection}/oauth/providers | List available providers |
| POST /api/{collection}/oauth/mobile-login | Local auth for mobile apps (when enableMobileAuth is true)
Mobile Local Auth
For email/password login in mobile apps, POST to /api/{collection}/oauth/mobile-login:
// Form data or JSON
const response = await fetch(`${PAYLOAD_URL}/api/users/oauth/mobile-login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: '[email protected]',
password: 'password',
redirect_uri: 'myapp://auth/callback',
}),
});
// Redirects to: myapp://auth/callback?token=...&user_id=...&expires_in=...Security
- PKCE: Enabled by default for providers that support it
- State Parameter: CSRF protection via state validation
- Redirect URI Validation: Mobile redirect URIs are validated against whitelist
- HttpOnly Cookies: Tokens stored securely in HttpOnly cookies (web)
License
MIT
