Na jó, vágjunk bele. Az Auth.js v5 (a régi NextAuth.js, csak új névvel) 2025 végén stabilizálódott, és — őszintén szólva — eléggé átszabta az API-t a Next.js App Router köré. A régi [...nextauth]/route.ts fájl helyett ma már egy központi auth.ts-ből exportált függvényekkel dolgozunk, és ami szerintem a legszebb az egészben: a session olvasása RSC-ben, Server Actionben, middleware-ben és Route Handlerben pontosan ugyanaz a hívás. Ebben az útmutatóban végigmegyünk a teljes 2026-os beállításon — OAuth, Credentials, adatbázis-adapter, middleware-védelem, és persze a klasszikus csapdák — működő kódpéldákkal.
Mi újdonság van az Auth.js v5-ben?
A v4-ről v5-re váltás nem kozmetikai, hanem strukturális. Ha tehát régebbi tutorialokat olvasol (és bevallom, én is beleestem ebbe a hibába a múlt nyáron), elavult mintákat fogsz látni. A legfontosabb változások 2026-ra:
- Univerzális
auth()függvény: ugyanaz a hívás működik Server Componentben, Server Actionben, Route Handlerben és middleware-ben. Nincs többé különgetServerSession,getSessionésuseSessionkülönbség a szerveroldalon — egyszerűen csakauth(). - Központi konfigurációs fájl: a hitelesítés gyökere egy
auth.tsa projekt gyökerében, ami visszaadauth,handlers,signInéssignOutexporokat. - Edge-kompatibilis hasítás: a middleware-be külön
auth.config.tskerül, ami nem importál Node-only adaptert vagy bcryptet, így gond nélkül futtatható az Edge Runtime-on. - Új csomagnév:
next-auth@5(igen, még mindig ezen a néven publikálva), de hivatalosan az „Auth.js" márka alatt. - Beépített
signInéssignOutServer Actionökhöz: nem kell többé anext-auth/reactkliens hookot ráerőltetni egy egyszerű űrlapra. Hála.
Telepítés és környezeti változók
A v5 még béta-jelölővel publikál (igen, 2026-ban is), így a kanonikus telepítés a @beta tag mentén történik — a stabil release-line ezt használja, lásd a authjs.dev migrációs jegyzeteket:
npm install next-auth@beta
npm install @auth/drizzle-adapter # ha adatbázist használsz
npm install bcryptjs # ha Credentials providert használsz
npm install -D @types/bcryptjs
Ezután generálj egy biztonságos AUTH_SECRET-et — a v5 ezt használja a JWT és a CSRF cookie aláírására:
npx auth secret
# vagy: openssl rand -base64 32
A .env.local minimális változatban valahogy így néz ki:
AUTH_SECRET="a-generalt-titkos-kulcs"
AUTH_TRUST_HOST=true # nem-Vercel produkciós környezetben (Docker, custom)
# OAuth példák
AUTH_GOOGLE_ID="..."
AUTH_GOOGLE_SECRET="..."
AUTH_GITHUB_ID="..."
AUTH_GITHUB_SECRET="..."
# Adatbázis
DATABASE_URL="postgres://..."
Fontos: Vercelen az AUTH_TRUST_HOST automatikusan beáll. Saját Docker-konténerben, vagy Cloudflare/Fly.io mögött viszont manuálisan kell true-ra tenni, különben a v5 elutasítja a host header-t és csodás 500-as hibát kapsz a callback URL-en. (Tudom, mert legutóbb fél órát vadásztam erre.)
Az auth.config.ts és auth.ts különválasztása
Itt jön a lényeg. Az App Router middleware-je az Edge Runtime-on fut, ahol nem érhető el a Node.js crypto modul, és — ami talán fájdalmasabb — a Drizzle/Prisma adapterek sem futnak. Ezért az Auth.js v5 hivatalos mintája egy kettős fájl: egy edge-safe konfigurációs fájl, és egy node-only főfájl, ami kibővíti azt.
auth.config.ts (edge-kompatibilis)
// auth.config.ts
import type { NextAuthConfig } from "next-auth";
import GitHub from "next-auth/providers/github";
import Google from "next-auth/providers/google";
export default {
providers: [
GitHub, // automatikusan olvassa az AUTH_GITHUB_ID/SECRET értékeket
Google,
],
pages: {
signIn: "/login",
},
callbacks: {
authorized({ auth, request: { nextUrl } }) {
const isLoggedIn = !!auth?.user;
const isOnDashboard = nextUrl.pathname.startsWith("/dashboard");
if (isOnDashboard) return isLoggedIn;
return true;
},
},
} satisfies NextAuthConfig;
auth.ts (node runtime, adapterrel)
// auth.ts
import NextAuth from "next-auth";
import { DrizzleAdapter } from "@auth/drizzle-adapter";
import Credentials from "next-auth/providers/credentials";
import bcrypt from "bcryptjs";
import { z } from "zod";
import { db } from "@/db";
import { users } from "@/db/schema";
import { eq } from "drizzle-orm";
import authConfig from "./auth.config";
export const { handlers, auth, signIn, signOut } = NextAuth({
...authConfig,
adapter: DrizzleAdapter(db),
session: { strategy: "jwt" },
providers: [
...authConfig.providers,
Credentials({
async authorize(credentials) {
const parsed = z
.object({ email: z.string().email(), password: z.string().min(8) })
.safeParse(credentials);
if (!parsed.success) return null;
const { email, password } = parsed.data;
const user = await db.query.users.findFirst({
where: eq(users.email, email),
});
if (!user?.passwordHash) return null;
const ok = await bcrypt.compare(password, user.passwordHash);
if (!ok) return null;
return { id: user.id, email: user.email, name: user.name };
},
}),
],
});
A trükk: a middleware csak az auth.config.ts-t importálja, így az Edge bundle nem kapja meg a Drizzle vagy bcrypt függőséget. A teljes auth.ts pedig szépen a Node runtime-ra korlátozódik (Server Components, Actions, API útvonalak). Egyszerű, de zseniális.
Route Handler és middleware bekötése
Az App Router catch-all route handler az egyetlen, ami szinte változatlan maradt v4-ből:
// app/api/auth/[...nextauth]/route.ts
export { GET, POST } from "@/auth";
A middleware viszont teljesen új mintát követ:
// middleware.ts
import NextAuth from "next-auth";
import authConfig from "./auth.config";
export const { auth: middleware } = NextAuth(authConfig);
export const config = {
matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
};
Az authorized callback (lásd auth.config.ts) eldönti, hogy egy adott útvonalon milyen szabály érvényesül. Ez sokkal tisztább, mint a v4-es withAuth HOC volt, mert a logika ugyanabban a fájlban él, mint a providerek — ami szerintem hatalmas előrelépés DX szempontból.
Credentials provider biztonságos jelszókezeléssel
A Credentials provider egy gyakran félreértett pont. Hash-elj rendesen: a regisztrációkor (a perzisztált hash), és soha, de tényleg soha ne küldd el a nyers jelszót semmilyen logba. A bcrypt 12-es saltrounddal 2026-ban még elfogadható, de ha lehet, válts argon2id-re produkcióban. (A bcrypt egyébként szépen öregszik, csak épp nem elég gyorsan ahhoz, hogy a modern GPU-kkal lépést tartson.)
Regisztráció Server Actionnel
// app/(auth)/register/actions.ts
"use server";
import { z } from "zod";
import bcrypt from "bcryptjs";
import { db } from "@/db";
import { users } from "@/db/schema";
import { redirect } from "next/navigation";
const RegisterSchema = z.object({
email: z.string().email(),
password: z.string().min(8).max(128),
name: z.string().min(1).max(100),
});
export async function register(_: unknown, formData: FormData) {
const parsed = RegisterSchema.safeParse(Object.fromEntries(formData));
if (!parsed.success) {
return { error: "Érvénytelen adatok" };
}
const passwordHash = await bcrypt.hash(parsed.data.password, 12);
try {
await db.insert(users).values({
email: parsed.data.email,
name: parsed.data.name,
passwordHash,
});
} catch {
return { error: "Ez az e-mail már foglalt" };
}
redirect("/login");
}
Bejelentkezés signIn-nel Server Actionben
// app/(auth)/login/actions.ts
"use server";
import { signIn } from "@/auth";
import { AuthError } from "next-auth";
export async function login(_: unknown, formData: FormData) {
try {
await signIn("credentials", {
email: formData.get("email"),
password: formData.get("password"),
redirectTo: "/dashboard",
});
} catch (error) {
if (error instanceof AuthError) {
return error.type === "CredentialsSignin"
? { error: "Hibás e-mail vagy jelszó" }
: { error: "Hitelesítési hiba történt" };
}
throw error; // a Next.js redirect dobott AuthError nem ide tartozik
}
}
Csapda — és erről egész könyvet lehetne írni: a signIn() sikeres hívás esetén egy speciális NEXT_REDIRECT hibát dob, ami valójában nem hiba, hanem a redirect mechanizmusa. Ezt nem szabad elkapni a try/catch-ben. Ezért fontos a fenti throw error ág: csak a valódi AuthError példányokat kezeljük, a többit hagyjuk továbbpattanni.
Session-stratégia: JWT vs. adatbázis
Az Auth.js v5 két stratégiát kínál, és — őszintén — ez egy kérdés, amit a legtöbben rosszul döntenek el:
| Szempont | JWT | Database |
|---|---|---|
| Edge middleware-ben olvasható | Igen | Nem (DB hívás kell) |
| Session érvénytelenítés | Csak lejáratig | Azonnali (DELETE row) |
| OAuth-only támogatás | Igen | Igen |
| Credentials támogatás | Csak ez működik | Nem hivatalos |
| Méret a cookie-ban | ~1-4 KB | Csak session ID |
A 2026-os ajánlás röviden: ha bármilyen Credentials providert használsz, válaszd a strategy: "jwt"-et, és a friss adatokat (jogosultságok, profilkép) a jwt és session callbackekben írd át. Ha tisztán OAuth-t használsz és azonnali kijelentkezést akarsz támogatni minden eszközön, válaszd a database stratégiát. Ennyi. A legtöbb projektnél a JWT a megfelelő választás.
JWT callbackek bővítése
// auth.ts (kiegészítés)
callbacks: {
async jwt({ token, user, trigger, session }) {
if (user) {
token.id = user.id;
token.role = (user as { role?: string }).role ?? "user";
}
if (trigger === "update" && session?.name) {
token.name = session.name; // useSession().update() trigger
}
return token;
},
async session({ session, token }) {
session.user.id = token.id as string;
session.user.role = token.role as string;
return session;
},
},
A TypeScript típusok bővítéséhez egy modul-augmentációs fájl kell — enélkül jönnek a vörös hullámvonalak az IDE-ben:
// types/next-auth.d.ts
import "next-auth";
import "next-auth/jwt";
declare module "next-auth" {
interface Session {
user: {
id: string;
role: string;
name?: string | null;
email?: string | null;
image?: string | null;
};
}
}
declare module "next-auth/jwt" {
interface JWT {
id: string;
role: string;
}
}
Session olvasása Server Componentben és Route Handlerben
És itt jön a v5 legelegánsabb része: mindenhol ugyanaz a hívás. Tényleg mindenhol.
// Server Component
import { auth } from "@/auth";
export default async function DashboardPage() {
const session = await auth();
if (!session) return null; // a middleware már átirányított ide jutva
return <p>Üdv, {session.user.name}!</p>;
}
// Route Handler
import { auth } from "@/auth";
import { NextResponse } from "next/server";
export async function GET() {
const session = await auth();
if (!session) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
return NextResponse.json({ userId: session.user.id });
}
// Server Action
"use server";
import { auth } from "@/auth";
export async function deletePost(id: string) {
const session = await auth();
if (!session?.user) throw new Error("Unauthorized");
// ... törlés logika
}
Kliensoldalon (csak ha tényleg szükséges, pl. egy dropdown menü user-statusához) a useSession hook él tovább, de előtte be kell ágyaznod egy SessionProvider-t a layoutba.
OAuth callback és profilmezők testreszabása
A GitHub és Google providerek alapból az e-mailt és a nevet hozzák, de a gyakorlatban majdnem mindig kell egy saját role mező vagy onboarding-flag. Itt jön be a profile callback:
GitHub({
profile(profile) {
return {
id: profile.id.toString(),
name: profile.name ?? profile.login,
email: profile.email,
image: profile.avatar_url,
role: profile.email === process.env.ADMIN_EMAIL ? "admin" : "user",
};
},
}),
Egy fontos részlet, amibe sokan beleesnek: ezt csak az első bejelentkezéskor használja az adapter — a következő hívásoknál már a DB-ből olvas. Tehát a role későbbi módosítása nem ide, hanem a DB-be történik, és a jwt callback fogja onnan frissíteni.
Védelem rétegekben: middleware + page guard + action guard
A 2026-os legjobb gyakorlat három rétegű védelem, és ez nem túlzás. A middleware ugyanis megkerülhető (lásd CVE-2025-29927), a kliens pedig — hát, a kliens sosem volt megbízható:
- Middleware: gyors átirányítás bejelentkezés előtt. UX-réteg, nem biztonsági.
- Layout/Page guard: a védett route layoutjában
auth()hívás, és ha nincs session →redirect("/login"). - Action/Handler guard: minden mutáló műveletben újraellenőrzés, a felhasználó jogosultságainak vizsgálatával. Ez az igazi biztonsági réteg.
// app/dashboard/layout.tsx
import { auth } from "@/auth";
import { redirect } from "next/navigation";
export default async function DashboardLayout({ children }: { children: React.ReactNode }) {
const session = await auth();
if (!session) redirect("/login");
return <>{children}</>;
}
Gyakori csapdák 2026-ban
Na, most jön a rész, ami miatt valójában megnyitottad ezt a cikket:
- „Edge runtime hibát dob bcrypt-tal": nem szabad a bcryptet az
auth.config.ts-ben importálni. Tartsd csak azauth.ts-ben — ez a két-fájlos minta egész értelme. - „signIn redirect mégis hibát dob": sose vond össze a
signIn()-t catch-allcatch-csel. Engedd át aNEXT_REDIRECT-et, különben magadba lőttél. - „Session frissítése nem érvényesül": JWT stratégia esetén a token addig él, amíg le nem jár. Vagy a kliensoldali
session.update()kell, vagy egy alacsonyabbmaxAge. - „OAuth callback URL mismatch": a v5 alapértelmezett callback URL
/api/auth/callback/[provider]— és ez sok dokumentációban (még a régi NextAuth-tutorialokban is) nem stimmel. A pontos URL-t aauthhandler logolja indításkor, érdemes onnan másolni. - „AUTH_SECRET undefined produkcióban": Vercelen explicit állítsd be Project Settings → Environment Variables alatt. A build folyamat NEM örökli a
.env.local-t. Ezt a hibát egyszer mindenki elköveti.
Tesztelés
Az Auth.js v5 tesztelése sokkal egyszerűbb, mint a v4-é volt, mert a session olvasása sima async függvényhívás. Egy egységteszt példa Vitesttel:
import { vi, test, expect } from "vitest";
import { auth } from "@/auth";
vi.mock("@/auth", () => ({
auth: vi.fn(),
}));
test("védett action elutasítja a vendéget", async () => {
vi.mocked(auth).mockResolvedValue(null);
await expect(deletePost("1")).rejects.toThrow("Unauthorized");
});
End-to-end szinten a Playwright storageState mechanizmusa a barátod: egyszer bejelentkezel, és a session cookie-t újrahasználod a tesztek között. Spórol egy csomó futási időt.
Migráció v4-ről v5-re: rövid checklist
- Telepítsd a
next-auth@beta-t. - Hozd létre az
auth.ts-t ésauth.config.ts-t a gyökérben. - Cseréld a
NEXTAUTH_*env változókatAUTH_*prefixre. - Cseréld le a
getServerSession()hívásokatauth()-ra. - Frissítsd a middleware-t a fenti két-fájlos mintára.
- Az adapter csomagja most
@auth/*-adapternéven publikál (pl.@auth/drizzle-adapter). - És persze: készíts adatbázis-mentést. Az adapter séma kompatibilis, de a teszteléshez érdemes vissza tudni állni — bízz bennem.
Mikor érdemes inkább Clerket vagy Lucia-t választani?
Az Auth.js v5 sweet spotja: kis-közepes alkalmazás, saját adatbázissal, OAuth + Credentials kombóval, és testre szabható UI-jal. De nem mindenre ez a jó eszköz. Ha:
- kész UI-komponenseket akarsz (login form, MFA prompt) → Clerk;
- teljes kontrollt akarsz a session és cookie szinten, adapter-mentesen → Lucia v3;
- vállalati SAML/OIDC kell → WorkOS vagy Auth0.
A többi 80%-os esetben viszont az Auth.js v5 a leggyorsabb út a produkcióig App Routerben. És ez nem kis dolog.
Gyakran ismételt kérdések
Az Auth.js v5 ugyanaz, mint a NextAuth.js?
Igen. Az Auth.js az új márkanév, az npm csomag továbbra is next-auth, és minden régi NextAuth fogalom (provider, adapter, callback) megmaradt. A v5 az első major release ezen az új márkán, és kifejezetten az App Router köré tervezett.
Használhatok Credentials providert adatbázis-stratégiával?
Hivatalosan nem. A Credentials provider csak a JWT session-stratégiával kompatibilis. Ha mindkettőt szeretnéd (saját email/jelszó és azonnali session-érvénytelenítés), két út áll nyitva: vagy egyedi „server-side session table"-t építesz a JWT mellé, vagy átállsz Lucia-féle, alacsonyabb szintű könyvtárra. Egyik sem fájdalommentes.
Mit jelent az AUTH_TRUST_HOST=true és mikor kell beállítani?
A v5 alapértelmezésben elutasítja a callback kéréseket, ha a Host fejléc nem felel meg az ismert envelope-nak — így védi magát a Host header injection ellen. Vercelen ezt automatikusan beállítja a platform. Saját Docker-konténerben, Cloudflare Workers mögött vagy bármilyen reverse proxy mögött manuálisan kell true-ra tenni, máskülönben a bejelentkezés szépen elszáll 500-as hibára.
Hogyan érem el a session-t kliens komponensben?
Csomagold be a layout gyökerét a SessionProvider komponenssel a next-auth/react-ből, majd használd a useSession() hookot. A 2026-os ajánlás viszont az, hogy amikor csak lehet, Server Componentben olvass auth()-tal, és a session-adatot propként add le a kliens komponenseknek. Így kerülöd a felesleges JS-bundle-méretet, és a kezdeti render is gyorsabb lesz.
Hogyan implementáljak role-based access controlt (RBAC)?
Tárold a szerepet a users táblában, told be a jwt callbackben a tokenbe, majd ellenőrizd három helyen: middleware-ben (UX), layoutban (page-szintű védelem), és a Server Actionben/Route Handlerben (a tényleges biztonsági réteg). Soha, de tényleg soha ne támaszkodj csak a kliensoldali session.user.role-ra — az két DevTools-kattintással hamisítható.
Összefoglalás
Az Auth.js v5 — ha komolyan veszed a kettős fájl mintát és a háromrétegű védelmet — 2026-ban egyszerűen a leggyorsabb és legtisztább módja a hitelesítésnek Next.js App Routerben. A három kulcsmondat: használj auth.config.ts-t a middleware-hez, tartsd a Node-only függőségeket az auth.ts-ben, és minden mutáló műveletben hívd újra az auth()-t. Ha ezt megteszed, a v4-es örökség legtöbb csapdájától megszabadulsz, és az új univerzális auth() API minden runtime-on egyformán fog viselkedni. Ennyi az egész.