You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NextAuth Azure AD B2C中Access Token未定义问题(NextJS Pages TypeScript)

问题:无法获取Azure AD B2C的Access Token并附加到NextAuth Session中

我一直尝试将access token附加到session中,但无法获取access token的值,不确定自身配置是否正确。以下是我的实现代码:

import util from "node:util";
import NextAuth, { AuthOptions } from "next-auth";
import AzureADB2CProvider from "next-auth/providers/azure-ad-b2c";

export const authOptions: AuthOptions = {
    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" },
            },
            token: {
                params: { scope: "openid profile user.Read.All email" },
            },

            // Attach UserProfileID into the Session Profile while keeping defaults
            profile(profile) {
                return {
                    id: profile.sub,
                    name: profile.name,
                    email: profile?.emails?.[0],
                    userProfileID: profile?.extension_UserProfileID,
                    image: null,
                };
            },
        }),
    ],
    
    callbacks: {
        async signIn({ profile }) {
            // It's a newly created account when extension_UserProfileID does not exist
            if (profile && !("extension_UserProfileID" in profile)) {
                return "/?newUser=1";
            } else {
                return true;
            }
        },
        async jwt({ token, user, account, profile }) {
            // I should get the value for access token in the account
            console.log("account", account);
            // On JWT Token, Attach id_token from account - which exists only on SignIn
            if (account && user) {
                token.id_token = account.id_token;
                token.userProfileID = user.userProfileID;
            }

            return token;
        },
        async session({ session, token }) {
            // Expose userProfileID unto the Session
            if ("userProfileID" in token) {
                session.user.userProfileID = Number(token.userProfileID);
            }

            // Include the ID Token in the Session
            if ("id_token" in token) {
                session.user.idt = String(token.id_token ?? "");
            }

            // To do: Dynamically load Role based on API
            session.user.role = "ServiceRequest";

            return session;
        },
    },

    logger: {
        error(code, metadata) {
            console.error(code, metadata);
        },
        warn(code) {
            console.warn(code);
        },
        debug(code, metadata) {
            console.debug(code, util.inspect(metadata, true, null, true));
        },
    },
};

export default NextAuth(authOptions);

AD B2C中的access token配置已开启隐式授权和混合流。

控制台输出的account对象如下:

account: {
    provider: 'azure-ad-b2c',
    type: 'oauth',
    providerAccountId: '8e22.......',
    id_token: 'eyJraW.......',
    token_type: 'Bearer',
    not_before: 1705946179,
    id_token_expires_in: 3600,
    profile_info: 'eyJraW.......',
    scope: 'offline_access openid',
    refresh_token: 'eyJraW.......',
    refresh_token_expires_in: 86400
  }

我已经尝试过相关解决方案,但问题仍未解决。


解决方案

1. 修正Scope配置,确保请求Access Token

当前配置中,authorization和token的scope未包含目标API的权限范围,Azure AD B2C仅在请求具体API权限时才会返回access token。

修改AzureADB2CProvider的scope配置:

AzureADB2CProvider({
    // ...其他配置
    authorization: {
        params: { 
            scope: "offline_access openid https://你的租户名.onmicrosoft.com/你的API标识/API权限范围" 
        },
    },
    token: {
        params: { 
            scope: "offline_access openid https://你的租户名.onmicrosoft.com/你的API标识/API权限范围" 
        },
    },
    // ...其他配置
})

替换示例中的API权限范围为你实际的配置(可在Azure AD B2C应用注册的API权限页获取)。

2. 在JWT回调中捕获Access Token

account对象中的access_token字段即为所需值,需将其存入token对象,再传递到session:

更新jwt回调:

async jwt({ token, user, account, profile }) {
    if (account && user) {
        token.id_token = account.id_token;
        token.userProfileID = user.userProfileID;
        // 新增:保存access token到token对象
        token.access_token = account.access_token;
        token.access_token_expires = account.expires_at; // 可选:保存过期时间
    }

    // 可选:处理access token过期,用refresh_token刷新
    if (Date.now() < (token.access_token_expires as number) * 1000) {
        return token;
    }
    // 此处可添加刷新逻辑,调用NextAuth的refreshAccessToken方法
    return token;
}

3. 将Access Token附加到Session

更新session回调,把access token加入session:

async session({ session, token }) {
    if ("userProfileID" in token) {
        session.user.userProfileID = Number(token.userProfileID);
    }

    if ("id_token" in token) {
        session.user.idt = String(token.id_token ?? "");
    }

    // 新增:将access token加入session
    if ("access_token" in token) {
        session.user.access_token = String(token.access_token ?? "");
    }

    session.user.role = "ServiceRequest";

    return session;
}

4. 检查Azure AD B2C应用配置

  • 确认应用注册已启用授权码流(因使用client_secret,后端服务更适合授权码流而非隐式流)
  • 确保API权限已正确添加并完成管理员同意
  • 验证回复URL配置与NextAuth的回调地址完全一致

内容的提问来源于stack exchange,提问作者Jedd Niñonuevo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 12:30:19