TAJMAC Auth

Next.js App Router Integration

Protect routes, validate sessions, and build auth flows with TAJMAC Auth in Next.js 14/15.

1. Middleware — Protect routes

Use Next.js middleware to validate the session cookie on every request.

middleware.ts
import { NextRequest, NextResponse } from "next/server";

const TAJMAC_AUTH_URL = process.env.TAJMAC_AUTH_URL!;

export async function middleware(req: NextRequest) {
  const session = req.cookies.get("tajmac.session")?.value;
  if (!session) {
    return NextResponse.redirect(new URL("/login", req.url));
  }

  // Validate session server-side (optional but recommended)
  const res = await fetch(`${TAJMAC_AUTH_URL}/api/v1/auth/session`, {
    headers: { Cookie: `tajmac.session=${session}` },
  }).catch(() => null);

  if (!res?.ok) {
    return NextResponse.redirect(new URL("/login", req.url));
  }

  return NextResponse.next();
}

export const config = {
  matcher: ["/dashboard/:path*", "/settings/:path*"],
};

2. Get session in Server Components

app/dashboard/page.tsx
import { cookies } from "next/headers";

async function getSession() {
  const cookieStore = await cookies();
  const session = cookieStore.get("tajmac.session");
  if (!session) return null;

  const res = await fetch(
    `${process.env.TAJMAC_AUTH_URL}/api/v1/auth/session`,
    { headers: { Cookie: `tajmac.session=${session.value}` } },
  );
  if (!res.ok) return null;
  return res.json() as Promise<{ user: { id: string; email: string; name: string } }>;
}

export default async function DashboardPage() {
  const session = await getSession();
  if (!session) redirect("/login");

  return (
    <div>
      <h1>Welcome, {session.user.name}</h1>
    </div>
  );
}

3. Sign-in form (Client Component)

app/login/login-form.tsx
"use client";

import { useState } from "react";

export function LoginForm() {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [error, setError] = useState<string | null>(null);

  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault();
    const res = await fetch("/api/auth/sign-in", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ email, password }),
    });
    if (!res.ok) {
      const json = await res.json();
      setError(json.error ?? "Sign in failed");
      return;
    }
    window.location.href = "/dashboard";
  }

  return (
    <form onSubmit={handleSubmit}>
      {error && <p style={{ color: "red" }}>{error}</p>}
      <input type="email" value={email} onChange={e => setEmail(e.target.value)} required />
      <input type="password" value={password} onChange={e => setPassword(e.target.value)} required />
      <button type="submit">Sign in</button>
    </form>
  );
}

4. Sign-in API route (forwards Set-Cookie)

app/api/auth/sign-in/route.ts
import { NextRequest, NextResponse } from "next/server";

const TAJMAC_AUTH_URL = process.env.TAJMAC_AUTH_URL!;
const APP_SLUG = process.env.TAJMAC_APP_SLUG!; // e.g. "my-app"

export async function POST(req: NextRequest) {
  const body = await req.json();

  const upstream = await fetch(`${TAJMAC_AUTH_URL}/api/v1/auth/sign-in/email`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ ...body, appSlug: APP_SLUG }),
  });

  const json = await upstream.json();
  const res = NextResponse.json(json, { status: upstream.status });

  // Forward Set-Cookie so session cookie lands on your domain
  upstream.headers.forEach((value, key) => {
    if (key.toLowerCase() === "set-cookie") {
      res.headers.append("Set-Cookie", value);
    }
  });

  return res;
}

5. Sign-out button

components/sign-out-button.tsx
"use client";

export function SignOutButton() {
  async function handleSignOut() {
    await fetch("/api/auth/sign-out", { method: "POST" });
    window.location.href = "/login";
  }

  return (
    <button onClick={handleSignOut}>Sign out</button>
  );
}
app/api/auth/sign-out/route.ts
import { NextRequest, NextResponse } from "next/server";

const TAJMAC_AUTH_URL = process.env.TAJMAC_AUTH_URL!;

export async function POST(req: NextRequest) {
  const session = req.cookies.get("tajmac.session");

  if (session) {
    await fetch(`${TAJMAC_AUTH_URL}/api/v1/auth/sign-out`, {
      method: "POST",
      headers: { Cookie: `tajmac.session=${session.value}` },
    }).catch(() => undefined);
  }

  const res = NextResponse.redirect(new URL("/login", req.url));
  res.cookies.set("tajmac.session", "", { expires: new Date(0), path: "/" });
  return res;
}
Next: OIDC Provider →Back to Docs