2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

NextAuth.jsでAzure AD認証する際にprofile情報を追加したい

Last updated at Posted at 2023-10-15

はじめに

NextAuth.jsでAzure AD (Azure Active Directory) をプロバイダとして使用する際に、profile情報にいくつか値を追加したかったのですが、やり方が分からず困ったので、調べたこととやり方を記載します。
NextAuth.jsの説明やAzure ADをプロバイダとして利用する方法などはこの記事では詳しく書きません。詳しくはドキュメントを参照ください。

環境

Next.js: v13.4.9
NextAuth.js: v4.22.1

profile情報を追加する

まずAzure ADをプロバイダとしてセットアップすると以下のようになっているかと思います。

pages/api/auth/[...nextauth].js
import NextAuth from 'next-auth'
import AzureADProvider from "next-auth/providers/azure-ad";

export default NextAuth({
  providers: [
    AzureADProvider({
      clientId: process.env.AZURE_AD_CLIENT_ID,
      clientSecret: process.env.AZURE_AD_CLIENT_SECRET,
      tenantId: process.env.AZURE_AD_TENANT_ID,
    })
  ],
  // その他の設定...
})

デフォルトではname email imageをprofile情報として受け取っているようです。
ただしemailはAzure Adのメールプロパティになるので、注意が必要です。メールアドレスは必須ではないですが、upn (User Principal Name)をメールアドレスとして利用したかったので、編集していきます。

profileオプションをカスタマイズしてupnを返すようにする

pages/api/auth/[...nextauth].js
export default NextAuth({
  providers: [
    AzureADProvider({
      clientId: process.env.AZURE_AD_CLIENT_ID,
      clientSecret: process.env.AZURE_AD_CLIENT_SECRET,
      tenantId: process.env.AZURE_AD_TENANT_ID,
      async (profile) => {
        return {
          id: profile.id,
          name: profile.name,
          email: profile.preferred_username,  // upnをメールアドレスとして利用
        }
      }
    })
  ],
  // その他の設定...
})

この profile 関数は、Azure AD から返されるプロファイル情報を受け取り、next-auth.js が期待する形式のプロファイルオブジェクトを返します。
ただ profile 情報を確認したところupnという値はなく、preferred_nameというプロパティがあり、こちらにupnの値が入っていました。Azure AD において、preferred_usernameは通常、ユーザーのupnを指すようなので、こちらをメールアドレスとして利用します。

ただ profile オブジェクトをカスタマイズしたので、アバター画像の取得ができなくなってしまいました。画像は同様に取得したかったので、デフォルトの profile オプションがどうなっているか調べてみました。

どうやら画像は graph API で別途取得しているようです。
https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/azure-ad.ts

デフォルトの実装をそのままコピペしてきます。

pages/api/auth/[...nextauth].js
export default NextAuth({
  providers: [
    AzureADProvider({
      clientId: process.env.AZURE_AD_CLIENT_ID,
      clientSecret: process.env.AZURE_AD_CLIENT_SECRET,
      tenantId: process.env.AZURE_AD_TENANT_ID,
      async profile(profile, tokens) {
          // https://docs.microsoft.com/en-us/graph/api/profilephoto-get?view=graph-rest-1.0#examples
          const response = await fetch(
            `https://graph.microsoft.com/v1.0/me/photos/${profilePhotoSize}x${profilePhotoSize}/$value`,
            { headers: { Authorization: `Bearer ${tokens.access_token}` } }
          )
    
          // Confirm that profile photo was returned
          let image
          // TODO: Do this without Buffer
          if (response.ok && typeof Buffer !== "undefined") {
            try {
              const pictureBuffer = await response.arrayBuffer()
              const pictureBase64 = Buffer.from(pictureBuffer).toString("base64")
              image = `data:image/jpeg;base64, ${pictureBase64}`
            } catch {}
          }
    
          return {
            id: profile.sub,
            name: profile.name,
            email: profile.preferred_username,  // upnをメールアドレスとして利用
            image: image ?? null,
          }
      },
    },
  ],
  // その他の設定...
})

必要なプロパティを追加する

もともとやりたかったこととして従業員IDを取得したかったので、そちらを実装していきます。

pages/api/auth/[...nextauth].js
export default NextAuth({
  providers: [
    AzureADProvider({
      clientId: process.env.AZURE_AD_CLIENT_ID,
      clientSecret: process.env.AZURE_AD_CLIENT_SECRET,
      tenantId: process.env.AZURE_AD_TENANT_ID,
      async profile(profile, tokens) {
        const [imageResponse, employeeIdResponse] = await Promise.all([
          fetch(
            `https://graph.microsoft.com/v1.0/me/photos/${profilePhotoSize}x${profilePhotoSize}/$value`,
            { headers: { Authorization: `Bearer ${tokens.access_token}` } }
          ),
          fetch(
            `https://graph.microsoft.com/v1.0/me>$select=employeeId`,
            { headers: { Authorization: `Bearer ${tokens.access_token}` } }
          )
        ])

    
        // Confirm that profile photo was returned
        let image
        // TODO: Do this without Buffer
        if (imageResponse.ok && typeof Buffer !== "undefined") {
          try {
            const pictureBuffer = await imageResponse.arrayBuffer()
            const pictureBase64 = Buffer.from(pictureBuffer).toString("base64")
            image = `data:image/jpeg;base64, ${pictureBase64}`
          } catch {}
        }

        const { employeeId } = await employeeIdResponse.json()
    
        return {
          id: profile.sub,
          name: profile.name,
          email: profile.preferred_username,  // upnをメールアドレスとして利用
          image: image ?? null,
          employeeId
        }
      },
    },
  ],
  // その他の設定...
})

できました。

TypeScriptを利用していて、プロパティを追加する場合は型の修正も必要になるかと思います。必要に応じてnext-auth.d.tsも修正してください。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?