Next.js Basic認証

# Basic認証とは
## 概要
このタイトルを見て、べーしっく認証？？？となる方が多いかと思います。
正直筆者も？？？？？？😇という感じでした笑
しかし、この画像を見るだけで、「あ！見たことある！」となるかと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2017749/04c168b5-739f-0962-beec-4fba528b41ea.png)


これがBasic認証です。
Basic認証とは、**Webサイトにアクセス制限をかける認証**(httpの機能が持つ認証システム)の1つで
手軽に制限をかける認証方法のことです。
特徴は、特定の領域にだけ制限をかけられることです。

## 仕組み
Basic認証では、URLの入力またはリンクをクリックすることで
ブラウザがWebサーバーにリクエストを送ります。
ここでBasic認証を設定していると、Webサーバーはブラウザに認証が必要であることを知らせます。

Webサーバーから認証の知らせを受けたブラウザは、
画面に認証ダイアログを表示して認証を求め、正しい値を入力すれば認証が通り、
アクセスができるようになります。

上記のBasic認証では、「.htaccess」「.htpasswd」という2つのヘッダが働いていて
Basic認証はサーバーの認証をかけたいフォルダに
「.htaccess」「.htpasswd」という2つのファイルを作成し、
それぞれに決まったコードを記述するだけで設定は完了します。
（Nextでは少し特殊な方法でコードを記述すれば、同様のファイルを作成してくれます）
上記のヘッダ内に記載したIDやパスワードと照合することで、
認証の許可もしくは拒否という仕組みが成立します。

ログイン情報が一致していればアクセスが許可され、
不一致であればアクセスは拒否されるため、ユーザーはファイルにアクセスできません。
（ファイル：Nextではページという表現の方がわかりやすいかもですね！）

これがBasic認証の仕組みになります🙌

## メリット・デメリット
### 時間がない方向け
- メリット
    - 他の認証方法より比較的簡単
    - 一度認証すると、ログイン情報が記録される
- デメリット
    - サーバーをまたいだ認証設定ができない
    - クローラーが巡回できないため、SEO対策はできない
    - セキュリティレベルが低め

### メリット
1. 他の認証方法より比較的簡単
1. 一度認証すると、ログイン情報が記録される
:::note warn 
ログイン情報の記録は使用しているブラウザ以外では行われないので
別の端末、別のブラウザでは記録されません！
:::

### デメリット
1. サーバーをまたいだ認証設定ができない
1. クローラーが巡回できないため、SEO対策はできない
1. セキュリティレベルが低め

Basic認証の適用範囲は、ヘッダである「.htaccess」ファイルを設置したディレクトリのみなので
サーバーをまたぐなどディレクトリの範囲を超えた範囲で制限はかけられません。

また、Basic認証を適用した範囲は「クローラー」もアクセス不可となります。
クローラーが巡回できない範囲は情報が取得できず、検索結果に表示できません。
そのためSEO効果を狙いたい場合、Basic認証は使わない方が良いでしょう。

:::note info
クローラーとは、インターネット上に存在するコンテンツを巡回して
あらゆる情報を取得している巡回プログラムのことです。
:::

セキュリティレベルが低いことに関してですが、
Basic認証ではログイン情報を入力する際「Base64」という文字コードを使います。
Base64はアルファベット、数字、記号の64種類のみの文字コードでやり取りするため、
盗聴されやすい通信になります。

さらに、簡易的な通信である「Base64」を平文でやり取りする「http」通信でやり取りすれば、
盗聴されやすくすり抜けられるリスクが上がります。
そのため、Basic認証は脆弱性が高いといわれます。
（httpsであればSSL/TLSで暗号化されるため、比較的マシです）

このようなメリット、デメリットがありますが
今回はインターン先の**社内でのみ使用するといったユースケース**だったため
採用しました！


# 実装
実装するにあたって、必要なことは以下の2点です！
1. API
1. ミドルウェア
1. 環境変数

ほぼコピペで実装できるようになってます〜🙌

## API
まず、APIは以下のコードを記述します。

