Next.js v14 の App Router で Basic 認証するには 

自分が作成したアプリケーションを特定の下層パスのみに認証をかけてパスワードを知っている一部のユーザーしか見せたくないってことありますよね。

![スクリーンショット 2024-05-19 14.38.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42432/f7151e0d-59ce-a043-ad6d-32c8e2cf3616.png)

アプリケーションの簡単な認証を実装する場合でも、アプリケーションを本番環境に移行するまでのアクセスを保護する場合でも、「Basic認証」または「HTTP認証」とも呼ばれる認証は、シンプルで効果的なソリューションです。この記事では、Next.js `v14`のプロジェクトで App Router を使用して Basic 認証 を実装する方法を紹介します。

# Basic 認証 とは？

Next.jsのプロジェクトでこれを実装する方法を理解するためには、まずBasic認証の機能を理解することが役立ちます。
その動作は比較的簡単です。

1. 訪問者が保護されたページにアクセスしようとします
1. サーバーはステータス`401`のレスポンスを送り、ユーザーが認証できるようにするための情報を`WWW-Authenticate`レスポンスヘッダーに含めます
1. 訪問者は認証フォームを含むウィンドウを通じて認証を行います
1. ブラウザは訪問者の認証情報を`Base64`でエンコードし、`Authorization`ヘッダーに含めてサーバーに送信します
1. サーバーは認証情報を確認し、認証情報の確認に応じてステータス`200`または`403`のレスポンスを返します
1. ブラウザはタブが閉じられるまでこれらの認証情報をキャッシュします

# Next.js で Basic 認証 を実装するための手順

Next.jsのプロジェクトで App Router を使用して「Basic 認証」を実装するには、以下の手順を踏む必要があります。

- **1.** 認証情報を保存するための環境変数を宣言
- **2.** ユーザーに認証インターフェースを提供するための ルートハンドラー を作成
- **3.** ユーザーがログインしているかどうかを確認し、リダイレクトするための ミドルウェア を作成

## 1. 認証情報を保存するための環境変数を宣言

この記事では、環境変数に認証情報を定義します。

また、エンドツーエンドテストや Lighthouse テストなどの特定のケースに応じて認証を有効または無効にするための別の環境変数を追加することもできます。

```.env.local
BASIC_AUTH_DISABLED=false
BASIC_AUTH_USER=user
BASIC_AUTH_PASSWORD=password
```

## 2. 認証インターフェースを提供するための ルートハンドラー を作成

このルートハンドラーは、Next.jsがユーザーに対してブラウザに直接統合されたインターフェースを通じて認証を要求するために、エラー401を返すことを可能にします。

```src/app/api/auth/route.ts
export const GET = () => {
  return new Response('Basic認証が必要です', {
    status: 401,
    headers: {
      'WWW-Authenticate':
        'Basic realm="Access to the staging site", charset="UTF-8"',
    },
  })
}
```

## 3. リダイレクトするための ミドルウェア を作成

環境変数が初期化され、ルートハンドラーが作成されたら、Next.jsアプリケーションへのアクセスを保護するためのミドルウェアを作成できます。

```src/middleware.ts
import { NextResponse, NextRequest } from 'next/server'

export const middleware = (req: NextRequest) => {
  if (
    process.env.BASIC_AUTH_DISABLED === 'true' ||
    process.env.NODE_ENV === 'development'
  ) {
    return NextResponse.next()
  }

  const basicAuth = req.headers.get('authorization')

  if (basicAuth) {
    const authValue = basicAuth.split(' ')[1]

    const [username, password] = atob(authValue).split(':')

    if (
      username === process.env.BASIC_AUTH_USER &&
      password === process.env.BASIC_AUTH_PASSWORD
    ) {
      return NextResponse.next()
    }
  }

  const url = req.nextUrl
  url.pathname = '/api/auth'

  return NextResponse.rewrite(url)
}

export const config = {
  matcher: ['/private'],
}

```

まず、環境変数を使用して認証が無効になっているか、開発環境にいるかを確認します。
この条件が`true`であれば、認証を使用しません。

```src/middleware.ts
  if (
    process.env.BASIC_AUTH_DISABLED === 'true' ||
    process.env.NODE_ENV === 'development'
  ) {
    return NextResponse.next()
  }
```

認証を使用する場合、まず、受信リクエストから`authorization`ヘッダーの値を取得します。
このヘッダーに値が含まれている場合、`user`と`password`の認証情報を取得しようとします。

これらの値が環境変数に保存されている認証情報と一致する場合、ユーザーは認証され、希望するURLにアクセスできます。

```src/middleware.ts
  const basicAuth = req.headers.get('authorization')

  if (basicAuth) {
    const authValue = basicAuth.split(' ')[1]

    const [username, password] = atob(authValue).split(':')

    if (
      username === process.env.BASIC_AUTH_USER &&
      password === process.env.BASIC_AUTH_PASSWORD
    ) {
      return NextResponse.next()
    }
  }
```

それ以外の場合、ユーザーは認証を求めるルートハンドラーにリダイレクトされます。

```src/middleware.ts
const url = req.nextUrl
url.pathname = "/api/auth"

return NextResponse.rewrite(url)
```

最後に、`config`オブジェクトを使用して、どのURLが認証を要求するかを選択できます。


```src/middleware.ts
export const config = {
  matcher: ['/private'],
}
```

# まとめ

App Router を使用する Next.js アプリケーションで「Basic認証」または「HTTP認証」を実装することは、それほど難しいものではありません。

認証を実装することで、特定のケースでアプリケーションへのアクセスを保護することができます。

この認証は、プロジェクトの開発中や開発中の機能に対して、WebサイトやNext.jsアプリケーションへのアクセスを防ぐために使用できます。

https://github.com/andmorefine/next-js-basic-auth

今回使用したソースコードをgithubに置いておきましたので参考にしてみてください。

## 参考資料

- [WWW-Authenticate](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/WWW-Authenticate)
- [How To Add HTTP Basic Auth to Next.js](https://vancelucas.com/blog/how-to-add-http-basic-auth-to-next-js/)
- [Authentication](https://nextjs.org/docs/pages/building-your-application/authentication)