İçeriğe geç
nextjs

Next.js Authentication: NextAuth.js ve Clerk

Tarık Tunç
Next.js Authentication: NextAuth.js ve Clerk

Next.js Authentication: NextAuth.js ve Clerk

Kimlik doğrulama (authentication), web uygulamalarının en kritik bileşenlerinden biridir. Next.js ekosisteminde en popüler iki çözüm NextAuth.js (Auth.js) ve Clerk'tir. Bu yazıda her iki kütüphaneyi detaylı olarak inceleyecek, kurulumdan production'a kadar adım adım rehber sunacağız.

NextAuth.js (Auth.js) Nedir?

NextAuth.js, Next.js için açık kaynaklı bir authentication kütüphanesidir. OAuth, email/password, magic link ve daha birçok yöntemi destekler. v5 ile birlikte Auth.js adını almış ve framework-agnostik hale gelmiştir.

Kurulum

npm install next-auth@beta
# .env.local
AUTH_SECRET=your-secret-key-here
AUTH_GITHUB_ID=github-client-id
AUTH_GITHUB_SECRET=github-client-secret
AUTH_GOOGLE_ID=google-client-id
AUTH_GOOGLE_SECRET=google-client-secret

Temel Yapılandırma

// auth.js (proje kökünde)
import NextAuth from 'next-auth';
import GitHub from 'next-auth/providers/github';
import Google from 'next-auth/providers/google';
import Credentials from 'next-auth/providers/credentials';
import { PrismaAdapter } from '@auth/prisma-adapter';
import { prisma } from '@/lib/prisma';
import bcrypt from 'bcryptjs';

export const { handlers, auth, signIn, signOut } = NextAuth({
  adapter: PrismaAdapter(prisma),
  providers: [
    GitHub,
    Google,
    Credentials({
      name: 'E-posta ve Şifre',
      credentials: {
        email: { label: 'E-posta', type: 'email' },
        password: { label: 'Şifre', type: 'password' },
      },
      async authorize(credentials) {
        const user = await prisma.user.findUnique({
          where: { email: credentials.email },
        });

        if (!user || !user.password) return null;

        const isValid = await bcrypt.compare(
          credentials.password,
          user.password
        );

        return isValid ? user : null;
      },
    }),
  ],
  callbacks: {
    async session({ session, token }) {
      if (token.sub) {
        session.user.id = token.sub;
        session.user.role = token.role;
      }
      return session;
    },
    async jwt({ token, user }) {
      if (user) {
        token.role = user.role;
      }
      return token;
    },
  },
  pages: {
    signIn: '/login',
    error: '/auth/error',
  },
});

Route Handler

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

Middleware ile Koruma

// middleware.js
import { auth } from '@/auth';

export default auth((req) => {
  const isLoggedIn = !!req.auth;
  const isOnDashboard = req.nextUrl.pathname.startsWith('/dashboard');
  const isOnLogin = req.nextUrl.pathname === '/login';

  if (isOnDashboard && !isLoggedIn) {
    return Response.redirect(new URL('/login', req.url));
  }

  if (isOnLogin && isLoggedIn) {
    return Response.redirect(new URL('/dashboard', req.url));
  }
});

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

Server Component'lerde Kullanım

// app/dashboard/page.js
import { auth } from '@/auth';
import { redirect } from 'next/navigation';

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

  if (!session) {
    redirect('/login');
  }

  return (
    <div>
      <h1>Hoş geldin, {session.user.name}!</h1>
      <p>E-posta: {session.user.email}</p>
      <img src={session.user.image} alt="Avatar" />
    </div>
  );
}

Login Sayfası

// app/login/page.js
import { signIn } from '@/auth';

export default function LoginPage() {
  return (
    <div className="login-container">
      <h1>Giriş Yap</h1>

      {/* OAuth butonları */}
      <form action={async () => { 'use server'; await signIn('github'); }}>
        <button type="submit">GitHub ile Giriş</button>
      </form>

      <form action={async () => { 'use server'; await signIn('google'); }}>
        <button type="submit">Google ile Giriş</button>
      </form>

      <hr />

      {/* Credentials formu */}
      <form action={async (formData) => {
        'use server';
        await signIn('credentials', formData);
      }}>
        <input name="email" type="email" placeholder="E-posta" required />
        <input name="password" type="password" placeholder="Şifre" required />
        <button type="submit">Giriş Yap</button>
      </form>
    </div>
  );
}

Clerk Nedir?

Clerk, tam yönetilen (fully managed) bir authentication ve kullanıcı yönetimi platformudur. Hazır UI bileşenleri, kullanıcı profil yönetimi, organizasyon desteği ve webhook'lar sunar.

Kurulum

npm install @clerk/nextjs
# .env.local
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
CLERK_SECRET_KEY=sk_test_...
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up

Provider Kurulumu

// app/layout.js
import { ClerkProvider } from '@clerk/nextjs';
import { trTR } from '@clerk/localizations';

export default function RootLayout({ children }) {
  return (
    <ClerkProvider localization={trTR}>
      <html lang="tr">
        <body>{children}</body>
      </html>
    </ClerkProvider>
  );
}

Middleware

// middleware.js
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';

const isProtectedRoute = createRouteMatcher([
  '/dashboard(.*)',
  '/settings(.*)',
  '/api/private(.*)',
]);

export default clerkMiddleware(async (auth, req) => {
  if (isProtectedRoute(req)) {
    await auth.protect();
  }
});

export const config = {
  matcher: ['/((?!.*\\..*|_next).*)', '/', '/(api|trpc)(.*)'],
};

Hazır UI Bileşenleri

import {
  SignInButton,
  SignUpButton,
  SignedIn,
  SignedOut,
  UserButton,
} from '@clerk/nextjs';

function Header() {
  return (
    <header>
      <nav>
        <SignedOut>
          <SignInButton mode="modal" />
          <SignUpButton mode="modal" />
        </SignedOut>

        <SignedIn>
          <UserButton
            appearance={{
              elements: { avatarBox: 'w-10 h-10' },
            }}
            afterSignOutUrl="/"
          />
        </SignedIn>
      </nav>
    </header>
  );
}

Server Component'lerde Clerk

import { currentUser, auth } from '@clerk/nextjs/server';

export default async function ProfilePage() {
  const user = await currentUser();
  const { userId, orgId } = await auth();

  if (!user) return <p>Giriş yapmanız gerekiyor.</p>;

  return (
    <div>
      <h1>{user.firstName} {user.lastName}</h1>
      <p>{user.emailAddresses[0].emailAddress}</p>
      <img src={user.imageUrl} alt="Profil" />
    </div>
  );
}

Karşılaştırma

Özellik NextAuth.js Clerk
Fiyat Ücretsiz (açık kaynak) Freemium (10K MAU ücretsiz)
Barındırma Self-hosted Managed service
UI Bileşenleri Kendiniz yazarsınız Hazır bileşenler
Kullanıcı Yönetimi Kendiniz yaparsınız Dashboard var
Veritabanı Kendi veritabanınız Clerk yönetir
Özelleştirme Tam kontrol Tema ile sınırlı
Organizasyon Manuel Yerleşik destek
İpucu: Tam kontrol istiyorsanız ve veritabanınızı kendiniz yönetmek istiyorsanız NextAuth.js'i tercih edin. Hızlı kurulum, hazır UI bileşenleri ve yönetim paneli istiyorsanız Clerk daha uygun olabilir. Her ikisi de Next.js App Router ile tam uyumludur.
Uyarı: Credentials provider (email/şifre) kullanıyorsanız, şifreleri mutlaka bcrypt ile hash'leyin. CSRF korumasını aktif bırakın. Session token'larını HttpOnly cookie'lerde saklayın. Rate limiting uygulayın.

Sonuç

Next.js'te authentication için NextAuth.js ve Clerk iki güçlü seçenektir. NextAuth.js, açık kaynak ve self-hosted yapısıyla tam kontrol sunarken, Clerk managed bir servis olarak hızlı kurulum ve hazır bileşenler sağlar. Her iki çözüm de middleware tabanlı route koruması, Server Component desteği ve modern Next.js App Router uyumluluğu sunar. Projenizin ihtiyaçlarına, bütçenize ve kontrol gereksiniminize göre doğru çözümü seçebilirsiniz.

Kaynaklar

İlgili Yazılar