🐡
Next.js x Vercel でBasic認証を実装
TL;DR
- Next.js x Vercel で Basic 認証を middleware で実装する方法を紹介します。
動機
- 作成しているリリース前のサイトに Basic 認証を設定したいと考えます。
-
yarn dev
でも Basic 認証を求められると面倒なので、Production 環境だけ適用します。
実装方法
Basic 認証はmiddlewareを利用し実装できます。
公式のサンプルコードとして以下が参考になります。
重要なファイルは 2 つあります。
middleware.ts
以下が、middleware.ts
のサンプルソースコードです。
-
/
と/index
に一致する URL に対してこの middleware 関数が適用されます。 - middleware 関数は、authorization ヘッダーから Basic 認証情報を取得し、ユーザー名とパスワードが正しい場合は指定のアドレスにアクセスします。
- 正しくない場合は、
/api/auth
を呼び出し、Basic 認証をリライトします。
middleware.ts
import { NextRequest, NextResponse } from 'next/server'
export const config = {
matcher: ['/', '/index'],
}
export function middleware(req: NextRequest) {
const basicAuth = req.headers.get('authorization')
const url = req.nextUrl
if (basicAuth) {
const authValue = basicAuth.split(' ')[1]
const [user, pwd] = atob(authValue).split(':')
if (user === '4dmin' && pwd === 'testpwd123') {
return NextResponse.next()
}
}
url.pathname = '/api/auth'
return NextResponse.rewrite(url)
}
pages/api/auth.ts
以下が、api/auth.ts
のサンプルソースコードです。
- Next.js で API ルートを作成します。
- Basic 認証に関連する値を response に設定します。
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(`Auth Required.`)
}
では、次に新規にプロジェクトを作成し、実際に Basic 認証を追加実装します。
新規プロジェクト作成
terminal
$ pnpm create next-app next-basic-auth --typescript --eslint --src-dir --import-alias "@/*" --use-pnpm
実施結果
terminal
Library/pnpm/store/v3/tmp/dlx-35818 | +1 +
Packages are hard linked from the content-addressable store to the virtual store.
Content-addressable store is at: /Users/hayato94087/Library/pnpm/store/v3
Virtual store is at: Library/pnpm/store/v3/tmp/dlx-35818/node_modules/.pnpm
Library/pnpm/store/v3/tmp/dlx-35818 | Progress: resolved 1, reused 1, downloaded 0, added 1, done
✔ Would you like to use experimental `app/` directory with this project? … No / Yes
Creating a new Next.js app in /Users/hayato94087/Private/next-basic-auth.
Using pnpm.
Installing dependencies:
- react
- react-dom
- next
- @next/font
- typescript
- @types/react
- @types/node
- @types/react-dom
- eslint
- eslint-config-next
Packages: +267
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Packages are hard linked from the content-addressable store to the virtual store.
Content-addressable store is at: /Users/hayato94087/Library/pnpm/store/v3
Virtual store is at: node_modules/.pnpm
Progress: resolved 279, reused 261, downloaded 6, added 267, done
dependencies:
+ @next/font 13.1.6
+ @types/node 18.13.0
+ @types/react 18.0.28
+ @types/react-dom 18.0.10
+ eslint 8.34.0
+ eslint-config-next 13.1.6
+ next 13.1.6
+ react 18.2.0
+ react-dom 18.2.0
+ typescript 4.9.5
Done in 9.1s
Initializing project with template: default
Initialized a git repository.
Success! Created next-basic-auth at /Users/hayato94087/Private/next-basic-auth
<!-- textlint-disable -->
middleware.tsの作成
- src/middleware.ts を作成します。
- 今回はすべてのリクエストに対して Basic 認証を実施するため、matcher で特定パスを指定しません。
- 本番環境だけ Basic 認証を適用させます。
src/middleware.ts
import { NextRequest, NextResponse } from 'next/server';
export function middleware(req: NextRequest) {
const basicAuth = req.headers.get('authorization');
const url = req.nextUrl;
if (process.env.NODE_ENV === 'production') {
if (basicAuth) {
const authValue = basicAuth.split(' ')[1];
const [user, pwd] = atob(authValue).split(':');
if (user === '4dmin' && pwd === 'testpwd123') {
return NextResponse.next();
}
}
url.pathname = '/api/auth';
return NextResponse.rewrite(url);
}
}
実装する上での注意点があります。middleware の仕様が変更になっているため、インターネット上にある古い記事では Buffer.from
で実装しているソースコードがあることに注意してください。現在、Buffer.from
の代わりに atob
を利用します。
以下が middleware.ts で利用できる API です。
src/pages/api/auth.ts
src/pages/api/auth.ts を作成します。
src/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(`Auth Required.`)
}
ローカルで動作確認
開発環境を実行します。
terminal
$ yarn dev
- 開発環境では、
process.env.NODE_ENV
はdevelopment
になります。 - if 文により、Basic 認証を実施せずにページを表示できます。
ローカルの本番環境で動作確認
本番環境を実行します。
terminal
$ yarn build
$ yarn start
- 本番環境では、
process.env.NODE_ENV
はproduction
になります。 - if 文により、Basic 認証を実施してページを表示できます。
Vercelで動作確認
以下を実施した上で、動作確認を行います。
- GitHub のリポジトリにソースを push
- Vercel で新規プロジェクトを作成し、GitHub のリポジトリを指定
index ページにアクセスすると、Basic 認証が実施されます。
ちなみに、画像ファイルの next.svg に直接アクセスしても Basic 認証が実施されます。
まとめ
Vercel の middleware を利し、Production 環境だけ Basic 認証を実施できました。
参考
Discussion
こんにちは!いつも記事拝見させていただいております。
一点、Basic認証なのでまあいいかなー、とは思いつつ、
本来以下の部分はenvであるべきかなと思ったのでコメントさせていただきました。