はじめに
認証基盤といえば手軽に使えるAuth0が好きですが、会社の状況によっては使えないことがあると思います。
NextAuthを使いつつ、色々なIdPを試してみようと思いますが、今回はナレッジが少なそうなAzureAD B2Cを試してみようと思います。
まずはいつもの
yarn create next-app --typescript
をします。
プロジェクトのディレクトリに移動し、
yarn add -D next-auth
します。
Azure AD B2Cの設定
をもとに設定します。設定するときに次の設定はどこかにコピペしておくようにします。
- テナント(場所によってはディレクトリと表現)作成時に入力する、テナント名(場所によっては組織名と表現)
- 登録したアプリのアプリケーション名(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_SECRET
は openssl 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_NAME
は string | undefined
であることにかかわらず、providerのオプションの tenantId
が string
になっているせいで発生しています。
process.env.AZURE_AD_B2C_TENANT_NAME
が設定されていないときにアプリは起動したくないので、起動時にチェックすることで、tenantId
としては必ず string
が渡されるようにします。
起動時のチェックは、次の記事を参考にしました。
結果、次のような実装になります。
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.tsx
で SessionProvider
コンポーネントを追加します。
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/react
の signIn
関数を呼び出すことによって、この画面(プロバイダの選択画面)をスキップするという技もあります。
普通に使えますが、標準でデフォルトのプロバイダが選べるようになるといいなと、思います。
あと、個人的にはAzureの独特の日本語に苦しめられました。。笑
今回のコードのリポジトリはこちらです。 github.com