Skip to content

An example of using AuthKit in an Expo app.

Notifications You must be signed in to change notification settings

workos/expo-authkit-example

Repository files navigation

Expo AuthKit Example

A React Native Expo example demonstrating secure authentication with WorkOS AuthKit using PKCE (Proof Key for Code Exchange).

Simulator.mp4

Features

  • 🔐 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/node support via WebCrypto polyfill

Prerequisites

Setup

1. Install Dependencies

npm install

2. WebCrypto Polyfill (Critical)

The @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-crypto

Why 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();

3. Configure WorkOS

  1. Copy the environment template:

    cp .env.example .env
  2. Add your WorkOS Client ID to .env:

    EXPO_PUBLIC_WORKOS_CLIENT_ID=client_XXXXXXXXX
    
  3. Add the redirect URI to your WorkOS Dashboard:

    workos-authkit-example://callback
    

4. Create a Development Build

Important: A development build is required for two reasons:

  1. Expo Go cannot handle custom URL schemes for OAuth callbacks
  2. The react-native-quick-crypto polyfill requires native code compilation
# iOS
npx expo run:ios

# Android
npx expo run:android

Running the App

After creating a development build:

npx expo start --dev-client

Project Structure

expo-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

How It Works

Authentication Flow

  1. Sign In: User taps "Sign In" → Opens browser with PKCE-protected WorkOS auth URL
  2. Authenticate: User authenticates with their Identity Provider
  3. Callback: WorkOS redirects to workos-authkit-example://callback?code=...
  4. Token Exchange: App exchanges code for tokens using stored PKCE verifier
  5. Session Storage: Tokens stored securely in Expo SecureStore

Key Security Features

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)

Comparison with Electron Example

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

Troubleshooting

"ReferenceError: Property 'crypto' doesn't exist" or "Cannot read property 'digest' of undefined"

The WebCrypto polyfill isn't loading. Check:

  1. react-native-quick-crypto is installed
  2. The polyfill import is the FIRST line in your entry file (before any other imports)
  3. 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!

"Invariant Violation: Linking requires a build-time setting scheme"

You need a development build. Expo Go doesn't support custom schemes or native modules:

npx expo run:ios  # or run:android

OAuth callback not working

  1. Verify the redirect URI in WorkOS Dashboard matches workos-authkit-example://callback
  2. Ensure scheme is set in app.json
  3. Check you're using a development build, not Expo Go

Auth state not updating across components

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> (see App.tsx)

Token refresh failing

The refresh token may have expired. Clear the app data and sign in again.

Resources

License

MIT

About

An example of using AuthKit in an Expo app.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published