[Auth0]Next.jsで生成した静的ページに認証を追加してみた

2023.11.28

今さら…というツッコミが聞こえてきそうですがちょっと待ってください。

React.js/Auth0はAuth0の公式にサンプルがありますが、今回はNext.jsを選択しています。
そして、 Next.js/Auth0もAuth0公式にサンプルがあるのですがこれはSSRのサンプルであり、SSGのサンプルはありません。

このNext.js/Auth0/SSGのパターンは、ググっても記事が無くハマりどころがあったのでまとめていきます。

本記事を通してNext.js/Auth0/SSGの構成で認証認可の実装を体験できます。
想定所要時間は5〜10分です。

補足

  • 基礎の組み合わせの話なので、React.js/Next.jsをちゃんと理解している方はハマらないと思います。
  • 本記事は、最終的にはCloudFront/S3の静的サイトホスティングを利用する場合を想定しています。

それでは、はじめていきましょう。

Next.jsプロジェクト作成

npx create-next-app@latest

v13からApp Routerが導入されたようですね。

Auth0 React SDKインストール

npm i @auth0/auth0-react

注意すべきはnextjs-auth0ではないことです。

nextjs-auth0公式ドキュメントにも記載があります。

You should be aware of the security implications of both models. However, [auth0-react](https://github.com/auth0/auth0-react) may be more suitable for your needs if you meet any of the following scenarios:

- You are using [Static HTML Export](https://nextjs.org/docs/advanced-features/static-html-export) with Next.js. - You do not need to access user data during server-side rendering.

実装

SDKをインストールしたら、まずはauth0-reactの公式サンプルを参考に実装してみます。
以下の様になると思います。

Auth0Provider

app/layout.js

<Auth0Provider
  domain="YOUR_AUTH0_DOMAIN"
  clientId="YOUR_AUTH0_CLIENT_ID"
  authorizationParams={{
    redirect_uri: window.location.origin,
  }}
>
	{children}
</Auth0Provider>

useAuth0

app/page.js

const { isLoading, isAuthenticated, error, user, loginWithRedirect, logout } = useAuth0();
  if (isLoading) {
    return <div>Loading...</div>;
  }
  if (error) {
    return <div>Oops... {error.message}</div>;
  }

  if (isAuthenticated) {
    return (
        <div>
          Hello {user.name}{' '}
          <button onClick={() => logout({ logoutParams: { returnTo: window.location.origin } })}>
            Log out
          </button>
        </div>
    );
  } else {
    return <button onClick={() => loginWithRedirect()}>Log in</button>;
  }

では、これで確認してみましょう。

エラー発生

Server Error
Error: (0 , react__WEBPACK_IMPORTED_MODULE_0__.createContext) is not a function
This error happened while generating the page. Any console logs will be displayed in the terminal window.

はい、何やらauth0-reactに問題があるようなエラーメッセージです。これをChatGPTに問いかけると、reactとreact-domを最新化せよと返ってくるのですが、既に最新です。

※本記事を投稿した時点では

"dependencies": {
    "@auth0/auth0-react": "^2.2.3",
    "next": "14.0.3",
    "react": "^18",
    "react-dom": "^18"
  },

なので最新化してもエラーは解消しません。ググってもそれらしい記事はありません。

エラー解消

以下をlayout.jsとpage.jsの先頭に追加するだけです。

'use client'

考察

まずNext.jsには React Server ComponetReact Client Components が存在します。

App Routerを使用したNext.jsは、デフォルトでServer Componentを使用しサーバーサイドレンダリングを行います。
逆にクライアントサイドのレンダリングがしたい場合、Client Componentを使用します。そのために、コンポーネントファイルの先頭にuse clientと書く必要があります。

一方、Auth0Providerは内部でuseStateを使用しています。

ここで、以下のServer Component/Client Componentのユースケースを見てみましょう。

https://nextjs.org/docs/app/building-your-application/rendering/composition-patterns#when-to-use-server-and-client-components

こちらにあるようにuseStateを使用する場合はClient Componentでなければならないのでuse clientと書けば良いというわけです。

補足

  • 本パターンでのOIDCの認証フローは、認可コードフロー+PKCEです。
  • SPA(CSR)はパブリッククライアントなので、インプリシットフローだと勘違いしがちです(私のことです)が、クライアントがパブリックか否かはフローとは関係ありません
  • そして、インプリシットフローは既に非推奨です。今はパブリッククライアントでもPKCEで認可コード横取りの耐性が向上させた認可コードフローが推奨されています
  • React.js/Auth0はちゃんと最新のプロトコルに則っているんですね

以上です、どなたかのお役に立てば!