ロゴテキスト ロゴ

    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
    都内でフリーランスのエンジニアをやってます。フロントとアプリ開発メインに幅広くやってます。