こんにちは、Webエンジニアの永井です。
今回はNextAuth.jsを利用してログインユーザーの権限毎に認証を実装する方法を紹介します。
サイトのページによっては特定の権限を持つユーザーのみアクセス可能にしたい場合があると思うので、その場合に役立ちます。
環境
next: 13.4.7
next-auth: 4.22.1
NextAuth.jsとは
NextAuth.jsは、Next.jsアプリケーションで簡単に認証機能を実装するためのフレームワークです。様々な認証プロバイダー(Google、Facebook、Twitterなど)と連携し、ユーザーのログインやセッション管理を行うことができます。
ユーザー権限毎に認証を実装するには、NextAuth.jsのmiddlewareの機能と、Next.jsのmiddlewareの機能を組み合わせて使う必要があります。なので、それぞれの特徴について簡単に見ておきます。
Next.js
Next.jsのmiddlewareを使うと、ページにアクセスする前に処理を実行することができます。リクエストの内容に応じた前処理や認証、リダイレクトなどに適しています。
実装はmiddleware.ts (or .js)をプロジェクトのルート直下やsrc直下に配置して行います。
NextAuth.js
NextAuth.jsのmiddlewareでは、サイト全体に認証を付けたり、特定ページのみに認証を付けたりすることができます。
実装自体はNext.jsのmiddlewareファイルに書きます。
実装
例として、以下のようなサイトを想定して実装します。
- ログインユーザーの権限は「管理者」「ユーザー」の二つ
- ログインページ以外でログインが必要
- 特定ページのみ「管理者」権限が必要
- 特定ページに「管理者」以外がアクセスした場合は404を返す
ユーザー権限をトークンに設定する
まず、トークンに権限を設定します。このトークンをmiddlewareで受け取って権限の判定を行います。
// pages/api/auth/[...nextauth].ts
import NextAuth, { NextAuthOptions } from "next-auth"
export const authOptions: NextAuthOptions = {
...
  callbacks: {
    async jwt({ token }) {
      token.userRole = getUserRole();
      return token;
    },
  },
};
export default NextAuth(authOptions);このままではtoken.userRoleで型エラーが出てしまうので、型定義ファイルを作成して解決します。
import "next-auth/jwt"
declare module "next-auth/jwt" {
  interface JWT {
    userRole?: "admin" | "user";
  }
}
middlewareに認証を実装する
設定したトークンを元に権限を判定して、認証を実装します。
// src/middleware.ts
import { NextResponse } from 'next/server';
import { withAuth } from 'next-auth/middleware';
// 管理者権限が必要なパス一覧
const adminOnlyPathRegexs = [
  new RegExp(`^/xxx/.*$`),
  ...
];
export default withAuth(
  function middleware(req) {
    const { pathname } = req.nextUrl;
    // 管理者権限のないユーザーが管理者権限が必要なパスにアクセスした場合は404
    if (
      req.nextauth.token?.userRole !== "admin" &&
      adminOnlyPathRegexs.some((regex) => regex.test(pathname))
    ) {
      return NextResponse.rewrite(new URL('/404', req.url));
    }
  }
);
export const config = {
  // ログインページ(/login)以外を対象にする
  matcher: '/((?!login).*)',
};
これでユーザー権限毎に認証を実装できました。
今回は省略していますが、middleware関数が呼ばれる前にcallbacksの設定が呼ばれます。この中のauthorizedでは、falseを返すことでログインページにリダイレクトさせることが可能です。
デフォルトでは、ログイン済みの場合(tokenがある)にtrueが返され、未ログインではログインページにリダイレクトされます。
// Example (default value)
export default withAuth(
...
 callbacks: {
   authorized({ req , token }) {
     if(token) return true; // If there is a token, the user is authenticated
   }
 }
};
例えば、管理者以外はログインページにリダイレクトしたい場合はこちらで判定することができます。
export default withAuth(
...
 callbacks: {
   authorized({ req , token }) {
     return token?.userRole === "admin";
   }
 }
};
終わりに
NextAuth.jsを利用して認証を実装する方法を紹介しました。
NextAuth.jsを使うと簡単に実装できるのでぜひ活用してください。