SMARTCAMP Engineer Blog

スマートキャンプ株式会社(SMARTCAMP Co., Ltd.)のエンジニアブログです。業務で取り入れた新しい技術や試行錯誤を知見として共有していきます。

Next.js と Auth0で認証機能実装してみた

こんにちは!スマートキャンプ、エンジニアの関口です!

私は現在BOXILと連携させる新規アプリケーションの開発に携わっております。 このアプリケーションは、バックエンドAPIはGo 、フロントエンドはTypeScript/Next.jsで構成されています。

このプロジェクトの中で私は認証機能を担当しました。認証機能はAuth0というIDaaSを利用して実装しています。同じ様な構成で認証機能を実装したいと考えている方の理解の助けになればと思い、 このアプリケーションの構成に寄せる形でNext.jsとAuth0を連携した認証機能を実装する方法を紹介します。

Next.jsとは

Next.jsとはReactをベースとしたWebアプリケーションフレームワークです。Next.jsはVercelというホスティングサービスを提供しているVercel社が提供しています。Next.jsはページ単位でHTMLの生成方法をSSRかSSGで選択できたり、ファイルベースのルーティングを備えています。 公式サイトにもあるように本番環境に備えたあらゆる機能を提供しています。

nextjs.org

Auth0とは

Auth0とはWebアプリやモバイルアプリ、WebAPIなどに対して認証・認可の機能を提供する認証基盤サービス(IDaaS)です。Auth0を利用することで開発中のアプリケーションに簡単に認証機能を組み込むことができます。連携できるソーシャルログイン の種類の豊富さや、ユーザー管理、ログ管理などのAuth0のコンソール画面の扱いやすさも魅力的です。

auth0-reactとは

今回auth0-reactというSDKを採用しました。Next.jsとAuth0を連携する場合のSDKの選択肢としてauth0-reactnextjs-auth0があります。どちらのSDKを採用するか判断するために、nextjs-auth0のドキュメントを参考にしました。

github.com

ドキュメントには以下の様なケースではnextjs-auth0よりもauth0-reactの方が適していると書かれています。

  • Next.jsでStatic HTML Exportを使用している場合

  • サーバーサイドレンダリング時にユーザーデータにアクセスする必要がない場合

  • Next.jsのAPI Routesをプロキシとして使用して外部APIを呼び出すのではなく、アクセストークンを取得してフロントエンドレイヤーから直接外部APIを呼び出したい場合

私達のアプリケーションではアクセストークンを取得してフロントエンドレイヤーから直接バックエンドのGo APIを呼び出すような設計にしていたので、auth0-reactを採用しました。

実装

Next.jsの環境構築

実装に入っていきます!

最初にNext.jsの環境構築をおこないます。Next.jsの公式ドキュメントを参考にNext.jsのアプリケーションを作成するためのコマンドを実行します。今回は「auth0-sample」という名前でアプリケーションを作成し、TypeScriptを採用するので、コマンドにオプションを追加します。

npx create-next-app auth0-sample --ts

アプリケーションの作成が成功したら、サーバーを立ち上げます。

npm run dev

サーバーを立ち上げてhttp://localhost:3000にアクセスしてください。すると以下の画像のような画面が表示されます。

Auth0の設定

Auth0の設定をおこないます。まずAuth0のアカウン登録の際にテナントドメインを設定します。

ApplicationsページのCreate Applicationからアプリケーションの名前とアプリケーションのタイプを選択し、アプリケーションを作成します。

アプリケーションが作成されると、Settings画面からシークレット情報の確認やログイン後のコールバックURLなどの各種設定ができるようになります。

認証機能の実装

続いてauth0-reactを利用してAuth0とNext.jsの連携をおこない、認証機能の実装をしていきます。

まずauth0-reactのinstallをおこないます。

npm install @auth0/auth0-react

続いてAuth0とNext.jsの連携をおこなうための設定を_app.tsxに記述します。

import type { AppProps } from 'next/app'
import { Auth0Provider } from '@auth0/auth0-react';

