Next.js でミドルウェアを活用するとき、config
オブジェクトの matcher
プロパティが重要な役割を果たします。本記事では、この matcher
の仕組みや、withAuth
と組み合わせた場合の動作について、初心者にもわかりやすく解説します。また、NextAuthMiddlewareOptions
を参考に、withAuth
の具体的な挙動についても触れていきます。
middleware.ts
と matcher
の基本動作
Next.js の middleware.ts
は、リクエストがサーバーに到達する前に実行される特別な処理を記述する場所です。config.matcher
プロパティを設定することで、どのリクエストにミドルウェアを適用するかを制御できます。
例として、以下の設定を見てみましょう:
export const config = {
matcher: ['/dashboard/:path', '/editor/:path', '/login', '/register'],
}
matcher
で指定されたパスのみ対象
この例では、/dashboard/:path
,/editor/:path
,/login
,/register
にリクエストが送られた場合のみ、ミドルウェアが実行されます。matcher
に指定されていないパスは対象外/about
や/api/data
など、matcher
に含まれていないパスはミドルウェアをスキップし、直接リクエストが処理されます。
withAuth
の動作と authorized
コールバック
NextAuth.js の withAuth
を利用すると、認証状態に基づいてリクエストを制御できます。このとき、authorized
コールバックが重要な役割を果たします。
基本例
以下は、特定の条件でリクエストを制限する例です:
export default withAuth({
callbacks: {
authorized: ({ token }) => !!token, // JWT トークンが存在すればアクセス許可
},
});
authorized
コールバックauthorized
は、リクエストを許可するかどうかを決定する関数です。この関数がtrue
を返すと、リクエストがそのまま進行します。false
を返す場合、認証ページ(デフォルトでは/api/auth/signin
)にリダイレクトされます。- トークンの検証
authorized: ({ token }) => !!token
では、トークンが存在する場合にtrue
を返しています。つまり、認証済みのリクエストだけが許可されます。
matcher
が動作に与える影響
matcher
は、ミドルウェアを実行するリクエストを選別するため、認証が必要なページや不要なページを効率よく制御できます。
認証前後のページに matcher
を設定する理由
- 認証前のページ (
/login
,/register
)
認証済みユーザーがこれらのページにアクセスした場合、再度ログインフォームを表示するのは不自然です。ミドルウェアで/dashboard
にリダイレクトすることで、自然な動作を実現します。 - 認証後のページ (
/dashboard
,/editor
)
未認証ユーザーがこれらのページにアクセスすると、セキュリティ的に問題があります。ミドルウェアで/login
にリダイレクトすることで、アクセス制限を確実にします。
matcher
を使った効率的な制御
matcher
を設定することで、ミドルウェアを適用する範囲を必要最小限に抑えることができます。
- パフォーマンス向上
不要なリクエストにミドルウェアを適用しないため、処理が軽くなります。 - 意図した動作の保証
/api
や_next/static
など、認証と無関係なリソースに影響を与えないため、予期せぬエラーが減ります。
実例: middleware.ts
全体のコード
以下は、認証ページと認証後ページを適切に制御するミドルウェアの例です:
import { withAuth } from 'next-auth/middleware';
import { NextResponse } from 'next/server';
export default withAuth(
async function middleware(req) {
const { pathname } = req.nextUrl;
// 認証済みのトークンを取得
const token = await getToken({ req });
const isAuth = !!token;
const isAuthPage = pathname.startsWith('/login') || pathname.startsWith('/register');
// 認証済みのユーザーがログインページにアクセスした場合
if (isAuthPage && isAuth) {
return NextResponse.redirect(new URL('/dashboard', req.url));
}
// 未認証ユーザーが保護されたページにアクセスした場合
if (!isAuth && !isAuthPage) {
return NextResponse.redirect(new URL('/login', req.url));
}
},
{
callbacks: {
authorized: ({ token }) => !!token, // 認証済みかどうかをチェック
},
}
);
export const config = {
matcher: ['/dashboard/:path', '/editor/:path', '/login', '/register'],
};
まとめ
matcher
は、ミドルウェアを効率的に適用するためのフィルターです。特定のパスにのみ認証ロジックを適用することで、不要な処理を減らし、意図した動作を保証できます。
- 認証前のページを含める理由:認証済みユーザーが不要なログインページを見ないようにするため。
- 認証後のページを含める理由:未認証ユーザーがアクセスできないようにするため。
これを活用することで、Next.js アプリケーションの認証機能を安全かつ効率的に構築できます。