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

2021.07.10
2021.07.22
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だと実行出来るため、まだまだ安定しないところがありそうです

下準備

プロジェクトの作成

$ npx create-next-app --ts
✔ What is your project named? … nextjs-basic-auth-sample

表示される事を確認

$ yarn run dev




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

pages/hello.tsx
import React, { VFC } from 'react'

const Hello: VFC = () => {
  return <h1>hello page</h1>
}

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



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を追加

参考用として作っているプロジェクトのため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で同じように設定




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認証がかかるようになりました!

おすすめ