今回やること
Next.js App RouterへClerkを導入し、sign-inしている利用者だけが/dashboardを表示できるようにします。
到達点は次のとおりです。
- Clerkのkeyを環境変数へ保存する
ClerkProviderをroot layoutへ追加するclerkMiddleware()を設定する- dashboardの近くで
auth()を使って保護する
middlewareを通すだけでは、すべてのrouteが自動で非公開になるわけではありません。保護するresourceの近くで認証を確認します。
前提条件
- Node.jsを利用できる
- Next.js App Router projectがある
- Clerk accountとapplicationを作成済み
- Clerk dashboardでpublishable keyとsecret keyを確認できる
この記事では認証までを扱います。
packageをinstallする
Next.js projectで実行します。
npm install @clerk/nextjs
install後はlockfileも更新されます。
環境変数を設定する
project rootの.env.localへkeyを保存します。
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_xxxxxxxxx
CLERK_SECRET_KEY=sk_test_xxxxxxxxx
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEYはbrowserで使う公開keyです。CLERK_SECRET_KEYはserver専用で、client code、screen shot、Gitへ出してはいけません。
通常のNext.js templateでは.env*がignoreされていますが、commit前に確認します。
git check-ignore .env.local
ClerkProviderを追加する
app/layout.tsxでapplication全体をClerkProviderで囲みます。
import { ClerkProvider } from "@clerk/nextjs";
import type { ReactNode } from "react";
export default function RootLayout({
children,
}: Readonly<{ children: ReactNode }>) {
return (
<ClerkProvider>
<html lang="ja">
<body>{children}</body>
</html>
</ClerkProvider>
);
}
既存のmetadata、font、styleは消さず、providerだけを追加します。
clerkMiddlewareを設定する
Next.js 16ではproject rootにproxy.tsを作ります。Next.js 15以下では同じ内容をmiddleware.tsという名前で置きます。
import { clerkMiddleware } from "@clerk/nextjs/server";
export default clerkMiddleware();
export const config = {
matcher: [
"/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)",
"/(api|trpc)(.*)",
"/__clerk/(.*)",
],
};
clerkMiddleware()は認証情報をrequestで利用可能にします。この時点ではdashboardは非公開になりません。
古い記事にあるcreateRouteMatcher()はdeprecated warningがあるため、この記事では使いません。
sign-in UIを表示する
home pageへClerkのcomponentを置きます。
import {
Show,
SignInButton,
SignUpButton,
UserButton,
} from "@clerk/nextjs";
export default function HomePage() {
return (
<main>
<Show when="signed-out">
<SignInButton />
<SignUpButton />
</Show>
<Show when="signed-in">
<UserButton />
<a href="/dashboard">Dashboardへ</a>
</Show>
</main>
);
}
表示切替だけではserver resourceを保護できません。次のauth()確認が必要です。
dashboardを保護する
app/dashboard/page.tsxを作ります。
import { auth } from "@clerk/nextjs/server";
export default async function DashboardPage() {
const { isAuthenticated, redirectToSignIn, userId } =
await auth();
if (!isAuthenticated) {
return redirectToSignIn();
}
return (
<main>
<h1>Dashboard</h1>
<p>認証済みuser: {userId}</p>
</main>
);
}
auth()をdashboardの近くに置くと、このpageの条件が分かります。
認証と認可を分ける
Clerkはidentityとsessionを使って「誰か」を認証します。applicationは、認証後にそのuserが対象resourceを操作できるか別途確認します。

起動する
development serverを起動します。
npm run dev
表示されたlocal URLをbrowserで開きます。
成功を確認する
次の順番で確認します。
- sign-out状態でhomeにsign-in buttonが出る
- sign-in後にUserButtonとdashboard linkが出る
- sign-in後は
/dashboardを表示できる - sign-out後に
/dashboardを直接開くとsign-inへ戻る - UIだけでなくdashboard URLを直接開いて保護を確認する
Next.js versionとfile名
Next.js 16以上はproxy.ts、15以下は同じcodeをmiddleware.tsへ置きます。versionは次で確認します。
npm list next
versionに合う一方だけを置きます。
よくあるつまずき
secret keyを公開する
CLERK_SECRET_KEYへNEXT_PUBLIC_を付けません。Gitへ入った場合はClerk dashboardでrotateします。
middlewareだけで保護したと思う
clerkMiddleware()に加え、pageやAPIの近くでauth()を確認します。
UIだけ非表示にする
APIを直接呼べるため、server resourceでもauth()を確認します。userIdの確認は認証であり、data所有者やroleを調べる認可は別に必要です。
練習
/settings pageと/api/settings routeを追加し、sign-in済みuserだけが利用できるようにしてください。
確認基準は次のとおりです。
- pageとAPIの両方で
auth()を使う - sign-out時のpageはsign-inへredirectする
- sign-out時のAPIは401を返す
- secret keyをclientへ渡していない
- 古い
createRouteMatcher()を使っていない
まとめ
ClerkProviderをroot layoutへ置く- Next.js 16は
proxy.ts、15以下はmiddleware.ts clerkMiddleware()でClerkをrequestへ統合する- pageとAPIの近くで
auth()を確認する - 認証と認可を分ける
次のステップ
認証が動いた後に、data所有者、role、organizationなどの認可規則を追加しましょう。
参考リソース
- Clerk Next.js SDK
- Clerk clerkMiddleware reference
- Clerk: Migrate away from Middleware-based auth checks