Protect routes, validate sessions, and build auth flows with TAJMAC Auth in Next.js 14/15.
Use Next.js middleware to validate the session cookie on every request.
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*"],
};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>
);
}"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>
);
}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;
}"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>
);
}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;
}