•8 min read

Next.js Middleware:堅牢な認証と認可の構築

Next.js Middleware:堅牢な認証と認可の構築

認証と認可は、あらゆるセキュアなウェブアプリケーションの基盤です。これまでNext.jsでは、ルートを保護するために、コンポーネントを高階コンポーネント(HOC)でラップしたり、ページごとにgetServerSidePropsでチェックを実行したりしていました。しかし、Next.js App RouterとEdge Middlewareの成熟により、セキュリティを処理するための、はるかに優れた、一元化された、高性能な方法が利用できるようになりました。

2026年、Next.js MiddlewareはEdgeで実行されます。これは、認証チェックがユーザーに非常に近い場所で、リクエストがオリジンサーバーに到達する前に実行されることを意味します。この記事では、Next.js Middlewareを使用して、堅牢でスケーラブルな認証および認可パイプラインを構築する方法を探ります。

Audio Briefing
0:00 / 0:00

認証にMiddlewareを使用する理由

Middlewareを使用すると、リクエストをインターセプトし、有効なセッションまたはトークンをチェックし、リクエストを通過させるか、書き換えるか、ユーザーをリダイレクトするかを決定できます。

主な利点:

  1. 一元化: 認証ロジックを何十ものページやレイアウトに分散させる必要がなくなります。単一のmiddleware.tsファイルでアプリケーション全体を保護できます。
  2. パフォーマンス: Edgeランタイム(Vercel Edge FunctionsやCloudflare Workersなど)で実行されるため、コールドスタートがほぼゼロで、レイテンシーが最小限に抑えられます。ページがレンダリングを開始する前にチェックが実行されます。
  3. セキュリティ: 認証されていないリクエストをEdgeで停止することで、オリジンサーバーとデータベースへの負荷を軽減します。
Advertisement

基本的な認証フロー

当社のゼロリークブラウザデコーダーを使用して、JWTペイロード、ヘッダークレーム、および有効期限タイムスタンプをリアルタイムでテストおよび検査してください。

クッキーに保存されたJSON Webトークン(JWT)を使用した標準的な実装を見てみましょう。

1. Middlewareのセットアップ

プロジェクトのルート(または使用している場合はsrcディレクトリ)にmiddleware.tsファイルを作成します。

import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { verifyJwtToken } from '@/lib/auth' // Your custom JWT verifier

export async function middleware(request: NextRequest) {
  const token = request.cookies.get('auth-token')?.value

  const isAuthPage = request.nextUrl.pathname.startsWith('/login') || 
                     request.nextUrl.pathname.startsWith('/register')

  // 1. Check if token exists and is valid
  const verifiedToken = token && (await verifyJwtToken(token).catch((err) => {
    console.error('Failed to verify token', err)
  }))

  // 2. Redirect unauthenticated users away from protected routes
  if (!verifiedToken) {
    if (!isAuthPage && request.nextUrl.pathname.startsWith('/dashboard')) {
      const loginUrl = new URL('/login', request.url)
      loginUrl.searchParams.set('callbackUrl', request.nextUrl.pathname)
      return NextResponse.redirect(loginUrl)
    }
  }

  // 3. Redirect authenticated users away from auth pages
  if (verifiedToken && isAuthPage) {
    return NextResponse.redirect(new URL('/dashboard', request.url))
  }

  // 4. Pass the request through if all checks pass
  return NextResponse.next()
}

// See "Matching Paths" below
export const config = {
  matcher: ['/dashboard/:path*', '/login', '/register'],
}

2. パスのマッチング(matcher)

matcherの設定は非常に重要です。これは、どのルートがミドルウェアをトリガーするかをNext.jsに伝えます。すべてのリクエスト(静的アセットやAPIルートを含む)でミドルウェアを実行するのは非効率的です。

['/dashboard/:path*', '/login', '/register']を指定することで、認証ロジックが必要な場所でのみ実行されるようにします。

認可の処理(ロールベースアクセス制御 - RBAC)

認証は「あなたは誰ですか?」と尋ねます。認可は「あなたは何を許可されていますか?」と尋ねます。ユーザーが認証されると、そのユーザーのロールや権限をチェックする必要があることがよくあります。

ミドルウェアを拡張してRBACを簡単に処理できます。

// ... previous imports
import { verifyJwtToken } from '@/lib/auth'

export async function middleware(request: NextRequest) {
  const token = request.cookies.get('auth-token')?.value
  const verifiedToken = token && (await verifyJwtToken(token))

  // ... standard auth checks

  // Handle Authorization
  if (verifiedToken) {
    const userRole = verifiedToken.role // Assuming role is encoded in the JWT

    // Protect admin routes
    if (request.nextUrl.pathname.startsWith('/admin')) {
      if (userRole !== 'ADMIN') {
        // User is authenticated, but not authorized
        return NextResponse.redirect(new URL('/unauthorized', request.url))
      }
    }
  }

  return NextResponse.next()
}

export const config = {
  matcher: ['/dashboard/:path*', '/admin/:path*', '/login'],
}

Middlewareにおけるセキュリティのベストプラクティス

  1. 軽量に保つ: Edgeランタイムにはサイズと実行時間の制限があります。ミドルウェアで重いデータベースクエリを実行しないでください。JWT検証のような高速でステートレスなチェックに依存してください。
  2. Edge JWTにはjoseを使用する: 標準のNode.js jsonwebtokenはNodeの暗号化APIに依存していますが、これらはEdgeでは利用できません。ミドルウェアでトークンの署名と検証を行うには、joseのようなライブラリを使用してください。
  3. セキュアなクッキー: XSSおよびCSRF攻撃を防ぐために、認証クッキーがHttpOnly、Secure、およびSameSite=laxまたはstrictで設定されていることを確認してください。
  4. 適切なエラー処理: トークン検証が失敗した場合(例:署名の期限切れ)、エラーを適切にキャッチしてログインにリダイレクトし、500エラーを引き起こす未処理の例外をスローしないようにしてください。
Advertisement

高度なトピック: サーバーコンポーネントへのデータ渡し

一般的な課題は、ミドルウェアでトークンを検証した後、トークンを再検証せずにユーザーデータをサーバーコンポーネントに渡す方法です。

ミドルウェアでリクエストヘッダーを変更し、サーバーコンポーネントでそれらを読み取ることができます。

ミドルウェア内:

if (verifiedToken) {
  const requestHeaders = new Headers(request.headers)
  requestHeaders.set('x-user-id', verifiedToken.userId)
  
  return NextResponse.next({
    request: {
      headers: requestHeaders,
    },
  })
}

サーバーコンポーネント内(page.tsx):

import { headers } from 'next/headers'

export default function DashboardPage() {
  const headersList = headers()
  const userId = headersList.get('x-user-id')

  return <div>Welcome User: {userId}</div>
}

まとめ

Next.js Middlewareは、認証と認可を処理するための強力なEdge最適化レイヤーを提供します。これらのチェックを一元化することで、コンポーネント階層を大幅に簡素化し、パフォーマンスを向上させ、アプリケーション全体のセキュリティ体制を強化できます。ステートレスなJWTと慎重なルートマッチングを組み合わせることで、最新のNext.jsアーキテクチャのバックボーンを形成します。

こちらもおすすめです

Share this article:

Stay Updated

Get the latest posts delivered straight to your inbox.

Free Developer Utilities

Free In-Browser Developer Tools

Clean AI CLI logs, build cron expressions, decode JWTs, and calculate chmod permissions offline.

Explore Tools
Advertisement