Auth.js v5 στο Next.js 15: Πλήρης Οδηγός Authentication με OAuth, Credentials και Middleware

Πλήρης οδηγός για Auth.js v5 στο Next.js 15 App Router: OAuth, Credentials, JWT vs database sessions, middleware και Drizzle adapter με έτοιμα παραδείγματα.

Auth.js v5 + Next.js 15 Guide (2026)

Τελευταία ενημέρωση: 9 Ιουνίου 2026

Το Auth.js v5 (η νέα έκδοση του NextAuth.js) είναι μια ολοκληρωμένη βιβλιοθήκη authentication για το Next.js 15 App Router, που υποστηρίζει OAuth providers, credentials, magic links και passkeys κάτω από ενιαία API. Στην έκδοση 5, η αρχιτεκτονική προσαρμόστηκε πλήρως στα React Server Components και τα Server Actions: η συνάρτηση auth() αντικαθιστά το getServerSession, η ρύθμιση γίνεται από ένα κεντρικό αρχείο auth.ts, και το middleware integration είναι first-class. Σε αυτόν τον οδηγό θα δούμε βήμα-βήμα πώς στήνεται.

Honestly, η διαφορά εμπειρίας από το v4 είναι τεράστια. Στο τελευταίο μου project μετέφερα μια εφαρμογή SaaS από NextAuth v4 σε Auth.js v5 και ο κώδικας authentication μειώθηκε σχεδόν στο μισό.

  • Το Auth.js v5 (beta από το 2024, σταθερό το 2025/2026) είναι ο διάδοχος του NextAuth.js με Universal API και πλήρη υποστήριξη Edge runtime.
  • Το setup γίνεται σε ένα αρχείο auth.ts που εξάγει τις { handlers, auth, signIn, signOut }, αντικαθιστώντας το παλιό [...nextauth]/route.ts pattern.
  • Η συνάρτηση auth() δουλεύει σε Server Components, Route Handlers, Server Actions και Middleware χωρίς ξεχωριστά helpers.
  • Υποστηρίζει OAuth (Google, GitHub, 80+ providers), Credentials, Email magic links και WebAuthn/Passkeys.
  • Με database adapters (Drizzle, Prisma, MongoDB) μπορείς να αποθηκεύεις χρήστες και sessions στη βάση δεδομένων, αλλιώς χρησιμοποιείται JWT.
  • Το middleware integration με NextAuth({...}).auth ως default export προστατεύει routes χωρίς manual cookie parsing.

Τι είναι το Auth.js v5 και τι αλλάζει από το NextAuth;

Το Auth.js είναι το επίσημο rebrand του NextAuth.js, που σηματοδοτεί τη μετατροπή της βιβλιοθήκης σε framework-agnostic authentication layer. Παρότι το πακέτο συνεχίζει να δημοσιεύεται ως next-auth για το Next.js, υπάρχουν πλέον και adapters για SvelteKit (@auth/sveltekit), Express (@auth/express) και Qwik. Στην πράξη, για το Next.js η εμπειρία ανάπτυξης αλλάζει δραστικά σε σχέση με το v4.

Οι σημαντικότερες αλλαγές που πρέπει να γνωρίζεις πριν ξεκινήσεις:

  • Ενιαίο αρχείο auth.ts: Αντί για ξεχωριστή ρύθμιση σε [...nextauth]/route.ts και imports σε κάθε file, καλείς μία φορά τη NextAuth(config) και εξάγεις τα handlers μαζί με helpers.
  • Universal auth() function: Αντικαθιστά τα getServerSession, getSession, withAuth. Δουλεύει παντού: Server Components, Route Handlers, Middleware, Server Actions.
  • Edge-compatible πλέον by default: Με κατάλληλο διαχωρισμό config (Edge-safe παράγοντες χωρίς Node modules) τρέχει σε Edge runtime.
  • Account linking: Νέο API allowDangerousEmailAccountLinking και βελτιωμένο linking flow για πολλαπλούς providers.
  • Καλύτερο TypeScript: Τα callbacks έχουν πλέον σωστά τυποποιημένα user, token και session objects.

Εγκατάσταση και αρχικό setup σε Next.js 15

So, ας ξεκινήσουμε. Για ένα Next.js 15 project με App Router, εγκατέστησε το πακέτο και δημιούργησε ένα κρυπτογραφικά ισχυρό AUTH_SECRET:

pnpm add next-auth@beta
# δημιούργησε secret για JWT signing
npx auth secret
# προσθέτει AUTH_SECRET στο .env.local αυτόματα

