@wocha/react-native
v0.1.0
Published
React Native and Expo hooks for Wocha authentication (OAuth PKCE)
Maintainers
Readme
@wocha/react-native
React Native and Expo hooks for Wocha authentication. Implements the OAuth 2.0 authorisation code flow with PKCE as a public client — no client secret required.
Install
npm install @wocha/react-native
# Recommended Expo peers for production:
npx expo install expo-web-browser expo-secure-store expo-linkingPeer dependencies:
| Package | Required | Purpose |
|---------|----------|---------|
| react | Yes | Hooks and provider |
| react-native | Yes | App lifecycle and Linking fallback |
| expo-web-browser | Optional | In-app / system browser OAuth session |
| expo-secure-store | Optional | Encrypted token storage (Keychain / Keystore) |
| expo-linking | Optional | Deep link callback handling |
When Expo packages are not installed, the SDK falls back to react-native Linking for redirects and in-memory token storage (not suitable for production).
Quick start
1. Register a redirect URI
In the Wocha Console, register a custom URL scheme for your app:
myapp://auth/callbackFor Expo, add the scheme to app.json:
{
"expo": {
"scheme": "myapp"
}
}2. Wrap your app
import { WochaProvider } from "@wocha/react-native";
export default function App() {
return (
<WochaProvider
config={{
issuer: "https://my-tenant.auth.wocha.ai",
clientId: "your-client-id",
redirectUri: "myapp://auth/callback",
}}
>
<RootNavigator />
</WochaProvider>
);
}3. Sign in from a screen
import { useWochaAuth, useSession } from "@wocha/react-native";
import { Button, Text, View } from "react-native";
export function HomeScreen() {
const { signIn, signOut } = useWochaAuth();
const { data, status } = useSession();
if (status === "loading") {
return <Text>Loading…</Text>;
}
if (status === "authenticated" && data) {
return (
<View>
<Text>Signed in as {data.email}</Text>
<Button title="Sign out" onPress={() => void signOut()} />
</View>
);
}
return <Button title="Sign in" onPress={() => void signIn()} />;
}Deep link callbacks are handled automatically by WochaProvider — no separate callback screen is required when using expo-web-browser or Linking.
Configuration
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| issuer | string | Yes | OIDC issuer URL (e.g. https://my-tenant.auth.wocha.ai) |
| clientId | string | Yes | OAuth public client ID |
| redirectUri | string | Yes | Deep link callback URL registered in the Console |
| scope | string | No | Default: openid profile email org_id offline_access |
| audience | string | No | API audience claim when required by your tenant |
| apiUrl | string | No | Customer API base for org switching. Defaults to {tenant}.api.wocha.ai/v1 on Wocha Cloud |
| storageKeyPrefix | string | No | Prefix for secure storage keys |
| onRedirectCallback | (appState?: unknown) => void | No | Called after a successful OAuth callback |
Hooks
| Hook | Description |
|------|-------------|
| useSession() | Session data, status, error, and refresh() |
| useUser() | Current user and auth status |
| useOrg() | Active org, org list, and switchOrg() |
| useWochaAuth() | Imperative signIn(), signUp(), signOut(), switchOrg(), getAccessToken() |
| useAccessToken() | Access token and refresh helpers |
| usePermission(check) | SpiceDB permission check (requires apiUrl) |
Permission check
import { usePermission } from "@wocha/react-native";
const { allowed, isLoading } = usePermission({
resource: { type: "document", id: "doc_123" },
permission: "read",
});UI components
Import from @wocha/react-native/components:
| Component | Description |
|-----------|-------------|
| SignInButton | Triggers the OAuth sign-in flow |
| SignOutButton | Signs out and clears tokens |
| UserInfo | Displays current user name and email |
| AuthGuard | Renders children only when authenticated |
| OrgSwitcher | Pressable list for switching organisations |
import {
SignInButton,
SignOutButton,
UserInfo,
AuthGuard,
OrgSwitcher,
} from "@wocha/react-native/components";
<AuthGuard fallback={<SignInButton />}>
<UserInfo />
<OrgSwitcher />
<SignOutButton />
</AuthGuard>Organisation switching
import { useOrg } from "@wocha/react-native";
const { orgId, orgIds, switchOrg, isSwitching } = useOrg();Security model
- Public client + PKCE — safe for mobile apps; no client secret in the binary.
- Secure storage — refresh tokens are stored with
expo-secure-storeorreact-native-keychainwhen available. - Background refresh — access tokens refresh automatically before expiry and when the app returns to the foreground.
- Deep link validation — OAuth
stateis verified on every callback.
Self-hosted configuration
<WochaProvider
config={{
issuer: "https://auth.internal.example.com",
clientId: process.env.EXPO_PUBLIC_WOCHA_CLIENT_ID!,
redirectUri: "myapp://auth/callback",
apiUrl: "https://api.internal.example.com/v1",
}}
>
{children}
</WochaProvider>Related packages
| Package | Use when |
|---------|----------|
| @wocha/react | React web SPA |
| @wocha/nextjs | Next.js with server-side BFF |
| @wocha/sdk | Management API from a backend |
Troubleshooting
Redirect URI mismatch
The redirectUri in config must exactly match a URI registered for your OAuth application in the Console.
Session not persisting
Install expo-secure-store (or react-native-keychain). Without secure storage the SDK uses in-memory storage and tokens are lost when the app closes.
Callback never fires
Confirm your app scheme is configured in app.json / AndroidManifest.xml / Info.plist, and that expo-linking or React Native Linking receives the callback URL.
See the React Native quickstart for a full integration walkthrough.
