ahmedallem.
Engineering · 3 min read

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.

Ahmed Allem

Ahmed Allem

Founder & CTO · Aviation, AI & Startups

ShareShare
Getting Started with Allem SDK Auth: Adapter-Based Authentication for React

@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