Στη συνέχεια δημιούργησε ένα αρχείο auth.ts στη ρίζα του project (όχι μέσα στο app/):

// auth.ts
import NextAuth from "next-auth"
import GitHub from "next-auth/providers/github"
import Google from "next-auth/providers/google"

export const { handlers, auth, signIn, signOut } = NextAuth({
  providers: [GitHub, Google],
  pages: {
    signIn: "/login",
  },
  callbacks: {
    authorized: async ({ auth }) => {
      // αν επιστρέψει true ο χρήστης έχει access
      return !!auth
    },
  },
})

Το catch-all route handler συνδέει το Auth.js με τα Next.js Route Handlers:

// app/api/auth/[...nextauth]/route.ts
import { handlers } from "@/auth"
export const { GET, POST } = handlers

Με μόλις αυτά τα τρία αρχεία, το Auth.js είναι λειτουργικό. Τα endpoints /api/auth/signin, /api/auth/signout, /api/auth/session και /api/auth/callback/[provider] είναι έτοιμα.

Σύνδεση με OAuth providers (Google, GitHub)

Το Auth.js v5 υποστηρίζει πάνω από 80 OAuth providers out-of-the-box. Για το GitHub θα χρειαστείς ένα OAuth App από το GitHub Developer Settings, με callback URL http://localhost:3000/api/auth/callback/github. Για το Google αντίστοιχα από το Google Cloud Console.

Πρόσθεσε τα credentials στο .env.local:

AUTH_GITHUB_ID=your_github_client_id
AUTH_GITHUB_SECRET=your_github_client_secret
AUTH_GOOGLE_ID=your_google_client_id
AUTH_GOOGLE_SECRET=your_google_client_secret
AUTH_SECRET=your_generated_secret

Το Auth.js αναγνωρίζει αυτόματα τις μεταβλητές με πρόθεμα AUTH_, οπότε δεν χρειάζεται manual configuration. Σε ένα Server Component μπορείς πλέον να ελέγξεις την session του χρήστη απευθείας:

// app/page.tsx
import { auth, signIn, signOut } from "@/auth"

export default async function Home() {
  const session = await auth()

  if (!session?.user) {
    return (
      <form action={async () => {
        "use server"
        await signIn("github")
      }}>
        <button type="submit">Sign in με GitHub</button>
      </form>
    )
  }

  return (
    <div>
      <p>Καλώς ήρθες, {session.user.name}</p>
      <form action={async () => {
        "use server"
        await signOut()
      }}>
        <button type="submit">Sign out</button>
      </form>
    </div>
  )
}

Πρόσεξε ότι οι κλήσεις signIn και signOut γίνονται μέσω inline Server Actions. Αυτό είναι το επίσημο pattern σε App Router και δεν χρειάζεται client component. Για περισσότερα patterns με μεταβολές δεδομένων δες τον οδηγό μας για Server Actions στο Next.js 15.

Credentials provider με email και password

Αν θες παραδοσιακό email/password login, ο Credentials provider σου επιτρέπει να ορίσεις δική σου authorize function. Σε αντίθεση με τους OAuth providers, εδώ είσαι 100% υπεύθυνος για password hashing, rate limiting και validation.

// auth.ts (επέκταση)
import Credentials from "next-auth/providers/credentials"
import { z } from "zod"
import bcrypt from "bcryptjs"
import { getUserByEmail } from "@/lib/users"

const credentialsSchema = z.object({
  email: z.string().email(),
  password: z.string().min(8),
})

export const { handlers, auth, signIn, signOut } = NextAuth({
  providers: [
    GitHub,
    Google,
    Credentials({
      credentials: {
        email: { label: "Email", type: "email" },
        password: { label: "Password", type: "password" },
      },
      authorize: async (credentials) => {
        const parsed = credentialsSchema.safeParse(credentials)
        if (!parsed.success) return null

        const user = await getUserByEmail(parsed.data.email)
        if (!user || !user.passwordHash) return null

        const isValid = await bcrypt.compare(
          parsed.data.password,
          user.passwordHash
        )
        if (!isValid) return null

        return {
          id: user.id,
          email: user.email,
          name: user.name,
        }
      },
    }),
  ],
  session: { strategy: "jwt" },
})

Διαχείριση session: JWT vs Database

Το Auth.js υποστηρίζει δύο στρατηγικές session: JWT (stateless, default) και database (stateful). Η επιλογή επηρεάζει σημαντικά απόδοση και λειτουργικότητα.