```ts:pages/api/auth.ts
import type { NextApiRequest, NextApiResponse } from 'next'

export default function handler(_: NextApiRequest, res: NextApiResponse) {
  res.setHeader('WWW-authenticate', 'Basic realm="Secure Area"')
  res.statusCode = 401
  res.end(`Basic Auth Required.`)
}

```

### 解説
`res.setHeader('WWW-authenticate', 'Basic realm="Secure Area"')`は
レスポンスのヘッダ設定です。
ヘッダの内容（`WWW-authenticate`）が気になる方は以下の記事が参考になると思います！

https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/WWW-Authenticate

`res.statusCode = 401`では、認証が不許可であることを示す**Unauthorized**を
表すステータスコード401をセットします。

最後の`res.end(`Basic Auth Required.`)`では
認証失敗時に表示されるメッセージを入れます。

次に、ミドルウェアを実装しましょう〜！

## ミドルウェア
以下のコードをコピペしましょう！
```ts:pages/_middleware.ts
import { NextRequest, NextResponse } from 'next/server'

export function middleware(req: NextRequest) {
  const url = req.nextUrl
  if (!url.pathname.startsWith('/')) return

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

  if (basicAuth) {
    const authValue = basicAuth.split(' ')[1]
    const [user, pwd] = Buffer.from(authValue, 'base64').toString().split(':')

    if (
      user === process.env.BASIC_AUTH_NAME &&
      pwd === process.env.BASIC_AUTH_PASSWORD
    ) {
      return NextResponse.next()
    }
  }
  url.pathname = '/api/auth'

  return NextResponse.rewrite(url)
}

```

### 解説
`if (!url.pathname.startsWith('/')) return`の`/`には使用したいページのパスを入れます。
ここでは、使うページ以外でBasic認証が動いてほしくないので
早期リターンにしています。

`const [user, pwd] = Buffer.from(authValue, 'base64').toString().split(':')`
ここは**よく検索でヒットする内容と大きく異なる**箇所になります。
検索で出てくるものは`Buffer.from(authValue, 'base64').toString()`の代わりに
`atob()`が使用されますが、今は`Buffer.from`の書き方が推奨されているので
こちらを使用しました！

`if ( user === process.env.NEXT_PUBLIC_BASIC_AUTH_NAME && pwd === process.env.NEXT_PUBLIC_BASIC_AUTH_PASSWORD )`
ここも他の検索記事と少し違いますが、下手にパスワードが漏洩しても困るので
`.env`に記述し、環境変数として扱いました！

:::note warn
Nextjsのバージョンによっては、`_middleware.ts`のファイル名で動作しないことがあります。
その時は、`middleware.ts`とアンダースコア`_`を除去すれば問題ありません！
:::

## 環境変数の設定
```:.env
BASIC_AUTH_NAME=username
BASIC_AUTH_PASSWORD=password
```

こちらは先ほど用意した環境変数ですね！
~~Nextでは、`NEXT_PUBLIC_`で始める必要があるので
その点だけご注意を！~~

:::note warn
`NEXT_PUBLIC_`をつけてしまうとブラウザからアクセスできてしまうため
つけない方が良いです。また、付けずとも読み込むことはできます。
参考：[Next.js公式](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#bundling-environment-variables-for-the-browser)
:::



# 終わりに
Basic認証、いかがでしたか？
サクッと簡単に認証したい場合に使えるので、ユースケースがあっていれば
すごく便利ですね！
ぜひみなさんもユースケースを考えて適切な認証を
作成しましょう✊
それでは👋

# 参考文献
https://www.cybersolutions.co.jp/blog/basic/#:~:text=Basic%E8%AA%8D%E8%A8%BC%E3%81%A8%E3%81%AFWeb,%E3%82%89%E3%82%8C%E3%82%8B%E7%82%B9%E3%81%8C%E7%89%B9%E5%BE%B4%E3%81%A7%E3%81%99%E3%80%82

https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/WWW-Authenticate

https://nextjs.org/docs/advanced-features/middleware