Next.jsの開発でBASIC認証をかける方法をご紹介します。
最終的にローカルではBASIC認証がかからないけど、VercelではBASIC認証がかかるようにします。
実装する上でnextjs-basic-auth
というライブラリを使ってみました。
スター数もそこまで多くなく、バージョンも0.1.2だったので不安だったのですが、BASIC認証が動作する事を確認しました!
GitHubに参考用プロジェクトを上げています
https://github.com/yuki-takara/nextjs-basic-auth-sample
同じく記事内のプロジェクトをVercelに上げています
https://nextjs-basic-auth-sample.vercel.app/
ご参考までに
検証した環境
1 | next | 11.0.1 |
2 | nextjs-basic-auth | 0.1.2 |
nextjs-basic-authのバージョンを0.1.3にしたところ
以下の実装でERR_STREAM_WRITE_AFTER_END
というエラーが出て、
ローカルでもVercelでもエラーが起きました。
0.1.2だと実行出来るため、まだ安定しないところがありそうです
Next.jsのversion12以降を利用している人はmiddlewareを利用するとより簡単にBasic認証をかける事が出来ます!
下準備
プロジェクトの作成
$ npx create-next-app --ts
✔ What is your project named? … nextjs-basic-auth-sample
表示される事を確認
$ yarn run dev
どのページでもBASIC認証がかかる事を確認したいので適当なページを追加します
import React, { VFC } from 'react'
const Hello: VFC = () => {
return <h1>hello page</h1>
}
export default Hello
BASIC認証をかける
BASIC認証用のライブラリを追加
$ yarn add nextjs-basic-auth
公式に書かれているやり方をまず参考にしていきます。
認証に用いるためのファイルを作成します。
公式だとプロジェクト直下にutil
フォルダを用意してますが、ファイルのパスはどこでも構わないので、私はsrc/utils
フォルダ配下に用意しました。
import initializeBasicAuth from 'nextjs-basic-auth'
const users = [
{ user: 'user', password: 'password' },
{ user: 'admin', password: 'admin' },
]
export default initializeBasicAuth({ users })
↑の場合、それぞれ
「user:password」「admin:admin」の組み合わせでBASIC認証がかけられます。
最後に全ページでBASIC認証をかけたいので、_app.tsx
に認証をかけます。
↓がgetInitialProps
を_app.tsxで呼び出す際の基本形で、
import App from 'next/app'
import type { AppProps, AppContext } from 'next/app'
import '../styles/globals.css'
function MyApp({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />
}
MyApp.getInitialProps = async (appContext: AppContext) => {
const appProps = await App.getInitialProps(appContext)
return { ...appProps }
}
export default MyApp
ここにBASIC認証のための設定を追記します。
import App from 'next/app'
import type { AppProps, AppContext } from 'next/app'
import basicAuthCheck from '../src/utils/basicAuthCheck'
import '../styles/globals.css'
function MyApp({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />
}
MyApp.getInitialProps = async (appContext: AppContext) => {
const { req, res } = appContext.ctx
if (req && res) {
await basicAuthCheck(req, res)
}
const appProps = await App.getInitialProps(appContext)
return { ...appProps }
}
export default MyApp
この状態でVercelにアップすると同じくBASIC認証がかかった状態になります!
BASIC認証を使いやすくする
キャンセルした時の対応
さてここまでの実装だととても大きな問題があります
キャンセル押すと普通に表示される!!!??? 😨😨😨
もはやBASIC認証の意味が何もないという、、w
これに関してはIssueが上がっていて、Issue内で対応策が投稿されています
If you cancel, the page loads normally. · Issue #4 · jchiatt/nextjs-basic-auth
参考にしながら basicAuthCheck.ts
を書き換えます
import initializeBasicAuth from 'nextjs-basic-auth'
import type { IncomingMessage, ServerResponse } from 'http'
const users = [
{ user: 'user', password: 'password' },
{ user: 'admin', password: 'admin' },
]
const basicAuthCheck = async (req: IncomingMessage, res: ServerResponse): Promise<void> => {
await initializeBasicAuth({ users })(req, res)
// Workaround for major bug: If you cancel, the page loads normally. - https://github.com/jchiatt/nextjs-basic-auth/issues/4
if (res.statusCode === 401) {
res.end('<html>Unauthorized</html>')
}
}
export default basicAuthCheck
無事「Unauthorized」と表示され、コンテンツが表示されなくなりました!
BASIC認証の有無を.envで
この状態だとローカルでもSTGでも本番でもBASIC認証がかかってしまいます。
あるあるだと「STGだけBASIC認証をかけて関係者のみに公開する」というパターンですよね。
それを実現するため、.env
の値で制御出来るようにします。
# ローカル環境ではBASIC認証をかけない
ENABLE_BASIC_AUTH=false
module.exports = {
・・・
env: {
ENABLE_BASIC_AUTH: process.env.ENABLE_BASIC_AUTH,
},
}
Vercelの Settings > Environment Variable でENABLE_BASIC_AUTH
を追加
参考用として作っているプロジェクトのためProduction
にもチェックを入れています
あとは_app.tsx
のgetInitialProps
のBASIC認証の部分の条件分岐を書き換えればOK!
MyApp.getInitialProps = async (appContext: AppContext) => {
const { req, res } = appContext.ctx
if (req && res && process.env.ENABLE_BASIC_AUTH === 'true') {
await basicAuthCheck(req, res)
}
const appProps = await App.getInitialProps(appContext)
return { ...appProps }
}
これでVercel上でのみBASIC認証がかかるようになりました!
BASIC認証のユーザ名・パスワードを.envで管理する
この状態だとBASIC認証用のユーザ名・パスワードをGit管理する事になります。
変更する時コードを一々変更しないといけなくなるので、同じく.envで管理してみます。
ENABLE_BASIC_AUTH=false
BASIC_AUTH_USERNAME=
BASIC_AUTH_PASSWORD=
module.exports = {
・・・
env: {
ENABLE_BASIC_AUTH: process.env.ENABLE_BASIC_AUTH,
BASIC_AUTH_USERNAME: process.env.BASIC_AUTH_USERNAME || 'admin',
BASIC_AUTH_PASSWORD: process.env.BASIC_AUTH_PASSWORD || 'admin',
},
}
Vercelで同じように設定
basicAuthCheckで.envの値を読み込むように変更
import initializeBasicAuth from 'nextjs-basic-auth'
import type { IncomingMessage, ServerResponse } from 'http'
const users = [
{ user: process.env.BASIC_AUTH_USERNAME!, password: process.env.BASIC_AUTH_PASSWORD! },
]
const basicAuthCheck = async (req: IncomingMessage, res: ServerResponse): Promise<void> => {
await initializeBasicAuth({ users })(req, res)
// Workaround for major bug: If you cancel, the page loads normally. - https://github.com/jchiatt/nextjs-basic-auth/issues/4
if (res.statusCode === 401) {
res.end('<html>Unauthorized</html>')
}
}
export default basicAuthCheck
これでローカルではBASIC認証がかからず、Vercel上のみでBASIC認証がかかるようになりました!