ΧαρακτηριστικόJWT StrategyDatabase Strategy
ΑποθήκευσηEncrypted cookie (JWE)Row σε sessions table
Edge runtimeΠλήρως συμβατόΑπαιτεί DB connection
Immediate revokeΜέχρι expirationΔιαγραφή row
ScalingStateless (εύκολο)Χρειάζεται sticky DB
Default expiration30 ημέρες30 ημέρες (configurable)
Credentials providerΥποχρεωτικόΔεν υποστηρίζεται
Custom data στο sessionΜέσω jwt callbackΜέσω session callback

Για τις περισσότερες εφαρμογές προτείνουμε JWT strategy. Λειτουργεί παντού, scales οριζόντια και έχει χαμηλότερο latency. Πρόσθεσε custom claims στο token μέσω callbacks:

callbacks: {
  jwt: async ({ token, user }) => {
    if (user) {
      token.id = user.id
      token.role = user.role
    }
    return token
  },
  session: async ({ session, token }) => {
    if (session.user) {
      session.user.id = token.id as string
      session.user.role = token.role as string
    }
    return session
  },
},

Για επέκταση των TypeScript types, δημιούργησε ένα next-auth.d.ts με module augmentation:

// next-auth.d.ts
import "next-auth"

declare module "next-auth" {
  interface Session {
    user: {
      id: string
      role: string
      email: string
      name: string
    }
  }
}

Πώς προστατεύεις routes με middleware;

Το middleware integration του Auth.js v5 είναι από τις πιο καθαρές αλλαγές σε σχέση με το v4. Αντί για το παλιό withAuth HOC, εξάγεις απευθείας την auth ως default export:

// middleware.ts
export { auth as middleware } from "@/auth"

export const config = {
  matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
}

Αυτό τρέχει τον authorized callback που ορίσαμε στο auth.ts πριν από κάθε request. Για πιο fine-grained logic, μπορείς να γράψεις custom middleware function:

// middleware.ts
import { auth } from "@/auth"
import { NextResponse } from "next/server"

export default auth((req) => {
  const isLoggedIn = !!req.auth
  const isProtected = req.nextUrl.pathname.startsWith("/dashboard")
  const isAuthPage = req.nextUrl.pathname.startsWith("/login")

  if (isProtected && !isLoggedIn) {
    const callbackUrl = encodeURIComponent(req.nextUrl.pathname)
    return NextResponse.redirect(
      new URL(`/login?callbackUrl=${callbackUrl}`, req.url)
    )
  }

  if (isAuthPage && isLoggedIn) {
    return NextResponse.redirect(new URL("/dashboard", req.url))
  }
})

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

Για βαθύτερη κάλυψη του middleware lifecycle (rate limiting, i18n, headers), δες τον οδηγό μας για Next.js Middleware.

Integration με Drizzle ORM adapter

Αν θες database sessions ή απλά persistence χρηστών, το Auth.js έχει επίσημο adapter για το Drizzle ORM. Πρώτα εγκατέστησε το πακέτο:

pnpm add @auth/drizzle-adapter

Ορίσε το schema με τους πίνακες που περιμένει το Auth.js (users, accounts, sessions, verificationTokens):

// db/schema.ts
import { pgTable, text, timestamp, primaryKey, integer } from "drizzle-orm/pg-core"
import type { AdapterAccount } from "next-auth/adapters"

export const users = pgTable("user", {
  id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
  name: text("name"),
  email: text("email").unique(),
  emailVerified: timestamp("emailVerified", { mode: "date" }),
  image: text("image"),
})

export const accounts = pgTable(
  "account",
  {
    userId: text("userId").notNull().references(() => users.id, { onDelete: "cascade" }),
    type: text("type").$type<AdapterAccount["type"]>().notNull(),
    provider: text("provider").notNull(),
    providerAccountId: text("providerAccountId").notNull(),
    refresh_token: text("refresh_token"),
    access_token: text("access_token"),
    expires_at: integer("expires_at"),
    token_type: text("token_type"),
    scope: text("scope"),
    id_token: text("id_token"),
    session_state: text("session_state"),
  },
  (account) => ({
    compoundKey: primaryKey({ columns: [account.provider, account.providerAccountId] }),
  })
)

Σύνδεσε τον adapter στο auth.ts:

import { DrizzleAdapter } from "@auth/drizzle-adapter"
import { db } from "@/db"

export const { handlers, auth, signIn, signOut } = NextAuth({
  adapter: DrizzleAdapter(db),
  providers: [GitHub, Google],
  session: { strategy: "jwt" }, // ή "database"
})

Για περισσότερα για το Drizzle setup, schema migrations και relations, δες τον αναλυτικό μας οδηγό για Drizzle ORM στο Next.js App Router.

