こんにちは、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を使うと簡単に実装できるのでぜひ活用してください。