Auth.js v5 v Next.js 16: Autentifikácia s App Routerom a Drizzle (2026)
Kompletný auth flow v Next.js 16 App Routeri s Auth.js v5 a Drizzle ORM: GitHub OAuth, Credentials, middleware, Server Actions a rozdiel JWT vs database session.
Auth.js v5 (predtým NextAuth.js) je odporúčaná knižnica pre autentifikáciu v Next.js 16 App Routeri. Poskytuje jednotnú auth() funkciu pre Server Components, middleware, Server Actions aj Route Handlers, natívnu podporu pre React Server Components a integráciu s Drizzle ORM cez oficiálny adapter. V tomto sprievodcovi zostavíme kompletný auth flow s GitHub OAuth a Credentials provider, session-based ochranou routes a Postgres databázou, vrátane všetkých rozbitých medzikrokov, ktoré tutoriály zvyčajne preskakujú.
Auth.js v5 zjednotil API cez jednu auth() funkciu, ktorá funguje v Server Components, middleware, Server Actions aj Route Handlers. Už nepotrebujete getServerSession ani useSession pre server-side kód.
Konfigurácia sa rozdeľuje na auth.config.ts (edge-safe, len providers) a auth.ts (obsahuje databázu, Node.js runtime), kvôli obmedzeniam middleware v Edge Runtime.
Drizzle adapter @auth/drizzle-adapter vyžaduje presnú schému s tabuľkami users, accounts, sessions a verification_tokens. Nesprávne stĺpce spôsobia tichú stratu session.
Middleware v Next.js 16 beží defaultne v Node.js runtime, čo umožňuje volať auth() priamo bez callback triku známeho z v4.
Session strategy "jwt" je vhodná pre stateless apps a Edge, "database" pre revocation a audit. V produkcii u Auth.js v5 s Drizzle je database strategy pre revocation nutná.
Server Actions môžu volať signIn() a signOut() priamo, ale musíte správne ošetriť AuthError a redirect(), inak dostanete tichú chybu vo forme HTTP 200 s prázdnym telom.
Prečo Auth.js v5 a čo sa zmenilo oproti NextAuth v4
Auth.js je nový názov projektu NextAuth.js. V5.0 stabilná bola vydaná koncom 2024 a odvtedy prešla viacerými breaking changes, ktoré finálne dozreli s Next.js 16. V4 bola vyslovene vytvorená pre Pages Router s getServerSideProps, a hoci sme ju s App Routerom používali dva roky, vždy to bolo nepohodlné: obalovanie SessionProvider, klientske hooky pre server data, dva samostatné API pre server a klient.
Úprimne, v našom fintech tíme sme migrovali z NextAuth v4 na Auth.js v5 v januári 2026 a najväčšia úľava bola presne toto: jedno auth() volanie funguje všade. V Server Component, v middleware, vo forme handleri Server Action, v Route Handleri, všade to isté API, ktoré vráti session alebo null. Žiaden getServerSession(authOptions), žiadne dva zdroje pravdy.
Ďalšia dôležitá zmena: konfigurácia sa rozdelila na edge-safe časť (bez databázy, bez Node.js API) a plnú konfiguráciu s adapterom. Dôvod je jednoduchý. Middleware v Next.js aj napriek podpore Node.js runtime v 15.2+ často beží v Edge Runtime, a Drizzle s node-postgres tam nefunguje. Prakticky teda máte auth.config.ts, ktorý importuje middleware, a auth.ts, ktorý pridá adapter a exportuje handlery.
Ak migrujete zo staršieho projektu, prečítajte si tiež náš sprievodca Next.js Middleware a Node.js Runtime. Vysvetľuje presne, ako sa runtime pre middleware v Next.js 16 vyberá a prečo to má vplyv na auth kód.
Inicializácia projektu a inštalácia závislostí
Predpokladám čistý Next.js 16 projekt s TypeScript a App Routerom. Pre naše nastavenie potrebujeme Auth.js core, Drizzle adapter, Drizzle ORM, Postgres driver a bcrypt pre Credentials provider:
Pozor: balík sa stále volá next-auth aj v v5, nie @auth/nextjs, ako by ste mohli očakávať. Menovanie je matúce; oficiálna migračná dokumentácia Auth.js v5 to potvrdzuje. Používame @beta tag, pretože v čase písania (september 2026) je najnovšie stabilné vydanie 5.0.0-beta.29, ktoré je produkčne overené a používame ho v našom fintech backende.
Ďalej nastavíme premenné prostredia. V root projektu vytvoríme .env.local:
AUTH_SECRET vygenerujte cez openssl rand -base64 32. Bez neho vám Auth.js v produkcii zhavaruje pri prvom pokuse o session; v dev móde len vypíše warning. GitHub OAuth app zaregistrujete na Settings → Developer settings → OAuth Apps, callback URL http://localhost:3000/api/auth/callback/github.
Drizzle schéma pre Auth.js adapter
Toto je krok, kde sa najviac ľudí sekne. @auth/drizzle-adapter očakáva konkrétne názvy tabuliek a stĺpcov. Ak sa vaše názvy líšia, adapter tíško nič neuloží a login "funguje" (redirect prejde), ale session neexistuje. Uvádzam schému, ktorá odpovedá presne tomu, čo adapter interne queruje:
Migráciu spustíte cez npx drizzle-kit push alebo generovaním SQL cez drizzle-kit generate. Client pre databázu vytvorte v samostatnom súbore:
// db/index.ts
import { drizzle } from "drizzle-orm/postgres-js";
import postgres from "postgres";
import * as schema from "./schema";
const client = postgres(process.env.DATABASE_URL!, { max: 1 });
export const db = drizzle(client, { schema });
Konfigurácia auth.ts a auth.config.ts
Rozdelenie na dva súbory nie je štýlová preferencia, je to technická nutnosť. Middleware môže bežať v Edge Runtime, kde nie sú dostupné Node.js API ani PostgreSQL driver. Preto auth.config.ts obsahuje len providers a callbacks bez databázy:
A hlavný auth.ts, ktorý pridá adapter, Credentials provider a export handlery pre API route:
// auth.ts
import NextAuth from "next-auth";
import Credentials from "next-auth/providers/credentials";
import { DrizzleAdapter } from "@auth/drizzle-adapter";
import bcrypt from "bcryptjs";
import { db } from "@/db";
import { users, accounts, sessions, verificationTokens } from "@/db/schema";
import { eq } from "drizzle-orm";
import authConfig from "./auth.config";
export const { handlers, auth, signIn, signOut } = NextAuth({
adapter: DrizzleAdapter(db, {
usersTable: users,
accountsTable: accounts,
sessionsTable: sessions,
verificationTokensTable: verificationTokens,
}),
session: { strategy: "database" },
...authConfig,
providers: [
...authConfig.providers,
Credentials({
credentials: {
email: { label: "Email", type: "email" },
password: { label: "Heslo", type: "password" },
},
async authorize(credentials) {
if (!credentials?.email || !credentials?.password) return null;
const [user] = await db
.select()
.from(users)
.where(eq(users.email, credentials.email as string))
.limit(1);
if (!user?.passwordHash) return null;
const valid = await bcrypt.compare(
credentials.password as string,
user.passwordHash
);
return valid ? user : null;
},
}),
],
});
Route handler pre samotné auth endpointy je jednorazová záležitosť:
// app/api/auth/[...nextauth]/route.ts
export { GET, POST } from "@/auth";
Ako chrániť routes pomocou middleware v Next.js 16
Middleware je najprirodzenejšie miesto pre auth guardy. Spustí sa pred renderom, môže redirectovať a nezaťažuje jednotlivé stránky boilerplate kódom. V Next.js 16 middleware bežne beží v Node.js runtime, čo umožňuje volať auth() priamo (v v4 sa muselo obchádzať cez withAuth callback vzor):
Tento pattern využíva authorized callback definovaný v auth.config.ts. Návratová hodnota true povolí request, false presmeruje na /prihlasenie, a vrátený Response alebo NextResponse sa použije priamo.
Matcher záznam "/((?!api|_next/static|_next/image|.*\\.png$).*)" vylučuje statické assety a API routes. Tie by ste inak spustili middleware pre každý JS chunk a obrázok, čo je pri database session strategy drahá operácia (query do DB pre každý request).
Ako získať session v Server Components
Toto je časť, kvôli ktorej má Auth.js v5 zmysel. V Server Component jednoducho zavoláte auth() a dostanete typovanú session alebo null. Žiaden hook, žiadny provider, žiadne use client:
Rovnaké API funguje v Route Handleri, v Server Actione aj v generateMetadata. Pre client komponenty existuje useSession() hook a SessionProvider, ale v mojom tíme sme sa dohodli, že session čítame len server-side a klientovi ju posielame ako prop. Je to jeden zdroj pravdy a nevznikajú race conditions medzi middleware redirect a klientským re-hydratom.
Server Actions umožňujú spracovať login form bez API route ani klientského JS. signIn vezmite priamo z auth.ts a zavolajte ho v action. Osobne toto považujem za najčistejší auth flow, aký som v Next.js kedy písal:
// app/prihlasenie/actions.ts
"use server";
import { signIn } from "@/auth";
import { AuthError } from "next-auth";
export async function login(formData: FormData) {
try {
await signIn("credentials", {
email: formData.get("email"),
password: formData.get("password"),
redirectTo: "/dashboard",
});
} catch (error) {
if (error instanceof AuthError) {
switch (error.type) {
case "CredentialsSignin":
return { error: "Nesprávny email alebo heslo." };
default:
return { error: "Nastala chyba pri prihlasovaní." };
}
}
throw error; // NEXT_REDIRECT musí prebublať!
}
}
Podobne pre logout, Server Action zavolá signOut():
"use server";
import { signOut } from "@/auth";
export async function logout() {
await signOut({ redirectTo: "/" });
}
Credentials provider s hashovaným heslom
Credentials provider je jediný spôsob, ako mať klasický email + heslo flow (Auth.js neposkytuje built-in registráciu). Registráciu si napíšete sami ako Server Action, ktorá vloží nového užívateľa s hashovaným heslom:
// app/registracia/actions.ts
"use server";
import { db } from "@/db";
import { users } from "@/db/schema";
import bcrypt from "bcryptjs";
import { signIn } from "@/auth";
export async function register(formData: FormData) {
const email = formData.get("email") as string;
const password = formData.get("password") as string;
const passwordHash = await bcrypt.hash(password, 12);
await db.insert(users).values({ email, passwordHash });
await signIn("credentials", { email, password, redirectTo: "/dashboard" });
}
Pozor, pre production doplňte: validáciu formátu emailu (napr. cez Zod), overenie existencie emailu vopred, rate-limiting registrácie, minimálnu dĺžku hesla. V našom fintech projekte na registráciu púšťame Zod schema a limit 5 pokusov / IP / hodinu cez upstash rate-limiter.
Bcrypt s cost factorom 12 je v roku 2026 rozumný default. Pri 12 je bcrypt hash na M2 MacBook ~300 ms, čo je dosť na to, aby bruteforce bol nepraktický, ale nie taký pomalý, aby vás to zabolelo pri legitímnom prihlásení. Alternatívou je Argon2id, ktorý je novší a odolnejší proti GPU útokom, ale pre 99 % projektov je bcrypt v poriadku.
Aký je rozdiel medzi JWT a database session strategy?
Auth.js podporuje dve session strategy a výber ovplyvňuje performance aj bezpečnostný model. Porovnanie:
Vlastnosť
JWT strategy
Database strategy
Storage
Cookie (encrypted JWE)
Postgres tabuľka sessions
DB query per request
0
1 (SELECT sessions)
Session revocation
Nemožná pred expiry
Okamžitá (DELETE)
Session veľkosť
~1-4 KB cookie
~40 B token cookie
Edge Runtime
Áno
Nie (potrebuje DB driver)
Update user info
Až po re-login
Okamžite pri ďalšom requeste
Vhodné pre
Stateless apps, marketing weby, Edge
Fintech, admin panely, audit
V mojej praxi: JWT používame pre verejné marketing weby s prihláseným newsletter opt-inom (nič citlivé). Database strategy používame pre všetko, čo má dashboard alebo umožňuje užívateľovi vidieť dáta iných užívateľov. Dôvod je jednoduchý. Pri kompromitovanom účte potrebujete session okamžite zabiť, a to s JWT jednoducho nespravíte bez pridania blacklist tabuľky, čo eliminuje výhodu stateless prístupu.
Ak si chcete overiť odporúčania priamo v zdroji, spec pre JWT (RFC 7519) a JWE encryption format (RFC 7516) je dobré aspoň raz preletieť. Auth.js interne používa JWE, nie plain JWT, takže tokeny sú zašifrované, nie len podpísané.
Príbuzný pattern (mutácie a formuláre v RSC) rozoberáme aj v článku Next.js 16 Server Actions: praktický sprievodca, ak sa chcete v Server Actions cítiť pohodlnejšie predtým, než začnete robiť auth flow.
Časté chyby a ich riešenia
Chyba: "MissingCSRF"
Vyskytuje sa, keď voláte signIn s redirect: false a spracovávate response manuálne. Auth.js v5 vyžaduje CSRF token v POST requeste. Riešenie: buď použite Server Action variant (ako vyššie), alebo si natiahnite token cez getCsrfToken() pred requestom.
Chyba: session je vždy null aj po úspešnom logine
V 90 % prípadov je príčina nesprávna Drizzle schéma. Najčastejšie chýba stĺpec emailVerified alebo je pomenovaný snake_case. Skontrolujte cez drizzle-kit studio, či sa po logine vytvoril záznam v account tabuľke a session tabuľke. Ak nie, adapter ticho zlyhal.
Chyba: middleware nekonečná redirect slučka
Ak matcher pokrýva aj samotnú /prihlasenie stránku, middleware ju bude redirectovať na seba samu. Riešenie: v authorized callbacku výslovne povoliť auth routes:
Chyba: "Cannot find module 'crypto'" v Edge middleware
Vzniká, ak auth.config.ts nechtiac importuje čokoľvek, čo transitively naťahuje Node.js API. Napríklad import bcrypt priamo v config súbore. Riešenie: v auth.config.ts nesmie byť žiadny import z @/db, bcrypt, @auth/drizzle-adapter, to všetko patrí len do auth.ts. Detailný breakdown edge vs. node runtime rozdielov je v tomto sprievodcovi.
Chyba: OAuth callback vráti "Configuration"
Chýba AUTH_SECRET alebo je AUTH_URL nesprávne (napr. bez schémy alebo s trailing slash). V produkcii na Vercel je AUTH_URL automaticky odvodený z VERCEL_URL, ale za reverse proxy alebo na custom doméne ho musíte nastaviť ručne. Detailné error kódy sú v Auth.js errors referencii.
Často kladené otázky
Aký je rozdiel medzi Auth.js a NextAuth?
Auth.js je rebrandovaný NextAuth.js. Počnúc v5 sa projekt oficiálne volá Auth.js a rozšíril podporu okrem Next.js aj na SvelteKit, SolidStart a Express. Balík na npm sa však stále volá next-auth a v5 verzia je určená pre Next.js. Ostatné frameworky používajú vlastné balíky (@auth/sveltekit, @auth/express).
Je Auth.js v5 vhodný pre produkciu?
Áno, aj napriek "beta" tagu je 5.0.0-beta.29 (a novšie beta releasy) v produkcii používaný veľkými spoločnosťami vrátane Vercel, Cal.com a nášho fintech tímu. "Beta" v tomto kontexte znamená, že sa môže drobne meniť API, nie že by bol nespoľahlivý. Ak potrebujete plne stabilné SemVer garancie, počkajte na 5.0 GA (očakávaný Q4 2026).
Môžem mať database aj JWT session strategy v jednom projekte?
Nie, session.strategy je globálne nastavenie a nemôžete ho meniť per user alebo per route. Ak potrebujete rôzne správanie, musíte spraviť dve samostatné Auth.js inštancie s rôznymi cookie name a base path, čo je zložité a v praxi to takmer nikto nerobí. Radšej si vyberte jednu stratégiu, ktorá vyhovuje pre všetkých užívateľov.
Ako pridám vlastné polia do user objektu (napríklad role)?
Pridajte stĺpec do users tabuľky v Drizzle schéme, rozšírte next-auth typy v next-auth.d.ts a v session callback-u kopírujte hodnotu z user do session.user. Adapter automaticky natiahne všetky stĺpce z users tabuľky, takže user.role bude dostupné v callback-u aj bez ďalšej práce.
Prečo Auth.js v5 nemá built-in email registráciu?
Filozofia Auth.js je poskytnúť auth flow (session, providers, callbacks) a nechať user management na aplikácii. Registrácia sa v každom projekte líši (custom polia, verifikácia, invite systémy), preto ju viete jednoducho postaviť sami ako Server Action, ktorá vloží užívateľa do DB a zavolá signIn. Ak chcete out-of-box riešenie, pozrite sa na Clerk alebo Better Auth. Auth.js zámerne túto vrstvu neposkytuje.
Tomasz is a senior frontend engineer with eleven years of experience, currently a tech lead at a Berlin-based fintech where he migrated 180 routes from CRA to Next.js 14 App Router over nine months without a single user-facing outage. Before that, four years at Klarna building the merchant dashboard in Next.js, and two years at a small agency in Krakow shipping marketing sites for European retailers.
He maintains two small open-source libraries in the React ecosystem, one for typed environment variables and one for a Suspense-friendly data fetching wrapper that predates the React 19 use() hook. He prefers Drizzle over Prisma, NextAuth over Clerk for self-hosted setups, and has strong feelings about putting business logic in Server Components.
He writes the kind of tutorials that show the broken intermediate state, not just the working final code.
Praktický sprievodca Route Handlers v Next.js 16 App Routeri: HTTP metódy, cachovanie s use cache direktívou, Zod validácia, CORS v middleware, streaming, Edge vs Node.js runtime a playbook migrácie z pages/api.
Praktický sprievodca opravou hydratačných chýb v Next.js 16 s React DevTools, useEffect vzorom, dynamic importmi a suppressHydrationWarning. S ukážkami kódu.