Sign-in και sign-out με Server Actions

Σε προηγούμενες εκδόσεις του NextAuth, το client-side signIn() από το next-auth/react ήταν το standard pattern. Στο Auth.js v5 με App Router, οι Server Actions προσφέρουν πιο καθαρή, type-safe και progressively-enhanced εμπειρία.

// app/login/page.tsx
import { signIn } from "@/auth"
import { AuthError } from "next-auth"
import { redirect } from "next/navigation"

async function login(formData: FormData) {
  "use server"
  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 redirect("/login?error=invalid")
        default:
          return redirect("/login?error=unknown")
      }
    }
    throw error // Next.js redirect throws, οπότε πρέπει να ξανα-throw
  }
}

export default function LoginPage() {
  return (
    <form action={login}>
      <input name="email" type="email" required />
      <input name="password" type="password" required />
      <button type="submit">Sign in</button>
    </form>
  )
}

Συνηθισμένα προβλήματα και αντιμετώπιση

Παρακάτω τα πιο συχνά λάθη που βλέπουμε στα Auth.js v5 setups, και πώς να τα αποφύγεις.

Σφάλμα: "MissingSecret"

Δεν έχει οριστεί η AUTH_SECRET environment variable. Τρέξε npx auth secret για αυτόματη γέννηση, ή δημιούργησε χειροκίνητα με openssl rand -base64 32. Σε production (Vercel, Cloudflare), πρόσθεσε το secret στις environment variables του project.

Σφάλμα: "Edge runtime does not support Node.js 'crypto' module"

Συμβαίνει όταν εισάγεις bcrypt, Prisma client ή Node-only βιβλιοθήκη στο middleware. Διαχώρισε το config: φτιάξε auth.config.ts με μόνο OAuth providers (Edge-safe) και χρησιμοποίησέ το στο middleware. Το full auth.ts με Credentials + adapter παραμένει για το API route.

Το session επιστρέφει null αμέσως μετά το signIn

Σχεδόν πάντα είναι θέμα cookie configuration. Έλεγξε ότι το AUTH_URL ταιριάζει με το πραγματικό domain (συμπεριλαμβανομένου https:// σε production) και ότι δεν έχεις SameSite=Strict cookies σε cross-origin OAuth callbacks.

OAuth callback redirect loop

Συνήθως είναι λάθος callback URL στο developer console του provider. Το pattern είναι {AUTH_URL}/api/auth/callback/{provider}. Για localhost χρησιμοποίησε http://localhost:3000/api/auth/callback/github ακριβώς όπως είναι στο GitHub OAuth App settings.

Συχνές ερωτήσεις

Είναι το Auth.js το ίδιο με το NextAuth.js;

Το Auth.js είναι το rebranded όνομα του project NextAuth.js και πλέον υποστηρίζει πολλά frameworks (Next.js, SvelteKit, Express, Qwik). Για το Next.js το πακέτο εξακολουθεί να ονομάζεται next-auth και η v5 είναι η σταθερή έκδοση που στοχεύει το App Router.

Μπορώ να χρησιμοποιήσω Auth.js v5 με το Pages Router;

Ναι, αλλά το v5 σχεδιάστηκε με προτεραιότητα το App Router. Για legacy projects σε Pages Router, το v4 (next-auth@4) παραμένει υποστηριζόμενο. Αν ξεκινάς νέο project το 2026, χρησιμοποίησε App Router μαζί με v5.

Πώς προσθέτω custom πεδία στο session του χρήστη;

Μέσω των jwt και session callbacks στο auth.ts: πρόσθεσε τα πεδία στο token στο πρώτο, και επανέφερέ τα στο session object στο δεύτερο. Επέκτεινε επίσης τους TypeScript τύπους με module augmentation σε ένα next-auth.d.ts αρχείο.

Υποστηρίζει το Auth.js v5 passkeys και WebAuthn;

Ναι. Υπάρχει επίσημος WebAuthn provider (@auth/webauthn-provider) που υλοποιεί passwordless authentication με passkeys. Απαιτεί database adapter για αποθήκευση των authenticator credentials και είναι σε σταθερή κατάσταση από τα τέλη του 2025.

Πώς κάνω logout τον χρήστη server-side;

Εισάγεις το signOut από το @/auth και το καλείς μέσα σε Server Action: await signOut({ redirectTo: "/" }). Αυτό διαγράφει το session cookie (JWT strategy) ή τη session row (database strategy) και κάνει redirect.

Editorial Team
Σχετικά με τον Συγγραφέα Editorial Team

Our team of expert writers and editors.