TypeScript / React
@identsphere/react is the official React client. It includes typed hooks for
every endpoint, an Axios client with refresh + CSRF interceptors, and route
guards.
Install
npm install @identsphere/react @tanstack/react-query
@tanstack/react-query v5 is a peer dep.
Provider
Wrap your app:
// main.tsx
import { AuthProvider } from '@identsphere/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient();
createRoot(document.getElementById('root')!).render(
<QueryClientProvider client={queryClient}>
<AuthProvider
config={{
apiBaseUrl: import.meta.env.VITE_AUTH_API_URL!,
// optional overrides:
accessCookieName: 'identsphere_at',
csrfCookieName: 'identsphere_csrf',
}}
>
<App />
</AuthProvider>
</QueryClientProvider>
);
Hooks
Authentication
import { useLogin, useLogout, useRegister } from '@identsphere/react';
function LoginForm() {
const login = useLogin();
return (
<form onSubmit={async (e) => {
e.preventDefault();
const data = new FormData(e.currentTarget);
const res = await login.mutateAsync({
email: data.get('email') as string,
password: data.get('password') as string,
});
if (res.status === 'mfa_required') {
navigate(`/mfa?token=${res.mfa_token}`);
} else {
navigate('/dashboard');
}
}}>
<input name="email" type="email" />
<input name="password" type="password" />
<button type="submit" disabled={login.isPending}>Sign in</button>
{login.error && <p>{login.error.message}</p>}
</form>
);
}
Session
import { useSession } from '@identsphere/react';
function ProtectedPage() {
const { data, isLoading } = useSession();
if (isLoading) return <Spinner />;
if (!data) return <Navigate to="/login" />;
return <p>Hi, {data.user.email}</p>;
}
Permissions
import { useCapabilities } from '@identsphere/react';
function InviteButton() {
const caps = useCapabilities();
if (!caps?.permissions.includes('members.invite')) return null;
return <button>Invite</button>;
}
MFA
import {
useMfaStatus, useMfaSetup, useMfaEnable, useMfaDisable, useMfaChallenge,
} from '@identsphere/react';
function MfaPage() {
const status = useMfaStatus();
const setup = useMfaSetup();
const enable = useMfaEnable();
// step 1: get the QR code
// step 2: prompt user for first code
// step 3: call enable, show recovery codes once
}
Passkeys
import { usePasskeyEnroll, usePasskeyLogin, usePasskeys, useDeletePasskey } from '@identsphere/react';
function PasskeyPage() {
const enroll = usePasskeyEnroll();
return (
<button onClick={() => enroll.mutateAsync({ label: 'YubiKey 5C' })}>
Add passkey
</button>
);
}
The enroll hook drives the entire ceremony (begin → navigator.credentials.create → complete).
Sessions
import { useSessions, useRevokeSession, useRevokeAllSessions } from '@identsphere/react';
function SessionsPage() {
const { data } = useSessions();
const revoke = useRevokeSession();
return (
<ul>
{data?.map(s => (
<li key={s.id}>
{s.user_agent ?? 'Unknown'} — {s.is_current ? 'current' : 'other'}
{!s.is_current && (
<button onClick={() => revoke.mutateAsync(s.id)}>Revoke</button>
)}
</li>
))}
</ul>
);
}
Route guards
import { RequireAuth, RequirePermission } from '@identsphere/react';
<Routes>
<Route path="/" element={<Home />} />
<Route
path="/dashboard"
element={<RequireAuth fallback={<Navigate to="/login" />}><Dashboard /></RequireAuth>}
/>
<Route
path="/admin"
element={
<RequirePermission permission="members.remove" fallback={<Forbidden />}>
<AdminPanel />
</RequirePermission>
}
/>
</Routes>
Axios client
For your own HTTP calls to the auth backend (custom endpoints, fetching data that's behind auth), use the included Axios client:
import { useAuthClient } from '@identsphere/react';
function ProfileEditor() {
const client = useAuthClient();
const update = async (patch: ProfilePatch) => {
await client.patch('/v1/users/me', patch);
};
// ...
}
The client handles:
credentials: 'include'so cookies travel.X-CSRF-Tokeninjection from theidentsphere_csrfcookie.- Refresh on 401: catches the 401, POSTs
/v1/auth/refresh, retries the original request, surfaces the original error if refresh also fails.
Calling your own backend
For calls to your business API (not the auth backend), use your own
client; just ensure credentials: 'include'.
fetch('https://app.example.com/api/projects', {
credentials: 'include',
}).then(r => r.json());
Your backend then validates the JWT (Pattern 2) or proxies through the auth backend (Pattern 1).
Environment variables
| Variable | Purpose |
|---|---|
VITE_AUTH_API_URL | Origin of the auth backend, e.g. https://auth.example.com |
Vite, Next.js, and Create React App all accept different env-var prefixes; adapt accordingly.