【Next.js】Auth0を認証に使用する場合のメモ


Next.jsで、認証のプラットフォームにAuth0を用いる際のメモ。概ね[ここに書いてある](https://github.com/auth0-samples/auth0-nextjs-samples/tree/main/Sample-01)のだが…自分用の備忘録として。なお、Auth0はFreeでもいいのでなんらか既にアカウントを持っていることを前提とする。

# Auth0 - Create Application

「Regular Web Applications」を選ぶ。Next.jsだと「Single Web Applications」とか選びたくなるが、それではない。

![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/392942/12530a6f-8f8f-c314-0009-ab76c5b71666.png)

# Auth0 - Settingsタブ

- 「Application URLs」のコーナーにおいて、「Allowed Callback URLs」に`http://localhost:3000/api/auth/callback`をいれる。これはローカルでNext.js立ち上げたときのコールバックURL。ポートはデフォの3000にしてるけど環境によって変えてる場合は要変更。他環境の分いれるときは、カンマ(`,`)区切りでこの後URLを続けて書いていく。間にスペースとかは特に不要。基本的に、各URLの末尾に`/api/auth/callback`入れとけばよい。
- 「Allowed Logout URLs」に`http://localhost:3000/`をいれる。これもローカルでNext.js立ち上げたときのコールバックURL。ポート変えてる場合は同様にポート要変更。上と同じようにカンマ(`,`)区切りで複数のURLを指定できる。各URLのルートにあたるパス`/`を指定しておけばとりあえずOKらしい

![02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/392942/2bcfdfdf-fb63-d7ff-af0c-181efa46b992.png)

# 環境変数の準備

- ローカルだと`.env.local` をつくって以下のような内容を書き込めばよい。どういう値にすればいいとかそういう細かい話は[GitHubで説明されている](https://github.com/auth0-samples/auth0-nextjs-samples/tree/main/Sample-01#configure-credentials)。ただ、Auth0でアプリケーション作ったあと、「Quick Start」タブの「Configure the SDK」っていう場所にほぼそのままコピペ可能な内容で（つくったアプリケーションのClient IDやClient Secret等がそのまま `.env` の形式で使用できる形で）載ってるので、それこのままコピペしたほうが良い。(画像参照)
- GitHubにも書いてあるが、唯一 `AUTH0_SECRET` だけは自分で用意しなければならない。コマンドは指示されていて、`openssl rand -hex 32`を実行して出力された値をコピって貼り付ければよいだけである。

![03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/392942/519b4249-45cf-65a0-294f-d48c729c340f.png)

# /pages/api/auth/[...auth0].js

- 各プロジェクトに`/pages/api/auth/[...auth0].js` というファイルを用意する。内容のサンプルは[Auth0がGitHubに載せている](https://github.com/auth0-samples/auth0-nextjs-samples/blob/main/Sample-01/pages/api/auth/%5B...auth0%5D.js)。超スッキリしてるけどこれだけで認証部分は動く。一応。
- ただ逆に言うとこれは「最低限の動作」をするようにしか書かれていないので、これだけだとあんまりイケてなかったりする。例えばログインした直後に同じページにまた戻ってくるので、「ログイン後にポータル（的な）画面に遷移させたいなあ」とか、そういうもうちょっと複雑な動きさせたいなーとか思ったらこれに少し手を加える必要がある。これは`handleLogin`で`returnTo`にパスを指定することで実現できる。（[参考](https://community.auth0.com/t/redirecting-to-another-page-other-than-using-nextjs-auth0/66920)）こんなかんじ↓
  ```Typescript:/pages/api/auth/[...auth0].js
  import { handleAuth , handleLogin, handleLogout } from '@auth0/nextjs-auth0';

  export default handleAuth({
      async login(req, res) {
        await handleLogin(req, res, {
          returnTo: "/portal",
          authorizationParams: {
            scope: 'openid profile email read:products', 
          }
        });
      },
      async logout(req,res) {
        await handleLogout(req,res , {
          returnTo: '/',
        });
      },
  });
  ```

# ログイン判定

`@auth0/nextjs-auth0/client`から`useUser`をimportして、`user`オブジェクトの有無で切り分けできる。各画面でやるっていうよりは、共通のコンポーネント（Layoutとか）に仕込んでおいて、どの画面でも未ログインだったりセッション切れてたらログイン促すような感じにしておいた方がいいのではないかと思う。（私は個人的にそうやってます）なお、ログインは`/api/auth/login`にリンクさせれば、（環境変数や`/pages/api/auth/[...auth0].js`がちゃんと整備されている前提であれば）それでそのままAuth0のログイン画面に飛んでいく。ので、`user`オブジェクトがNullの場合は、`Link`でも用意してここに遷移させればいい。こんなかんじ↓

```Typescript:/components/layaout.tsx
import { useUser } from '@auth0/nextjs-auth0/client';
import { ReactNode } from "react";

type Props = {
  children?: ReactNode;
};

const Layout = ({ children }: Props) => {
  const { user, error, isLoading } = useUser();
  return (
    <>
        {user ? <div>
                    <p>こんにちは {user.email}</p>
                    {children}
                </div>
             : <div>
                    <Link href="/api/auth/login">
                        ログインする
                    </Link>
                </div>
        }
    </>
  );
};


export default Layout;
```

# ログインが必要なAPI

ログイン後の画面操作でAPIを呼び出す際に、「ログインしていること」を前提とするAPIを作りたいときは、`@auth0/nextjs-auth0`から`withApiAuthRequired`というfunctionをimportして、これでfunctionをラップする。（[参考](https://auth0.github.io/nextjs-auth0/modules/helpers_with_api_auth_required.html)）こんなかんじ↓

```Typescript:/pages/api/auth-api-test.ts
import type { NextApiRequest , NextApiResponse } from 'next';
import { withApiAuthRequired, } from '@auth0/nextjs-auth0';

const authApiTest = withApiAuthRequired(function api (req: NextApiRequest, res: NextApiResponse){
    const test = String(req.query.test);
    res.status(200).json({text: test});
});

export default authApiTest;
```

このAPIをログインしないで叩くと以下のようなレスポンスが返ってくる。HTTP statusは`401 Unauthorized`。

```json
{"error":"not_authenticated","description":"The user does not have an active session or is not authenticated"}
```

# 環境

```json
    "@auth0/nextjs-auth0": "^2.3.1",
    "next": "13.2.4",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "typescript": "5.0.2"
```