A React Native Expo example demonstrating secure authentication with WorkOS AuthKit using PKCE (Proof Key for Code Exchange).
Simulator.mp4
- 🔐 Secure PKCE Authentication - No client secrets in the app
- 📱 Native Deep Linking - Seamless OAuth callback handling
- 🔒 Secure Token Storage - Uses Expo SecureStore (hardware-backed)
- 🔄 Automatic Token Refresh - Transparent session management
- 🎨 React Native Paper UI - Material Design components
- 🔧 Uses Official WorkOS SDK - Full
@workos-inc/nodesupport via WebCrypto polyfill
- Node.js 18+
- Expo CLI
- WorkOS Account with AuthKit configured
- iOS Simulator or Android Emulator (development build required)
npm installThe @workos-inc/node SDK uses WebCrypto APIs (crypto.subtle) for PKCE. React Native doesn't provide these natively, so we use react-native-quick-crypto:
npm install react-native-quick-cryptoWhy is this needed? The WorkOS SDK's getAuthorizationUrlWithPKCE() uses crypto.subtle.digest() for SHA-256 hashing. This polyfill provides a native C++/JSI implementation of the full WebCrypto API.
Important: The polyfill must be installed before any other code runs:
// index.ts (app entry point)
import './src/polyfills'; // Must be FIRST import!
import { registerRootComponent } from 'expo';
import App from './App';
registerRootComponent(App);// src/polyfills.ts
import { install } from 'react-native-quick-crypto';
install();-
Copy the environment template:
cp .env.example .env
-
Add your WorkOS Client ID to
.env:EXPO_PUBLIC_WORKOS_CLIENT_ID=client_XXXXXXXXX -
Add the redirect URI to your WorkOS Dashboard:
workos-authkit-example://callback
Important: A development build is required for two reasons:
- Expo Go cannot handle custom URL schemes for OAuth callbacks
- The
react-native-quick-cryptopolyfill requires native code compilation
# iOS
npx expo run:ios
# Android
npx expo run:androidAfter creating a development build:
npx expo start --dev-clientexpo-authkit-example/
├── index.ts # Entry point (loads polyfills first!)
├── App.tsx # Main app with navigation
├── app.json # Expo config (scheme defined here)
├── src/
│ ├── polyfills.ts # WebCrypto polyfill for WorkOS SDK
│ ├── lib/
│ │ └── auth.ts # Core auth logic (uses WorkOS SDK)
│ ├── context/
│ │ └── AuthContext.tsx # Shared auth state provider
│ ├── hooks/
│ │ └── useAuth.ts # Re-exports from context
│ └── components/
│ ├── SignInButton.tsx # Sign in/out button
│ ├── Home.tsx # Home view
│ ├── Account.tsx # Account view (authenticated)
│ └── Footer.tsx # WorkOS branding
- Sign In: User taps "Sign In" → Opens browser with PKCE-protected WorkOS auth URL
- Authenticate: User authenticates with their Identity Provider
- Callback: WorkOS redirects to
workos-authkit-example://callback?code=... - Token Exchange: App exchanges code for tokens using stored PKCE verifier
- Session Storage: Tokens stored securely in Expo SecureStore
| Feature | Implementation |
|---|---|
| No client secret | PKCE flow via WorkOS SDK's getAuthorizationUrlWithPKCE() |
| Secure storage | Expo SecureStore (Keychain/Keystore) |
| Token refresh | WorkOS SDK's authenticateWithRefreshToken() |
| CSRF protection | State parameter in OAuth flow |
| Native crypto | react-native-quick-crypto (C++/JSI, not JS) |
This example mirrors the electron-authkit-example architecture:
| Electron | Expo |
|---|---|
electron-store |
expo-secure-store |
shell.openExternal() |
WebBrowser.openAuthSessionAsync() |
| Protocol handlers | expo-linking deep links |
| IPC for auth | Direct function calls |
| Native Node.js crypto | react-native-quick-crypto polyfill |
The WebCrypto polyfill isn't loading. Check:
react-native-quick-cryptois installed- The polyfill import is the FIRST line in your entry file (before any other imports)
- You've done a native rebuild after installing (
npx expo run:ios)
// ✅ Correct - polyfill FIRST
import './src/polyfills';
import { registerRootComponent } from 'expo';
// ❌ Wrong - other imports before polyfill
import { registerRootComponent } from 'expo';
import './src/polyfills'; // Too late!You need a development build. Expo Go doesn't support custom schemes or native modules:
npx expo run:ios # or run:android- Verify the redirect URI in WorkOS Dashboard matches
workos-authkit-example://callback - Ensure
schemeis set inapp.json - Check you're using a development build, not Expo Go
If signing in doesn't update all components (e.g., header button still shows "Sign In"):
- Ensure all components use
useAuth()from the same context - The app must be wrapped in
<AuthProvider>(seeApp.tsx)
The refresh token may have expired. Clear the app data and sign in again.
- WorkOS AuthKit Documentation
- react-native-quick-crypto - WebCrypto polyfill for React Native
- Expo SecureStore
- Expo WebBrowser
- Expo Linking
- OAuth 2.0 PKCE
MIT