mongolyyのブログ

開発(Javascript, Typescript, React, Next.js)や開発手法(スクラム, アジャイル)、勉強したことについて色々書ければと。

Next.js + TypeScript + NextAuth + AzureAD B2Cの組み合わせを試す

はじめに

認証基盤といえば手軽に使えるAuth0が好きですが、会社の状況によっては使えないことがあると思います。

NextAuthを使いつつ、色々なIdPを試してみようと思いますが、今回はナレッジが少なそうなAzureAD B2Cを試してみようと思います。

まずはいつもの

yarn create next-app --typescript

をします。

プロジェクトのディレクトリに移動し、

yarn add -D next-auth

します。

Azure AD B2Cの設定

docs.microsoft.com

docs.microsoft.com

docs.microsoft.com

をもとに設定します。設定するときに次の設定はどこかにコピペしておくようにします。

  • テナント(場所によってはディレクトリと表現)作成時に入力する、テナント名(場所によっては組織名と表現)
  • 登録したアプリのアプリケーション名(Azureによって採番された、a90abcd...のようなuuid形式のようなID)
  • 登録したアプリに対するクライアントシークレットの値
  • ユーザーフローの名前

アプリのリダイレクトURIは次の値を設定します。

  • https://jwt.ms
  • http://localhost:3000/api/auth/callback/azure-ad-b2c
  • https://<本番環境のドメイン>/api/auth/callback/azure-ad-b2c

ユーザーフローの「ユーザー属性とトークン要求」では次のように設定します。

.env.localの設定

AZURE_AD_B2C_TENANT_NAME=<テナント名>
AZURE_AD_B2C_CLIENT_ID=<アプリケーション名>
AZURE_AD_B2C_CLIENT_SECRET=<クライアントシークレットの値>
AZURE_AD_B2C_PRIMARY_USER_FLOW=<ユーザーフローの名前>

NEXTAUTH_SECRET=<任意のランダム文字列>

公式ドキュメントで紹介されていますが、NEXTAUTH_SECRETopenssl rand -base64 32 コマンドで作ると良さそうです。 Options | NextAuth.js

Providerの設定

ドキュメントをもとに次のようなファイルを作成しました。

import AzureADB2CProvider from "next-auth/providers/azure-ad-b2c"
import NextAuth from 'next-auth'

export default NextAuth({
  providers: [
    AzureADB2CProvider({
      tenantId: process.env.AZURE_AD_B2C_TENANT_NAME,
      clientId: process.env.AZURE_AD_B2C_CLIENT_ID,
      clientSecret: process.env.AZURE_AD_B2C_CLIENT_SECRET,
      primaryUserFlow: process.env.AZURE_AD_B2C_PRIMARY_USER_FLOW,
      authorization: { params: { scope: "offline_access openid" } },
    }),
  ]
})

IDEで次のようなwarningが表示されました。

process.env.AZURE_AD_B2C_TENANT_NAMEstring | undefined であることにかかわらず、providerのオプションの tenantIdstring になっているせいで発生しています。
process.env.AZURE_AD_B2C_TENANT_NAME が設定されていないときにアプリは起動したくないので、起動時にチェックすることで、tenantId としては必ず string が渡されるようにします。

起動時のチェックは、次の記事を参考にしました。

zenn.dev

結果、次のような実装になります。

import AzureADB2CProvider from "next-auth/providers/azure-ad-b2c"
import NextAuth from 'next-auth'

if (!process.env.AZURE_AD_B2C_TENANT_NAME || !process.env.AZURE_AD_B2C_CLIENT_ID || !process.env.AZURE_AD_B2C_CLIENT_SECRET || !process.env.AZURE_AD_B2C_PRIMARY_USER_FLOW) {
  console.error('Azure AD B2Cの環境変数が設定されていません!')
  process.exit()
}

export default NextAuth({
  providers: [
    AzureADB2CProvider({
      tenantId: process.env.AZURE_AD_B2C_TENANT_NAME,
      clientId: process.env.AZURE_AD_B2C_CLIENT_ID,
      clientSecret: process.env.AZURE_AD_B2C_CLIENT_SECRET,
      primaryUserFlow: process.env.AZURE_AD_B2C_PRIMARY_USER_FLOW,
      authorization: { params: { scope: "offline_access openid" } },
    }),
  ]
})

認証をmiddlewareで実装する

Next.jsで最近追加されたmiddlewareで認証を実装します。 nextjs.org

export { default } from "next-auth/middleware"

これだけです!

トップページでユーザー名を表示できるようにする

_app.tsxSessionProvider コンポーネントを追加します。

import '../styles/globals.css'
import type { AppProps } from 'next/app'
import { SessionProvider } from 'next-auth/react'

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <SessionProvider>
      <Component {...pageProps} />
    </SessionProvider>
  )
}

export default MyApp

トップページで表示名を表示するようにします。

import type { NextPage } from 'next'
import styles from '../styles/Home.module.css'
import { useSession } from 'next-auth/react'

const Home: NextPage = () => {
  const { data: session, status } = useSession()

  if (status === 'loading') {
    return (
      <div className={styles.container}>
        <main className={styles.main}>
          <h1 className={styles.title}>
            読み込み中
          </h1>
        </main>
      </div>
    )
  }

  if (status === 'unauthenticated') {
    return (
      <div className={styles.container}>
        <main className={styles.main}>
          認証に失敗しました
        </main>
      </div>
    )
  }

  return (
    <div className={styles.container}>
      <main className={styles.main}>
        <h1 className={styles.title}>
          Welcome {session?.user?.name}!!
        </h1>
      </main>
    </div>
  )
}

export default Home

動作確認

next dev で起動し、 http://localhost:3000 にアクセスしてみると、、

サインイン用の画面が出ました!
(Providerが一つしかないのは、もの寂しいですね、、)

ボタンをクリックするとサインイン画面が表示されます

Sign up now」からユーザーを作ってサインインします。 (うまくいかないときは他のログインが残っていることがあるので、シークレットブラウザで試すといいかも)

サインインできました!!

おわりに

今回、デフォルトの設定に従って、プロバイダの選択画面とAzure AD B2Cサインイン画面を表示していましたが、next-auth/reactsignIn 関数を呼び出すことによって、この画面(プロバイダの選択画面)をスキップするという技もあります。

github.com

普通に使えますが、標準でデフォルトのプロバイダが選べるようになるといいなと、思います。

github.com

あと、個人的にはAzureの独特の日本語に苦しめられました。。笑

今回のコードのリポジトリはこちらです。 github.com