Next.jsでBASIC認証を導入する(ローカル,vercel 両対応)

投稿日
/
更新日
Next.jsでBASIC認証を導入する(ローカル,vercel 両対応)

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
Next.jsの初期画面が表示された



どのページでもBASIC認証がかかる事を確認したいので適当なページを追加します

pages/hello.tsx
import React, { VFC } from 'react'
 
const Hello: VFC = () => {
  return <h1>hello page</h1>
}
 
export default Hello
helloページが表示された

BASIC認証をかける

BASIC認証用のライブラリを追加

jchiatt/nextjs-basic-auth

$ yarn add nextjs-basic-auth



公式に書かれているやり方をまず参考にしていきます。


認証に用いるためのファイルを作成します。

公式だとプロジェクト直下にutilフォルダを用意してますが、ファイルのパスはどこでも構わないので、私はsrc/utilsフォルダ配下に用意しました。

src/utils/basicAuthCheck.ts
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で呼び出す際の基本形で、

pages/_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認証のための設定を追記します。

pages/_app.tsx
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を書き換えます

src/utils/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」と表示され、コンテンツが表示されなくなりました!

Unauthorizedと表示されるようになった


BASIC認証の有無を.envで

この状態だとローカルでもSTGでも本番でもBASIC認証がかかってしまいます。

あるあるだと「STGだけBASIC認証をかけて関係者のみに公開する」というパターンですよね。



それを実現するため、.envの値で制御出来るようにします。

.env.local
# ローカル環境ではBASIC認証をかけない
ENABLE_BASIC_AUTH=false
next.config.js
module.exports = {
  ・・・
  env: {
    ENABLE_BASIC_AUTH: process.env.ENABLE_BASIC_AUTH,
  },
}


Vercelの Settings > Environment Variable でENABLE_BASIC_AUTHを追加

Vercelの環境変数にENABLE_BASIC_AUTHを追加

参考用として作っているプロジェクトのためProductionにもチェックを入れています



あとは_app.tsxgetInitialPropsのBASIC認証の部分の条件分岐を書き換えればOK!

pages/_app.tsx
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で管理してみます。

.env.local
ENABLE_BASIC_AUTH=false
BASIC_AUTH_USERNAME=
BASIC_AUTH_PASSWORD=
next.config.js
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で同じように設定

BASIC認証のユーザ名・パスワードをVercelのenvに設定



basicAuthCheckで.envの値を読み込むように変更

src/utils/basicAuthCheck.ts
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認証がかかるようになりました!

プロフィール画像
Yuki Takara
都内でフリーランスのエンジニアをやってます。フロントとアプリ開発メインに幅広くやってます。