Getting Started with Allem SDK Auth: Adapter-Based Authentication for React
Learn how to set up @allem-sdk/auth with adapter-based authentication, session management, and protected routes in your React application.

@allem-sdk/auth provides adapter-based authentication hooks for React. You implement the authentication logic and pass it to the provider — it works with any backend, any auth service, any token strategy.
Installation
npm install @allem-sdk/auth
Or with the meta-package:
npm install allem-sdk
Core Concept: Auth Adapter
An auth adapter is an object with three async methods:
interface AuthAdapter {
getSession: () => Promise<AuthSession>;
signIn: (credentials: Record<string, string>) => Promise<AuthSession>;
signOut: () => Promise<void>;
}
interface AuthSession {
user: AuthUser | null;
token: string | null;
expiresAt: number | null;
}
interface AuthUser {
id: string;
email?: string;
name?: string;
image?: string;
[key: string]: unknown;
}
You write the adapter once. The hooks handle state, loading, and re-renders.
Setup
1. Create Your Adapter
// lib/auth-adapter.ts
import type { AuthAdapter } from "@allem-sdk/auth";
export const authAdapter: AuthAdapter = {
getSession: async () => {
const res = await fetch("/api/auth/session");
if (!res.ok) return { user: null, token: null, expiresAt: null };
return res.json();
},
signIn: async (credentials) => {
const res = await fetch("/api/auth/signin", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(credentials),
});
if (!res.ok) throw new Error("Invalid credentials");
return res.json();
},
signOut: async () => {
await fetch("/api/auth/signout", { method: "POST" });
},
};
2. Wrap Your App
import { AuthProvider } from "@allem-sdk/auth";
import { authAdapter } from "@/lib/auth-adapter";
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<AuthProvider adapter={authAdapter}>
{children}
</AuthProvider>
);
}
The provider automatically calls getSession() on mount to restore the user's session.
useAuth
The primary hook for reading auth state and triggering actions:
import { useAuth } from "@allem-sdk/auth";
function Navbar() {
const { user, status, signIn, signOut } = useAuth();
if (status === "loading") return <Spinner />;
if (status === "unauthenticated") {
return (
<button onClick={() => signIn({ email: "user@example.com", password: "secret" })}>
Sign In
</button>
);
}
return (
<div>
<span>Welcome, {user?.name}</span>
<button onClick={signOut}>Sign Out</button>
</div>
);
}
useAuth Return Values
| Property | Type | Description |
|---|---|---|
user |
AuthUser | null |
Current user or null |
status |
"loading" | "authenticated" | "unauthenticated" |
Auth state |
isAuthenticated |
boolean |
Shorthand for status check |
isLoading |
boolean |
Shorthand for loading check |
signIn |
(credentials) => Promise |
Sign in with credentials |
signOut |
() => Promise |
Sign out the user |
useSession
Access the raw session object and refresh it:
import { useSession } from "@allem-sdk/auth";
function ApiClient() {
const { session, update } = useSession();
const fetchData = async () => {
const res = await fetch("/api/data", {
headers: {
Authorization: `Bearer ${session?.token}`,
},
});
return res.json();
};
// Refresh session after profile changes
const updateProfile = async (data) => {
await fetch("/api/profile", { method: "PUT", body: JSON.stringify(data) });
await update(); // Re-fetches session from adapter
};
return <Dashboard onProfileUpdate={updateProfile} />;
}
ProtectedRoute
Declaratively protect routes. Renders children only when authenticated:
import { ProtectedRoute } from "@allem-sdk/auth";
export default function DashboardPage() {
return (
<ProtectedRoute
fallback={<LoginPage />}
loadingFallback={<Spinner />}
>
<Dashboard />
</ProtectedRoute>
);
}
ProtectedRoute Props
| Prop | Type | Required | Description |
|---|---|---|---|
children |
ReactNode |
Yes | Content to show when authenticated |
fallback |
ReactNode |
Yes | Content to show when unauthenticated |
loadingFallback |
ReactNode |
No | Content to show while checking session |
Complete Login Page
"use client";
import { useState } from "react";
import { useAuth } from "@allem-sdk/auth";
import { Button, Input, Card, CardBody, CardHeader } from "@allem-ui/react";
export default function LoginPage() {
const { signIn, status } = useAuth();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
const handleSubmit = async () => {
try {
setError("");
await signIn({ email, password });
} catch (err) {
setError("Invalid email or password");
}
};
return (
<Card variant="outline" className="max-w-md mx-auto mt-20">
<CardHeader>Sign In</CardHeader>
<CardBody className="space-y-4">
<Input
label="Email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="you@example.com"
/>
<Input
label="Password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Enter your password"
/>
{error && <p className="text-red-500 text-sm">{error}</p>}
<Button
onPress={handleSubmit}
className="w-full"
isDisabled={status === "loading"}
>
{status === "loading" ? "Signing in..." : "Sign In"}
</Button>
</CardBody>
</Card>
);
}
What's Next
- Explore Allem SDK Analytics for event tracking
- Try Allem SDK Forms for form validation
- Read the full auth documentation
Enjoyed this article?
I write about building products, AI, aviation, and the journey of entrepreneurship. Follow along for more.
Keep reading

Getting Started with Allem UI Date Picker: Accessible Calendars for React
Learn how to set up @allem-ui/date-picker with Calendar, DatePicker, DateRangePicker, and TimeField components built on React Aria.

Getting Started with Allem UI Data Grid: Sortable, Filterable Tables for React
Learn how to set up @allem-ui/data-grid with TanStack Table to build sortable, filterable, and paginated data tables in React.

Getting Started with Allem UI React: Build Accessible Interfaces in Minutes
Learn how to install @allem-ui/react, set up Tailwind CSS v4, and build your first accessible interface with 30+ production-ready components.