Auth.js(オースジェイエス)は、Webアプリにログイン機能を組み込むためのJavaScript(TypeScript)のオープンソース認証ライブラリです。前身はNext.js専用の「NextAuth.js」で、v5からNext.js以外のフレームワークにも対応する形でAuth.jsへ改称されました。この記事では、名前とパッケージの関係、v4とv5の違い、Next.js 16での導入手順、4つの認証方式の要件を、2026年10月時点の公式ドキュメントとnpmの公開情報で整理します。Credentialsによるパスワードログイン、ロールでの画面とAPIの保護、社内ドメインのアカウントだけを通す設定はコード付きで示し、Next.js 16.4.2とnext-auth 5.0.0-beta.32の検証環境でビルドと起動まで確かめました。最後に、2025年9月にBetter Authチームへ保守が移った後の状況から、今から採用してよいかを判断できるようにします。
まとめ:Auth.jsの要点(2026年10月時点)
- Auth.jsはNextAuth.jsの新しい名前です。Next.js向けのパッケージ名は今も
next-authで、SvelteKitやExpress向けは@auth/sveltekitなどの@auth/*パッケージになります。 - npmの安定版(latest)はv4系の
4.24.15、v5は5.0.0-beta.32です。v5は2023年10月の最初のベータから正式版になっておらず、どちらも2026年7月20日を最後に新しい版は出ていません(10月19日時点)。 - v5ではルートの
auth.tsが返すauth()1つでServer Components・Route Handlers・Server Actionsのセッションを取得します。Next.js 16ではmiddleware.tsがproxy.tsに改名され、公式手順もexport { auth as proxy }に変わりました。 - Next.js 16.4.2で動かしたところ、記事のコードはビルド・ログイン・401/403の出し分けまで動きました。ただし、セッション型の拡張は
next-auth/jwtをimportしないとTS2322で型チェックが落ちます。next startで自前のサーバーに置く場合は、AUTH_TRUST_HOST=trueなどでホストを信頼する設定をしないと500になります。 - パスワードログインはCredentialsプロバイダで書けますが、ハッシュ化・保存・レート制限は自前です。ロールは
jwt・sessionコールバックでセッションへ載せ、ページとAPIの両方で確かめます。 - 2025年9月22日からBetter Authチームが保守しています。既存プロジェクトは使い続けられますが、新規はBetter Authが強く推奨されており、リポジトリへのコミットも2026年7月22日以降ありません。
next-auth4.24.15/5.0.0-beta.32、@auth/core0.41.3 未満なら脆弱性の修正のため更新が必要です。
Auth.jsとは:NextAuth.jsから改称した認証ライブラリの正体
Auth.jsの読み方と「Auth」が指す認証・認可の役割の違い
Auth.jsは「オースジェイエス」と読みます。「Auth」はauthentication(認証=本人確認)の略で、ログインしている人が誰かを確かめるための処理です。何を許可するかを決めるauthorization(認可)とは区別されます。Auth.jsが担うのは主に前者で、ログイン・ログアウト・セッション維持と、その結果を使ったアクセス制御の土台です。認可の仕組みとして混同されやすいOAuthについてはOAuth 2.0とは?仕組み・認可フローと認証との違い、OAuth 2.1の変更点をわかりやすく解説で整理しています。
もとはNext.js専用の「NextAuth.js」として広まり、v5の開発でフレームワーク非依存の中核 @auth/core を切り出したことから、プロジェクト名がAuth.jsになりました。GitHubのリポジトリは今も nextauthjs/next-auth で、スター数は約2.8万(2026年10月時点)、ライセンスはISCです。ライブラリ自体は無料で、費用が発生するのはDBやメール送信サービスなど組み合わせる側です。
パッケージ構成:next-authと@auth/core・@auth/*の関係
「auth.js npm」で探すと複数のパッケージが見つかります。まずフレームワーク用パッケージを選び、認証方式に応じてDBアダプタを足します。
| 用途 | パッケージ | 最新版(2026年10月) |
|---|---|---|
| Next.js(v4安定版) | next-auth | 4.24.15 |
| Next.js(v5) | next-auth@beta | 5.0.0-beta.32 |
| SvelteKit | @auth/sveltekit | 1.11.3 |
| Express | @auth/express | 0.12.3 |
| 中核ロジック | @auth/core | 0.41.3 |
| Prismaアダプタ | @auth/prisma-adapter | 2.11.3 |
| Drizzleアダプタ | @auth/drizzle-adapter | 1.11.3 |
公式の統合一覧では、SvelteKit・Express・SolidStart向けは「Experimental Release」の扱いです。@auth/core は各フレームワーク用パッケージの依存として入るもので(next-auth 5.0.0-beta.32は @auth/core 0.41.3に固定)、公式ドキュメントも「利用者が直接触る必要はない」としています。エラーのスタックトレースに「@auth/core」が出たときも、まず利用中のフレームワーク用パッケージの版を確認します。SvelteKitでの使い方はSvelteKitとは?Svelteとの違い・始め方とv3の変更点で扱っています。
NextAuth.js v4とAuth.js v5の違い:設定場所とauth()の変更点
v5は設定の置き場所とセッションの取り出し方が変わる破壊的変更です。公式の移行ガイド(Migrating to v5)に載っている差分を表にまとめます。
| 項目 | v4(NextAuth.js) | v5(Auth.js) |
|---|---|---|
| 設定ファイル | APIルート内の設定(下記参照) | ルートの auth.ts |
| サーバーでのセッション取得 | getServerSession(引数は下記) | auth() |
| 環境変数の接頭辞 | NEXTAUTH_ | AUTH_ |
| Cookie名の接頭辞 | next-auth | authjs |
| DBアダプタ | @next-auth/*-adapter | @auth/*-adapter |
| 設定の型 | NextAuthOptions | NextAuthConfig |
| 対応Next.js(peer依存) | 12.2.5以降(16まで) | 14.0以降(16まで) |
表のv4の設定ファイルは pages/api/auth/[...nextauth].ts、サーバーでのセッション取得は getServerSession(authOptions) です。実務で効くのは auth() への一本化で、v4で場面ごとに使い分けていた getServerSession・getSession・withAuth・getToken が、v5では auth.ts が返す auth 関数1つにまとまりました。Server Componentでは auth()、getServerSideProps では auth(ctx)、Pages RouterのAPI Routeでは auth(req, res) と呼び方だけが変わり、Route Handlerでは auth でハンドラを包む書き方も選べます。AUTH_URL は多くの場合リクエストヘッダから自動判定されますが、その代わりにホストを信頼する設定が必要になる場面があり、後述の実測で詳しく扱います。App Routerのファイル規約はApp Routerとは?Next.jsのファイル規約・動的ルーティング・Pages Routerとの違いを参照してください。
4つの認証方式:OAuth・Email・Credentials・パスキーの要件
Auth.jsの認証方式は、外部サービスに任せるOAuth/OpenID Connect、メールのリンクでログインするEmail、自前のID・パスワードで照合するCredentials、パスキーを使うWebAuthnの4系統です。方式によってデータベースの要否とセッション方式の制約が違います。
| 方式 | DB | セッション | 状態 |
|---|---|---|---|
| OAuth/OIDC | 任意 | JWT/DB | 安定 |
| Email(マジックリンク) | 必須 | JWT/DB | 安定 |
| Credentials | 任意(自前) | JWTのみ | 安定 |
| WebAuthn(パスキー) | 必須(対応アダプタ) | JWT/DB | 実験的 |
OAuth・OpenID Connect:組み込みプロバイダと環境変数の自動推論
Google・GitHub・LINE・Microsoft Entra ID・Keycloak・Auth0など、組み込みプロバイダは100前後あります(packages/core/src/providers の定義ファイル数による)。v5では環境変数を AUTH_{プロバイダ名}_ID と AUTH_{プロバイダ名}_SECRET の形で置くと自動で読み込まれるため、providers: [GitHub] のようにプロバイダ関数を並べるだけで済みます。プロバイダ側でのOAuthアプリとコールバックURLの登録は別途必要です。OpenID Connectの仕組みとIDトークンの検証はOIDC(OpenID Connect)とは?IDトークンの検証手順とOAuth 2.0との違いを解説で詳しく説明しています。
Email(マジックリンク)の要件:検証トークンを置くデータベース
メールアドレスに届いたリンクを踏むとログインできる方式で、送信にはResend・Nodemailer・SendGrid・Postmarkなどのプロバイダを使います。検証用トークンを保存する必要があるため、JWTセッションを選んだ場合でもデータベースアダプタの設定が必須です。公式ドキュメントも「データベースなしでEmailプロバイダを有効にすることはできない」と明記しています。
Credentials:JWTセッション限定でパスワードの管理は自前
既存の会員DBや社内の認証APIに、フォームで受け取ったID・パスワードを渡して照合する方式です。Auth.jsはパスワードのハッシュ化も保存もしません。ソルト付きハッシュの生成は会員登録やパスワード変更の処理で、ログイン時の照合は authorize 関数から呼ぶ処理で、それぞれ自分で書きます。Credentialsだけを使う構成でセッション方式を "database" にすると「Signing in with credentials only supported if JWT strategy is enabled」というエラーで止まります(@auth/core 0.41.3の lib/utils/assert.js にある文言)。既存の資産をつなぐ用途に限り、新規に作るならOAuthかパスキーを優先するのが無難です。コードは後述の「Credentialsでメールとパスワードのログインを実装する」で示します。
WebAuthn(パスキー):本番利用は推奨されない実験的な機能
パスキー用の Passkey プロバイダはありますが、公式ドキュメントは「実験的で本番利用は推奨しない」と警告しています。Passkeyプロバイダのページが挙げる最低バージョンは next-auth 5.0.0-beta.17、アダプタは @auth/prisma-adapter 1.3.3・@auth/unstorage-adapter 2.1.0・@auth/drizzle-adapter 1.1.1 で、Authenticator テーブルの追加と、設定の experimental: { enableWebAuthn: true } が要ります。本番でパスキーを必須にしたいなら、Auth.jsより対応が進んだ認証基盤を選ぶほうが確実です。パスキーの仕組み自体はFIDO認証とは?仕組み・FIDO2とパスキーの違い・証券のスマホ認証と弱点を解説を、登録と認証のフローをRP側で実装する手順はWebAuthnとは?パスキー認証を実装する仕組みと登録・認証フロー・RP実装の要点を参照してください。
Next.js 16にAuth.js v5を入れてGitHubログインを動かす手順
GitHubログインを例に、公式のInstallationページの手順で進めます。以下は、ルート直下にappディレクトリがあり、インポートエイリアス @/* がプロジェクトルートを指すNext.jsのTypeScriptプロジェクトが前提です。Next.js 14・15でも手順は同じで、違うのは手順4のファイル名だけです。Next.js自体のプロジェクト作成から始める場合はNext.jsとは?16系App Routerで最小アプリを動かす手順と採用判断で雛形を用意してから戻ってきてください。
手順1:next-auth@betaのインストールとAUTH_SECRETの生成
npm install next-auth@beta
npx auth secret
npx auth secret は乱数のシークレットを生成し、Next.jsの場合は .env.local に AUTH_SECRET として書き込みます。この値はセッショントークンの暗号化や、メール検証トークンのハッシュ生成に使われるため、環境ごとに別の値を使います。
手順2:GitHubのOAuthアプリ登録とコールバックURL・環境変数
GitHubのDeveloper settingsでOAuth Appを作り、Authorization callback URLに http://localhost:3000/api/auth/callback/github を登録します。発行されたClient IDとClient Secretを次の名前で置けば、コード側で読み込む処理は要りません。
AUTH_SECRET=(npx auth secret が書き込んだ値)
AUTH_GITHUB_ID=(Client ID)
AUTH_GITHUB_SECRET=(Client Secret)
手順3:ルートのauth.tsとapi/authのRoute Handlerの作成
// auth.ts(プロジェクトのルート)
import NextAuth from "next-auth"
import GitHub from "next-auth/providers/github"
export const { handlers, signIn, signOut, auth } = NextAuth({
providers: [GitHub],
})
// app/api/auth/[...nextauth]/route.ts
import { handlers } from "@/auth"
export const { GET, POST } = handlers
Route HandlerはApp Routerに置く必要がありますが、アプリの他の部分は pages/ のままでも動きます。
手順4:proxy.ts(Next.js 15以前はmiddleware.ts)の配置
// proxy.ts(Next.js 16)
export { auth as proxy } from "@/auth"
// middleware.ts(Next.js 15以前)
// export { auth as middleware } from "@/auth"
公式ドキュメント上は任意の手順で、アクセスのたびにセッションの有効期限を延ばす役割です。ファイル名の変更はNext.js側の仕様で、Next.jsのproxy.jsリファレンスに移行方法が載っています。ここで認可まで済ませる構成にすると、proxy(middleware)を迂回されたときにページが無防備になります。2025年3月に公開されたNext.jsのmiddleware迂回の脆弱性(CVE-2025-29927とは?Next.jsミドルウェア認可バイパスの影響範囲・修正版・検知方法)が実例で、保護したいページやServer Actionの中でも auth() を呼んで確かめる二重化が安全です。
手順5:ページでのセッション取得とログイン・ログアウトボタン
// app/page.tsx
import { auth, signIn, signOut } from "@/auth"
export default async function Page() {
const session = await auth()
if (!session?.user) {
return (
<form action={async () => { "use server"; await signIn("github") }}>
<button type="submit">GitHubでログイン</button>
</form>
)
}
return (
<form action={async () => { "use server"; await signOut() }}>
<p>{session.user.name} でログイン中</p>
<button type="submit">ログアウト</button>
</form>
)
}
npm run dev で起動して / を開き、GitHubの同意画面を経てユーザー名が表示されれば導入完了です。サーバー側でセッションを取るだけなら SessionProvider は要りません。クライアントコンポーネントで useSession を使う場合だけ、SessionProvider で包みます。
Credentialsでメールとパスワードのログインを実装するコード例
社内システムや既存の会員DBをそのまま使いたい場合は、Credentialsプロバイダで自前の照合処理をつなぎます。公式のCredentialsページは、authorize がユーザーオブジェクトを返せばログイン成功、null を返せば失敗という約束と、入力値をサーバー側で必ず検証することを求めています。ここではZod 4系で入力を検証し、Node.js標準の scrypt でハッシュを照合します。
入力検証のZodスキーマとscryptによるパスワード照合関数
// lib/zod.ts
import { z } from "zod"
export const signInSchema = z.object({
email: z.email(),
password: z.string().min(8).max(32),
})
// lib/user.ts(保存形式は "salt:hash" の16進文字列を想定)
import { scrypt, timingSafeEqual } from "node:crypto"
import { promisify } from "node:util"
import { prisma } from "@/prisma"
const scryptAsync = promisify(scrypt)
export async function verifyUser(email: string, password: string) {
const row = await prisma.user.findUnique({ where: { email } })
if (!row?.passwordHash) return null
const [salt, hash] = row.passwordHash.split(":")
const derived = (await scryptAsync(password, salt, 64)) as Buffer
const stored = Buffer.from(hash, "hex")
if (derived.length !== stored.length || !timingSafeEqual(derived, stored)) return null
return { id: row.id, email: row.email, name: row.name, role: row.role }
}
timingSafeEqual は比較にかかる時間からハッシュを推測されないための関数で、長さが違うと例外を投げるため先に長さを確かめます。passwordHash と role は自前で追加する列で、Auth.jsの標準スキーマには含まれません。Zodの書き方とZod 4での変更点はZodの使い方入門|npmでのインストールとz.infer・safeParse・Zod 4.6の変更点にまとめています。
// auth.ts
import NextAuth from "next-auth"
import Credentials from "next-auth/providers/credentials"
import { ZodError } from "zod"
import { signInSchema } from "@/lib/zod"
import { verifyUser } from "@/lib/user"
export const { handlers, signIn, signOut, auth } = NextAuth({
providers: [
Credentials({
credentials: { email: {}, password: {} },
authorize: async (credentials) => {
try {
const { email, password } = await signInSchema.parseAsync(credentials)
return await verifyUser(email, password)
} catch (e) {
if (e instanceof ZodError) return null
throw e
}
},
}),
],
})
// app/login/actions.ts
"use server"
import { AuthError } from "next-auth"
import { signIn } from "@/auth"
export async function login(_prev: { error?: string } | undefined, formData: FormData) {
try {
formData.set("redirectTo", "/") // 第3引数に渡すとOAuthの追加パラメータ扱いになる
await signIn("credentials", formData)
} catch (e) {
if (e instanceof AuthError) return { error: e.type }
throw e
}
}
公式ドキュメントによれば、Server Actionから呼ぶとログイン失敗はリダイレクトではなく CredentialsSignin の例外として投げられるため、上のように自分で捕捉します。ログイン後の移動先は redirectTo で指定しますが、next-auth 5.0.0-beta.32の signIn が読むのは第2引数(FormDataかオブジェクト)の中の redirectTo だけです。第3引数はプロバイダへ渡す認可リクエストのパラメータなので、そこに書くと移動先が効かず、フォームを送ったページ(Referer)へ戻ります。成功時の signIn もリダイレクトのための例外を投げるので、AuthError 以外は必ず投げ直します。クライアント側では useActionState(login, undefined) で受け、state?.error があれば「メールアドレスかパスワードが違います」と表示します。どちらが違うのかを区別しないのは、登録済みアドレスの探索を防ぐためです。ログイン試行回数の制限やパスワードリセットも、Credentialsでは自前で用意します。
ロールと所属ドメインでアクセスを絞る:jwt・session・signInコールバック
管理画面だけを管理者に開く、社員のアカウントだけをログインさせる、といった制御はコールバックで書きます。公式のロールベースアクセス制御ガイドの構成に沿って、JWTセッションの場合のコードを示します。
jwt・sessionコールバックでroleをセッションへ載せる設定と型拡張
// auth.ts(NextAuth({...}) の中に追加)
callbacks: {
jwt({ token, user }) {
if (user) token.role = user.role // サインイン直後だけ user が渡る
return token
},
session({ session, token }) {
session.user.role = token.role
return session
},
authorized({ auth, request: { nextUrl } }) {
if (nextUrl.pathname.startsWith("/admin")) return auth?.user?.role === "admin"
return true
},
},
// types/next-auth.d.ts(tsconfig.json の include に入る場所ならどこでもよい)
import { JWT } from "next-auth/jwt" // この1行が無いと JWT の拡張が効かない
import type { DefaultSession } from "next-auth"
declare module "next-auth" {
interface User { role?: string }
interface Session { user: { role?: string } & DefaultSession["user"] }
}
declare module "next-auth/jwt" {
interface JWT { role?: string }
}
型の拡張は公式のTypeScriptページの書き方で、DefaultSession["user"] と交差させて既定の項目を消さないようにします。1行目の import { JWT } from "next-auth/jwt" は使っていないように見えますが、外すと session.user.role = token.role の行が「Type ‘unknown’ is not assignable to type ‘string | undefined’.」(TS2322)で落ちます。next-auth/jwt は @auth/core/jwt を再エクスポートしているだけのため、importで読み込ませないと拡張が合流しません(後述の検証環境で確認)。
authorized は proxy.ts から呼ばれ、false を返すとサインインページへ送られます。JWTセッションではロールを変えても、本人が再サインインするまでトークン内の値は古いままです。権限の剥奪をすぐ効かせたい場合、Credentialsを使う構成はJWTセッションのままにするしかないため、jwt コールバックでDBのロールを読み直します(リクエストごとに問い合わせが増えます)。OAuthやEmailだけの構成なら、後述のデータベースセッションにしてセッションコールバックで user.role を読む方法も選べます。トークンに何を入れてよいかの判断はJWTとは?構造・署名検証の仕組みとセッション・OAuth/OIDCとの違いを実装視点で解説が参考になります。
Route Handlerでの認可判定とproxy.tsのmatcherによる保護範囲
// app/api/admin/route.ts
import { NextResponse } from "next/server"
import { auth } from "@/auth"
export const GET = auth(function GET(req) {
if (!req.auth) return NextResponse.json({ message: "Not authenticated" }, { status: 401 })
if (req.auth.user?.role !== "admin") return NextResponse.json({ message: "Forbidden" }, { status: 403 })
return NextResponse.json({ ok: true })
})
// proxy.ts(API と静的ファイルを対象から外す)
export { auth as proxy } from "@/auth"
export const config = {
matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
}
公式のProtecting Resourcesページの例をロール判定つきに広げたものです。matcherで api を除外しているため、APIの保護はRoute Handler側の判定だけが担います。未ログインは401、ログイン済みで権限が無い場合は403と分けておくと、フロント側でログイン画面へ送るか権限エラーを出すかを決められます。公式も「proxyだけで認可せず、データを取得する場所の近くでセッションを確かめる」よう注意しており、Server ComponentやServer Actionの先頭でも auth() の結果を確認します。ページ側の判定は次のように、保護したいデータを取得する前に置きます。
// app/admin/page.tsx
import { redirect } from "next/navigation"
import { auth } from "@/auth"
export default async function AdminPage() {
const session = await auth()
if (session?.user?.role !== "admin") redirect("/")
return <p>管理者だけが見られる画面</p>
}
Server Actionも外から直接呼び出せる関数なので、同じ判定を関数の先頭に書きます。
社内ドメインのGoogleアカウントだけをログインさせるsignInコールバック
社内ツールで「自社のGoogle Workspaceのアカウントだけ通したい」場合は、ログインのたびに呼ばれる signIn コールバックで弾きます。公式のアクセス制限ガイドとGoogleプロバイダのページに例がありますが、載っている profile.email.endsWith(...) の書き方は account・profile が省略可能な型のため、strict を有効にしたプロジェクトではTS2322で型チェックを通りません。null安全に書き直し、Google Workspaceの組織判定に変えると次のようになります。
// auth.ts(callbacks に追加。example.co.jp のアカウントだけを通す)
async signIn({ account, profile }) {
if (account?.provider === "google") {
return profile?.email_verified === true && profile.hd === "example.co.jp"
}
return false // Google以外は拒否。併用するなら、そのプロバイダで所属を確かめてから許可する
},
判定に使うのはメールアドレスの末尾ではなく、IDトークンの hd クレームです。GoogleのOpenID Connectのドキュメントは、特定ドメインの組織メンバーだけに絞るときは hd の値を検証するよう求めており、個人のGoogleアカウントには hd が付きません。false を返すと AccessDenied エラーとしてログインが止まり、URLの文字列を返すとそのURLへリダイレクトされます(@auth/core の lib/actions/callback/index.js の処理)。Google以外のプロバイダやCredentialsを同じ設定に並べている場合、最後の return false はそれらのログインも拒否するので、プロバイダごとの許可条件を足してください。データベースセッションの場合は、ここで弾けばユーザーの登録そのものも行われません。
Prisma Adapterによるユーザーとセッションのデータベース保存
Emailやパスキーを使う場合、またはセッションを即時に失効させたい場合はDBアダプタを設定します。公式のPrisma Adapterページの手順は次のとおりです。
npm install @prisma/client @prisma/extension-accelerate @auth/prisma-adapter
npm install prisma --save-dev
// auth.ts
import NextAuth from "next-auth"
import { PrismaAdapter } from "@auth/prisma-adapter"
import { prisma } from "@/prisma"
export const { handlers, auth, signIn, signOut } = NextAuth({
adapter: PrismaAdapter(prisma),
providers: [],
})
prisma.ts では、開発サーバーのホットリロードで接続が増え続けないよう、PrismaClientを globalThis に1つだけ保持する書き方が公式に載っています。Prisma Postgres(Accelerate)を使わない場合は withAccelerate() の拡張を外して構いません。スキーマは公式ページの User・Account・Session・VerificationToken の4モデル(パスキーを使うなら Authenticator も)を schema.prisma に貼り、npx prisma migrate dev でテーブルを作ります。ロールをDBで持つなら User に role String @default("user") を足し、セッションコールバックで user.role を返します。Prismaは7系でクライアントの生成先とDB接続の指定方法が変わっているため、手元の版に合わせた書き方はPrismaのバージョン一覧|最新は7.10.1、Prisma 8はRC(2026年10月)確認方法と7への固定で確認してください。
セッション戦略:JWTセッションとデータベースセッションの選び方
アダプタを設定していなければJWTセッション、設定していればデータベースセッションが既定になります。どちらもセッション情報はHttpOnly Cookieで運ばれます。
- JWTセッション:トークンは署名だけでなく暗号化(JWE、
A256CBC-HS512)され、既定の有効期限は30日です。DBが要らない反面、ログアウト後もトークンを持っていれば期限まで有効で、途中で失効させるにはブロックリストを自前で持つ必要があります。Cookieが約4KBを超えると分割して送るため、トークンに多くの情報を詰めるとリクエストが重くなります。 - データベースセッション:Cookieにはセッションidだけを入れ、中身はDBに置きます。管理画面から特定ユーザーを即時ログアウトさせたい、権限変更をすぐ反映したいといった要件があるならこちらです。
DBアダプタを使うときはランタイムに注意します。Next.js 16の proxy.ts はNode.jsランタイムで動くため、Edge非対応のDBクライアントが読み込めないという制約はありません。一方で、Next.js 15以前の middleware.ts は既定でEdgeランタイムで動き、Edge非対応のDBクライアントを読み込むとエラーになります。その場合、公式の移行ガイドはアダプタを含まない auth.config.ts と、アダプタを足す auth.ts に設定を分け、middleware側では前者だけを読み込み、セッション方式を "jwt" に固定する構成を紹介しています。JWTとBearerトークンの関係はBearer Token(ベアラートークン)とは?読み方・Authorization: Bearerの書き方とJWTとの違いで説明しています。
Auth.jsでよく出るエラーの原因と対処:MissingSecretなど6種
導入直後に止まりやすいエラーを、公式のエラーリファレンスの説明に沿って整理します。原因の切り分けには、設定に debug: true を足してサーバーログの [auth][debug] 行を見るのが近道です。
| エラー名 | 主な原因 | 対処 |
|---|---|---|
| MissingSecret | AUTH_SECRETが未設定 | npx auth secretで生成 |
| UntrustedHost | 本番起動でホスト信頼の設定が無い | AUTH_TRUST_HOST=true |
| CredentialsSignin | authorizeがnullを返した | Server Actionで捕捉 |
| AccessDenied | signInコールバックがfalse | 許可条件を見直す |
| AdapterError | アダプタのDB操作が失敗 | debug: trueで原因確認 |
| OAuthCallbackError | プロバイダがエラーを返した | コールバックURLを照合 |
本番だけで出る代表は MissingSecret と UntrustedHost です。ローカルの .env.local に値があっても、ホスティング側の環境変数に AUTH_SECRET を登録し忘れると、認証のエンドポイントが500を返します。UntrustedHost が出る条件は次章の実測で確かめました。OAuthCallbackError は、ユーザーが同意画面で拒否した場合のほか、プロバイダに登録したコールバックURLと実際のURL(ポート番号や末尾のパス)が一致しない設定ミスで起きます。
Next.js 16.4.2とnext-auth 5.0.0-beta.32で記事のコードを動かした結果
この記事のコード(GitHub+Credentials、ロールのコールバック、proxy.ts、/api/admin)を1つのプロジェクトにまとめ、2026年10月19日に次の環境で next build と next start を実行しました。DBの代わりにメモリ上の2ユーザー(admin・一般)を置き、curlで /api/auth/callback/credentials へ直接POSTしてログインし、ページとAPIへのアクセスを確かめています。Server Action経由のログイン、GitHub・Googleとの実際のOAuthのやり取りは、この検証の範囲外です。
| 項目 | 版 |
|---|---|
| Node.js | 26.5.0 |
| next | 16.4.2(Turbopack) |
| next-auth | 5.0.0-beta.32(@auth/core 0.41.3) |
| react | 19.3.1 |
| zod | 4.6.7 |
ビルドと型チェック:型拡張はnext-auth/jwtのimportが無いとTS2322
型拡張ファイルから import { JWT } from "next-auth/jwt" を外した状態では、next build の「Running TypeScript」で error TS2322: Type 'unknown' is not assignable to type 'string | undefined'. が出て失敗しました。importを戻すと型チェックを通り、/api/auth/[...nextauth]・/api/admin と「Proxy (Middleware)」を含むビルドが完了します。declare module "next-auth/jwt" を declare module "@auth/core/jwt" に書き換えても通りますが、公式の書き方に合わせるならimportを残すほうです。
本番起動:next startで必要になるホスト信頼の設定
AUTH_SECRET を設定しても、AUTH_TRUST_HOST が無いまま next start で起動すると、/api/auth/session は500を返し、ログに UntrustedHost: Host must be trusted. が出ました。@auth/core 0.41.3の lib/utils/env.js は、AUTH_URL・AUTH_TRUST_HOST・VERCEL・CF_PAGES のいずれかがあるか、NODE_ENV が production 以外のときだけホストを自動で信頼します。つまり next dev とVercel・Cloudflare Pagesでは不要で、自前のサーバーやコンテナで本番起動するなら、リバースプロキシの有無にかかわらず、AUTH_TRUST_HOST=true・AUTH_URL・設定の trustHost: true のいずれかでホストを信頼させます。公式のリファレンスは、Auth.jsがリクエストのHostヘッダを使ってURLを組み立てるため、デプロイ先がHostヘッダを安全に設定していることを確かめたうえで有効にするよう求めています。リバースプロキシを置くなら、外から来た Host・X-Forwarded-Host をそのまま通さず、プロキシ側で正しい値に上書きします。AUTH_TRUST_HOST=true だけで AUTH_SECRET を外すと、今度は MissingSecret で500になりました。
ログインと認可:401・403・307の出し分けとCookieの中身
| 操作 | 結果 |
|---|---|
| adminで/api/admin | 200 |
| 一般ユーザーで/api/admin | 403 |
| 未ログインで/api/admin | 401 |
| 一般ユーザーで/admin | 307(サインインページへ) |
| 誤パスワード・5文字のパスワード | error=CredentialsSigninへ転送 |
ログインに成功すると authjs.session-token(HttpOnly・SameSite=Lax)が発行され、有効期限はちょうど30日後でした。トークンの先頭部分をデコードすると {"alg":"dir","enc":"A256CBC-HS512",...} で、署名だけのJWTではなく暗号化されたJWEであることが確かめられます。/api/auth/session の応答には "role":"admin" が入り、コールバックでロールを載せた結果がクライアントにも渡ります。CSRF対策のトークンは /api/auth/csrf が返し、同時に authjs.csrf-token Cookieが置かれます。検証はhttpのlocalhostで行ったため接頭辞なしの名前ですが、ソースではhttpsのときセッションが __Secure-authjs.session-token、CSRFが __Host-authjs.csrf-token になります。リバースプロキシのCookie転送設定やE2Eテストで名前を指定するときは、この違いに注意してください。Zodの検証で弾かれた入力(5文字のパスワード)と、照合に失敗した入力は、どちらも同じ CredentialsSignin になり、外からは区別できませんでした。
2026年にAuth.jsを採用すべきか:保守体制と脆弱性の判断材料
採用前には、保守体制、リリース状況、セキュリティ修正の提供状況を確認し、代わりの選択肢と比べます。
保守体制:2025年9月22日からBetter Authチームが担当
Better Authの公式ブログは2025年9月22日、Auth.jsの保守をBetter Authチームが引き継いだと発表しました。既存の利用者は中断なく使い続けられ、セキュリティパッチと緊急の不具合には対応を続けるとしています。一方で「新規プロジェクトは、特定の機能差がない限りBetter Authで始めることを強く推奨する」とも書いており、authjs.devのトップにも「The Auth.js project is now part of Better Auth」と表示されています。発表時に機能差の代表例として挙げられていたデータベース不要のステートレスなセッション管理は、Better Authが2025年11月公開の1.4で対応しました。GitHub APIで見ると、nextauthjs/next-auth への最後のコミットは2026年7月22日(セキュリティ報告窓口の更新)で、10月19日までの約3か月は動きがありません。
Auth.js v5のリリース状況:約3年続くベータ版での提供
npmでは今も latest タグがv4系の4.24.15で、v5は beta タグの5.0.0-beta.32です。最初のベータ(5.0.0-beta.0)は2023年10月24日公開で、そこから約3年正式版が出ていません。authjs.devのドキュメントはv5以降を対象にしており、v4の資料は旧サイトのnext-auth.js.orgに残っています。v4を選ぶなら参照先を旧サイトに揃え、v5を選ぶならベータ版を本番に載せる前提で、更新のたびにリリースノートを確認する運用が要ります。
Auth.jsの脆弱性:移管後に公開された5件と修正版
| 公開日 | ID | 深刻度 | 内容 |
|---|---|---|---|
| 2026-07-20 | CVE-2026-73420 | High | マジックリンクが別の宛先へ届きうる |
| 2026-07-20 | CVE-2026-73418 | High | 不正BearerヘッダでgetToken()が例外 |
| 2026-07-20 | CVE-2026-73419 | Medium | OAuth検査Cookieと発行元の紐づけ欠如 |
| 2026-07-20 | CVE-2026-73421 | Low | 設定エラー時に認証チェックが素通り(v5のみ) |
| 2025-10-27 | GHSA-5jpx-9hw9-2fx4 | Medium | Emailの誤配送 |
2026年7月の4件の修正版は next-auth 4.24.15/5.0.0-beta.32、@auth/core 0.41.3で、構成エラーによる認証チェック迂回(CVE-2026-73421)はv5のみが対象です。表の「OAuth検査Cookie」は、state・nonce・PKCEの検査に使うCookieが発行元のプロバイダに紐づいていない問題を指します。2025年10月の誤配送は next-auth 4.24.12/5.0.0-beta.30で修正済みです。詳細はGitHubのSecurity Advisoriesで確認できます。最も影響が大きいCVE-2026-73420は、Emailプロバイダを既定のアドレス正規化のまま使い、Unicode正規化をかけるメール送信サービスを組み合わせている場合に、攻撃者が被害者のアカウントで使えるログインリンクを自分の受信箱に届けさせられるというものです。修正版の @auth/core 0.41.3では、検証の前にNFKC正規化をかける処理が入っています。Emailプロバイダを使っているなら最優先で更新してください。手元の版は次のコマンドで確かめられます。
npm ls next-auth @auth/core
npm install next-auth@beta # v5 の場合
npm install next-auth@4 # v4 の場合(4.24.15 以上が入る)
Auth.js・Better Auth・Clerkの比較:提供形態とデータ保存先
新規開発で比較されることが多い3つを、2026年10月19日時点の公開情報で並べます。
| 項目 | Auth.js | Better Auth | Clerk |
|---|---|---|---|
| 形態 | OSSライブラリ | OSSライブラリ | マネージドサービス |
| ライセンス | ISC | MIT | 商用サービス |
| 最新版 | 4.24.15/5.0.0-beta.32 | 1.7.10(2026-10-15) | – |
| GitHubスター | 約2.8万 | 約3.0万 | – |
| ユーザー情報の保存先 | 自前のDB(任意) | 自前のDB(DBなしも可) | Clerk側 |
| 開発の状況 | 保守のみ | 活発 | 活発 |
ユーザー情報を自社のDBに置くことが前提ならAuth.jsかBetter Authで、新規なら更新が続いているBetter Authです。ログイン画面・多要素認証・ユーザー管理画面まで外に任せて開発を短くしたいなら、利用者数に応じた料金と引き換えにClerkのようなマネージドサービスが候補に入ります。Better Authの使い方はbetter-authとは?Auth.jsとの違い・使い方とRedis・JWT対応を解説【v1.7】、Clerkの料金と導入手順はClerk認証とは?Next.jsへの導入手順とCore 3の変更点・MRU料金・Auth0との選び分け【2026年10月】で扱っています。
採用判断:既存は更新して継続、新規はBetter Authが第一候補
既にAuth.jsで動いているアプリは、修正版への更新と認証・認可の回帰確認を行えば継続利用を選べます。移行ガイドも「今の構成がうまく動いているなら急いで移行する必要はない」としています。新規開発では、保守チーム自身が推奨するBetter Authを第一候補にするのが合理的です。DB不要のセッションもBetter Authで使えるようになった今、Auth.jsを新規で選ぶ理由は、既存のv4資産や社内の雛形とライブラリを揃えたい場合などに限られます。避けるべきなのは、正式版の無いv5を「そのうち安定版になる」前提で新規の本番系に採用することです。3か月コミットが無い現状では、ベータのまま修正だけが続く状態を前提に計画してください。
ライブラリ選びより先に決めるべきことが残っているなら、そちらを優先します。社内のIdPとのSSO、複数システムでのアカウント統合、パスキー必須化、監査ログの保持期間といった要件は、どのライブラリを選んでも自前で設計する部分です。認証基盤そのものの設計から相談したい場合は、一創の認証基盤・ID管理システム開発で、既存システムの棚卸しから移行計画まで対応しています。
よくある質問
Auth.jsの読み方は?
「オースジェイエス」と読みます。Authはauthentication(認証)の略です。
Auth.jsとNextAuth.jsは別のライブラリですか?
同じプロジェクトです。v5の開発でNext.js以外にも対応したためAuth.jsへ改称しました。Next.js向けのnpmパッケージ名は今も next-auth です。
Auth.jsのCSRFトークンはどこで取得しますか?
/api/auth/csrf にGETするとJSONの csrfToken が返り、同時に authjs.csrf-token Cookie(httpsでは __Host-authjs.csrf-token)が置かれます。フォームから /api/auth/callback/credentials へ直接POSTする場合は、この値を csrfToken として一緒に送ります。Server Actionで signIn() を呼ぶ書き方なら、自分で扱う必要はありません。
Auth.jsでSAML認証(SSO)は使えますか?
SAMLを直接話す機能はありませんが、SAMLをOAuthに変換するBoxyHQ SAML(SAML Jackson)用のプロバイダが組み込まれており、これを経由して企業のIdPとつなげます。Microsoft Entra IDやOktaのようにOIDCで接続できるIdPなら、それぞれの組み込みプロバイダを使うほうが単純です。SAMLとOIDCの使い分けはSAMLとは?認証フロー・IdP/SPの仕組みとOAuth・OIDCとの使い分けを実装視点で解説で比較しています。
今からAuth.jsを使うならv4とv5のどちらですか?
Auth.jsを使うと決めているならv5(next-auth@beta)です。公式ドキュメントがv5前提で書かれており、Next.js 16の proxy.ts に対応した手順もv5にしかありません。ただし新規開発では、先にBetter Authを比較対象に入れてください。