🚥

Next.js15 からは unauthorized と forbidden で認証・権限のエラーハンドリングをしよう

2024/12/21に公開

認証や権限のエラー処理がシンプルになる

先日、Next.js 15.1 がリリースされました。

Next.js 15.1 では、認証エラーや権限エラーのエラーハンドリングの仕組みとしてunauthorized()forbidden()という 2 つ API が実験的機能として登場しました。

従来、未認証ユーザーのリダイレクトや、権限不足時のエラー処理は、独自の実装で対応する必要がありました。しかし、unauthorized()forbidden() を利用することで、notFound() のように Next.js が用意した標準的なエラーハンドリングとして統一でき、簡潔に記述できるようになります。

従来の認証・権限のエラーハンドリングのように開発者側が独自で実装するということはバグのリスクも高まりますし、他の人が読むときに内容を追わなきゃいけないので、ライブラリ側で標準の形で統一できることは、開発体験が改善される非常に価値のある機能です。

本記事では、この API の基本的な使い方と、実際のユースケースをコード例を交えて紹介します。

https://nextjs.org/docs/app/api-reference/functions/unauthorized

https://nextjs.org/docs/app/api-reference/functions/forbidden

基本的な使い方

unauthorized()forbidden() は v15.1 現在では実験的な機能として提供されているため、next.config.ts で機能を有効にする必要があります。

next.config.ts
const nextConfig = {
  experimental: {
    authInterrupts: true,
  },
};

unauthorized()

まずunauthorized()です。

認証エラーのときに遷移させたい画面をunauthorized.tsxに用意しておき、認証エラー処理を実行したい箇所でunauthorized()を呼ぶだけです。

export default async function DashboardPage() {
  // 例)ログイン情報を取得
  const session = await verifySession();

  if (!session) {
    // 認証エラーを挟みたい箇所でunauthorized()を呼ぶ
    unauthorized();
  }

  return <div>ダッシュボード</div>;
}
unauthorized.tsx
// unauthorized()が呼ばれたときに、表示したい画面を用意する
export default function UnauthorizedPage() {
  return (
    <main>
      <p>ログインしてください</p>
      <Link href="/login">ログインページに進む</Link>
    </main>
  )
}

forbidden()

続いてforbidden()です。

こちらも権限エラーのときに遷移させる画面をforbidden.tsxに用意しておき、権限エラー処理を実行したい箇所でforbidden()を呼んでください。

export default async function AdminPage() {
  // 例)ログイン情報を取得
  const session = await verifySession();

  // 管理者権限かどうかをチェック
  if (session.role !== "admin") {
    // 権限エラーを挟みたい箇所でforbidden()を呼ぶ
    forbidden();
  }

  return <div>管理者ページ</div>;
}
forbidden.tsx
// forbidden()が呼ばれたときに、表示したい画面を用意する
export default function Forbidden() {
  return (
    <div>
      <p>アクセスする権限がありません</p>
      <Link href="/">戻る</Link>
    </div>
  );
}

これまでの NotFound エラーように発生させたいタイミングでnotFound()を実行して、事前に用意していたnot-found.tsxのエラー画面に飛ばすイメージと同じ形で使えそうです。

ServerAction や Middleware でも柔軟に使える

unauthorized()forbidden() は、MiddlewareServer Action, Route Handlersなどでも実行できるので、処理を共通化しつつ、柔軟に使うこともできます。

これにより例えば

  • Middleware で、全画面共通でログイン情報による分岐をさせる
  • Server Action 関数内で、admin権限のときは特定の処理を実行して、user/guest権限のときはエラーを返す

みたいなことができそうです。

// admin権限の場合のみ、ユーザー作成処理を実行する

// ユーザー作成画面
export default function CreateUserPage() {
  return (
    <form action={createUser} method="post">
      <div>
        <label htmlFor="name">名前</label>
        <input type="text" name="name" id="name" required />
      </div>
      <div>
        <label htmlFor="email">メールアドレス</label>
        <input type="email" name="email" id="email" required />
      </div>
      <button type="submit">作成</button>
    </form>
  );
}

// ユーザー作成処理 (Server Action)
async function createUser(formData: FormData) {
  "use server";

  // 例)ログイン情報を取得
  const session = await verifySession();

  // admin権限以外はforbiddenエラーを返す
  if (session.role !== "admin") {
    forbidden();
  }

  // admin権限の場合のみ、ユーザー登録処理を実行
  // ...
}

まとめ

unauthorized()forbidden()によって、Next.js が提供する標準的なエラーハンドリングとして、認証や権限エラーを簡潔に記述できるようになりました。これにより、プロジェクト間での差異もなくなりますし、可読性が上がると思います。

まだ実験的な機能ではあるので、将来の変更にも追従していきたいです。

Discussion