はじめに
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をプロバイダとしてセットアップすると以下のようになっているかと思います。
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を返すようにする
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
デフォルトの実装をそのままコピペしてきます。
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を取得したかったので、そちらを実装していきます。
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
も修正してください。