WorkOS AuthKit integration for React Native, built on Zustand and Expo modules.
- π Secure token storage (Keychain/Keystore)
- π Automatic session restoration and token refresh
- π PKCE for added security
- π§© Zustand-based β no nested providers or layouts required
npx expo install authkit-react-native zustand expo-auth-session expo-secure-store expo-web-browser @react-native-async-storage/async-storageCreate a useAuth hook that your app imports everywhere. The store is created at module scope so the session begins restoring immediately on app launch.
// hooks/useAuth.ts
import { createAuthStore } from "authkit-react-native";
import { useStore } from "zustand";
const authStore = createAuthStore({
// Provide authorizationEndpoint or clientId
authorizationEndpoint: "https://api.example.com/v1/auth/authorize",
tokenEndpoint: "https://api.example.com/v1/auth/token",
revocationEndpoint: "https://api.example.com/v1/auth/revoke",
});
export function useAuth() {
return useStore(authStore);
}Use Stack.Protected to gate routes based on auth state. Because the store lives outside React, there's no provider to wrap your app in β just call useAuth() in your root layout.
Requires Expo SDK 53+ (Expo Router v5).
// app/_layout.tsx
import { useAuth } from "@/hooks/useAuth";
import { Loading } from "@/components/Loading";
import { Stack } from "expo-router";
export default function RootLayout() {
const { user, isLoading } = useAuth();
if (isLoading) return <Loading />;
return (
<Stack>
<Stack.Protected guard={!user}>
<Stack.Screen name="sign-in" />
</Stack.Protected>
<Stack.Protected guard={!!user}>
<Stack.Screen name="(app)" />
</Stack.Protected>
</Stack>
);
}Call signIn() to open the WorkOS AuthKit sign-in page. Returns true on success, false if the user cancelled.
import { useAuth } from "@/hooks/useAuth";
function SignInScreen() {
const { signIn } = useAuth();
return (
<>
<Button title="Sign in" onPress={() => signIn()} />
<Button
title="Sign up"
onPress={() => signIn({ screenHint: "sign-up" })}
/>
</>
);
}signOut() revokes the token and clears storage. It does not show any confirmation UI β add that in your app:
import { useAuth } from "@/hooks/useAuth";
import { Alert } from "react-native";
function SignOutButton() {
const { user, signOut } = useAuth();
const handleSignOut = () => {
Alert.alert(
`Are you sure you want to sign out as ${user.email}?`,
undefined,
[
{ text: "Cancel", style: "cancel" },
{ text: "Sign out", style: "destructive", onPress: () => signOut() },
],
);
};
return <Button title="Sign out" onPress={handleSignOut} />;
}| Option | Required | Default | Description |
|---|---|---|---|
authorizationEndpoint |
* | WorkOS default | OAuth authorize URL. Required unless clientId is provided. |
clientId |
* | β | WorkOS client ID. Required when using the default authorization endpoint. |
tokenEndpoint |
Yes | β | Token exchange URL. |
revocationEndpoint |
Yes | β | Token revocation URL. |
redirectUri |
No | makeRedirectUri() |
OAuth redirect URI. |
storageKeyPrefix |
No | "workos" |
Prefix for SecureStore/AsyncStorage keys. |
devMode |
No | false |
Logs errors to the console when enabled. |
See the examples/ directory for complete, copy-paste-ready projects: