AI

OSSの認証ライブラリ比較:Auth.js vs Lucia vs Better Auth でNext.jsにセキュアな認証を実装する

オープンソースラボ編集部2026年6月14日

OSSの認証ライブラリ比較:Auth.js vs Lucia vs Better Auth でNext.jsにセキュアな認証を実装する

Clerk(月$25〜)・Auth0(月$23〜)・Firebase Auth(無料〜)に対して、Auth.js(NextAuth.js)(最も使われるOSSNext.js認証)・Lucia(軽量・シンプルな認証ライブラリ)・Better Auth(TypeScript特化・フル機能)はOSSの認証ライブラリです。

認証実装で押さえるべき要件

  • セッション管理: JWTまたはDBセッションでユーザー状態を永続化
  • OAuthプロバイダー統合: GitHub・Google・Twitter等のソーシャルログイン
  • セキュリティ: CSRFトークン・セキュアCookie・HTTPS Only・レートリミット
  • ロールベースアクセス: admin/user/guestの権限をMiddlewareでRoute保護

主要ツールの概要

Auth.js(NextAuth.js v5)

2020年に公開されたTypeScript製のOSSNext.js認証ライブラリです。GitHubスター25k+。Next.jsアプリの認証で最も使われているライブラウリで、70以上のOAuthプロバイダー(GitHub・Google・Spotify・Apple等)をサポート。v5からEdge RuntimeおよびNext.js App Routerにフル対応し、Adapter経由でPrisma・Drizzle・Supabase等のDBにセッションを保存できます。

# Auth.js v5 のインストール
npm install next-auth@beta @auth/drizzle-adapter
# または
npm install next-auth@beta @auth/prisma-adapter
// auth.ts - Auth.jsの設定(App Router)
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 { DrizzleAdapter } from "@auth/drizzle-adapter";
import { db } from "@/lib/db";
import { users, accounts, sessions, verificationTokens } from "@/lib/schema";
import bcrypt from "bcryptjs";

export const { handlers, signIn, signOut, auth } = NextAuth({
  adapter: DrizzleAdapter(db, {
    usersTable: users,
    accountsTable: accounts,
    sessionsTable: sessions,
    verificationTokensTable: verificationTokens,
  }),
  session: { strategy: "database" },   // DBセッション(JWT不使用)
  providers: [
    GitHub({
      clientId: process.env.AUTH_GITHUB_ID!,
      clientSecret: process.env.AUTH_GITHUB_SECRET!,
    }),
    Google({
      clientId: process.env.AUTH_GOOGLE_ID!,
      clientSecret: process.env.AUTH_GOOGLE_SECRET!,
    }),
    Credentials({
      credentials: {
        email: { label: "Email", type: "email" },
        password: { label: "Password", type: "password" },
      },
      authorize: async (credentials) => {
        if (!credentials?.email || !credentials?.password) return null;
        const user = await db.query.users.findFirst({
          where: (u, { eq }) => eq(u.email, credentials.email as string),
        });
        if (!user || !user.passwordHash) return null;
        const valid = await bcrypt.compare(credentials.password as string, user.passwordHash);
        if (!valid) return null;
        return { id: user.id, email: user.email, name: user.name, role: user.role };
      },
    }),
  ],
  callbacks: {
    session: ({ session, user }) => ({
      ...session,
      user: { ...session.user, id: user.id, role: user.role },
    }),
  },
  pages: {
    signIn: "/login",
    error: "/login?error=true",
  },
});

// app/api/auth/[...nextauth]/route.ts
export { GET, POST } from "@/auth";
export const runtime = "nodejs";
// middleware.ts - Route Protection(Middlewareで認証チェック)
import { auth } from "@/auth";
import { NextResponse } from "next/server";

export default auth((req) => {
  const { pathname } = req.nextUrl;
  const isLoggedIn = Boolean(req.auth?.user);

  // 保護されたルート
  const protectedRoutes = ["/dashboard", "/settings", "/admin"];
  const isProtected = protectedRoutes.some((r) => pathname.startsWith(r));

  if (isProtected && !isLoggedIn) {
    const loginUrl = new URL("/login", req.url);
    loginUrl.searchParams.set("callbackUrl", pathname);
    return NextResponse.redirect(loginUrl);
  }

  // 管理者専用ルート
  if (pathname.startsWith("/admin") && req.auth?.user?.role !== "admin") {
    return NextResponse.redirect(new URL("/403", req.url));
  }

  return NextResponse.next();
});

export const config = {
  matcher: ["/((?!api|_next|.*\..*).*)"],
};
// Server Componentでセッションを取得
import { auth } from "@/auth";

export default async function DashboardPage() {
  const session = await auth();
  if (!session) redirect("/login");

  return (
    <div>
      <h1>ようこそ、{session.user.name}さん</h1>
      <p>Role: {session.user.role}</p>
    </div>
  );
}

// Client Componentでセッションを取得
"use client";
import { useSession } from "next-auth/react";

export function UserButton() {
  const { data: session, status } = useSession();
  if (status === "loading") return <Skeleton />;
  if (!session) return <SignInButton />;
  return <Avatar user={session.user} />;
}

Better Auth

2024年に公開されたTypeScript製のOSSフル機能認証ライブラリです。GitHubスター10k+。Auth.jsよりもTypeScript型安全性が高く、メール/パスワード・OAuthプロバイダー・MFA(TOTP)・パスキー・組織/チームの多テナント・APIキー管理をセルフホストで実現。Clerk代替として機能が最も揃っています。

// auth.ts - Better Auth設定
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { twoFactor } from "better-auth/plugins";
import { organization } from "better-auth/plugins";
import { apiKey } from "better-auth/plugins";
import { db } from "@/lib/db";

export const auth = betterAuth({
  database: drizzleAdapter(db, { provider: "pg" }),
  emailAndPassword: {
    enabled: true,
    minPasswordLength: 12,
    requireEmailVerification: true,
  },
  socialProviders: {
    github: { clientId: process.env.GITHUB_CLIENT_ID!, clientSecret: process.env.GITHUB_CLIENT_SECRET! },
    google: { clientId: process.env.GOOGLE_CLIENT_ID!, clientSecret: process.env.GOOGLE_CLIENT_SECRET! },
  },
  plugins: [
    twoFactor(),           // TOTP/2FAを有効化
    organization(),        // チーム・組織機能
    apiKey(),              // APIキー発行機能
  ],
  session: {
    cookieCache: { enabled: true, maxAge: 5 * 60 },
  },
});

// auth-client.ts - クライアントサイドのSDK
import { createAuthClient } from "better-auth/react";
import { twoFactorClient } from "better-auth/client/plugins";
import { organizationClient } from "better-auth/client/plugins";

export const authClient = createAuthClient({
  baseURL: process.env.NEXT_PUBLIC_APP_URL,
  plugins: [twoFactorClient(), organizationClient()],
});

export const { signIn, signUp, signOut, useSession } = authClient;

機能比較表

比較項目Auth.js v5LuciaBetter Auth
OAuthプロバイダー70+手動設定主要10+
メール/パスワード
MFA/TOTP❌(別途実装)
組織/チーム
APIキー管理
Edge Runtime
GitHub Stars25k+5k+10k+

認証後のユーザーデータをSupabaseで管理するにはDevOpsカテゴリ/categories/devopsのSupabase RLSと組み合わせてください。ログイン通知・異常検知アラートはCommunicationカテゴリ/categories/communicationのSlack Webhookと連携できます。

FAQ

Q. Auth.js v5(NextAuth.js)とClerkの違いは何ですか?

A. Clerk: クラウドサービス(ユーザーデータがClerkのサーバーに保存される)・UIコンポーネント付き(<SignIn>等すぐ使える)・無料枠10,000MAUまで・その後月$25〜。Auth.js: OSSライブラリ(ユーザーデータは自分のDBに保存)・UIは自分で作る必要がある・追加コストゼロ(インフラコストのみ)。GDPRやデータ主権の観点から「ユーザーデータを外部に出したくない」場合はAuth.jsの一択。開発速度を優先するスタートアップ初期はClerk、スケールして月$200+になった段階でAuth.jsに移行するパターンが多いです。

Q. Auth.jsでメールアドレス確認(Email Verification)を実装するには?

A. Resend(OSSメール送信SDK)またはNodemailerと組み合わせて実装できます。Auth.js v5ではsendVerificationRequestコールバックでカスタムメール送信処理を定義します。

import Resend from "next-auth/providers/resend";

export const { handlers, auth, signIn, signOut } = NextAuth({
  providers: [
    Resend({
      apiKey: process.env.AUTH_RESEND_KEY,
      from: "noreply@yourcompany.com",
    }),
  ],
});
// /login でメールアドレスを入力するとMagic Linkを送信(パスワードレス)

Q. Better AuthのTOTP(2FA)をNext.jsに実装するには?

A. Better AuthのtwoFactorプラグインが最も簡単です。

// TOTPを有効化
const qrCode = await authClient.twoFactor.getTotpUri();
// QRコードをGoogle Authenticatorで読み取ってもらう

// TOTP検証
await authClient.twoFactor.verifyTotp({ code: "123456" });

Q. JWTセッションとDBセッションどちらを使うべきですか?

A. JWTセッション(stateless): ①全サーバーインスタンスがDBなしでトークンを検証できる(水平スケールが楽)②ログアウト時に即座に無効化できない(TTLが切れるまで有効)③ペイロードにユーザー情報を含めるため若干大きい。DBセッション(stateful): ①サーバーがDBにセッションを照会する(追加DBアクセスが発生)②ログアウト・強制無効化が即座に効く③セキュリティ要件が高い場合(医療・金融・社内ツール)に推奨。B2Cの一般向けWebサービスはJWT・B2Bやセキュリティ要件が高いサービスはDBセッションが一般的です。

まとめ

ユースケース推奨ツール
Next.js社会ログイン・シンプル認証Auth.js v5
軽量・フレームワーク非依存・カスタム認証Lucia
Clerk代替・MFA・組織・APIキー全部入りBetter Auth

関連外部リソース

他の記事も読む

Let's Build Together

OSS導入、自社だけで悩まない。

ツール選定から構築・運用・AI活用まで、オープンソースラボ運営元のClasslessが伴走します。初回のご相談は無料です。