function MyApp({ Component, pageProps }: AppProps) {
    //ログイン後のリダイレクト先を指定
  const redirectUri = `${process.env["NEXT_PUBLIC_BASE_URL"]}/success`
  return(
          <Auth0Provider
              domain={process.env["NEXT_PUBLIC_AUTH0_DOMAIN"]}
              clientId={process.env["NEXT_PUBLIC_AUTH0_CLIENT_ID"]}
              redirectUri={redirectUri}
          >
              <Component {...pageProps} />
          </Auth0Provider>
      )

}
export default MyApp

リダイレクト先のURLやAuth0のドメイン、クライアントIDは環境変数で指定しています。

ログイン、ログアクト後にどのページにリダイレクトするかをAuth0のコンソール画面で設定します。ログイン後はAllowed Callback URLs、ログアウト後はAllowed Logout URLsに設定します。

認証ページ作成

続いて認証ページを作成します。

isAuthenticatedメソッドでログイン中のユーザーが存在するか判定し、存在する場合はログアウトボタンを、存在しない場合はログインボタンを表示させます。

//pages/index.tsx
import styles from '../styles/Home.module.css'
import { useAuth0 } from '@auth0/auth0-react';

export default function Home() {
    const {
        isAuthenticated,
        loginWithRedirect,
        logout,
        user
    } = useAuth0();

    return (
        <div className={styles.container}>
          <main className={styles.main}>
            <h1 className={styles.title}>
              Welcome to <a href="https://nextjs.org">Next.js!</a>
            </h1>
              {isAuthenticated && (
                  <div>
                     <p>{user?.name}</p>
                     <button onClick={() => logout()}>ログアウト</button>
                  </div>
              )}
              {!isAuthenticated && (
                  <div>
                      <p>ログイン</p>
                      <button onClick={() => loginWithRedirect()}>ログイン</button>
                  </div>
              )}
          </main>
        </div>
      )
}

次にログイン後に遷移するページを作成します。

// pages/success.tsx

import { useAuth0 } from '@auth0/auth0-react';
import styles from '../styles/Home.module.css'

export default function Home() {
    const {
        isAuthenticated,
        loginWithRedirect,
        logout,
        user
    } = useAuth0();

    return (
        <div className={styles.container}>
            <h1>
                Welcome to <a href="https://nextjs.org">Success Page!</a>
            </h1>
            {isAuthenticated && (
                <div>
                    <p>{user?.name}でログイン中</p>
                    <button onClick={() => logout()}>ログアウト</button>
                </div>
            )}
            {!isAuthenticated && (
                <div>
                    <button onClick={() => loginWithRedirect()}>ログイン</button>
                </div>
            )}
        </div>
    )
}

ログインするためにはユーザーが必要なので、Auth0のコンソール画面のUser Managementから ユーザーを作成しましょう。

以上によりログイン機能が実装できたので動作確認していきましょう。

サーバーを立ち上げてhttp://localhost:3000にアクセスしてください。以下のような画面が表示され、ログインボタンを押すとAuth0のログインページにアクセスできます。 ログインページで先程作成したユーザーのメールアドレスとパスワードを入力します。

Auth0のログイン画面

ログインに成功すると先程リダイレクト先として指定したサクセスページにリダイレクトされます。 Cookiesに「auth0.is.authenticated」というキーが生成され、値に「true」が代入されます。これによってユーザーがログインしているかどうかの判断をおこなっています。 以上がNext.jsとAuth0による認証機能になります。

まとめ

Next.jsアプリケーションにAuth0とSDKを利用することで簡単に認証機能が実装できることができました。 認証はアプリケーションを構築する上で欠かせない機能ですが、Auth0のようなIDaaSに頼ることでアプリケーションの肝となるロジックに開発を集中することができます。

今回のアプリケーションは以下のリポジトリにまとめました!興味のある方はご覧ください!

https://github.com/daichi1998928/nextjs-auth0-samplegithub.com

今回の記事がNext.jsとAuth0を利用して認証機能を実装したいと考えている方の助けになれば幸いです。最後まで読んでいただきありがとうございました!