如何从MSAL Browser获取授权码?当前返回undefined(Microsoft Graph API)
问题解决:MSAL Browser获取授权码返回undefined的修复方案
问题根源
你代码里loginResponse.code返回undefined,是因为MSAL Browser的loginPopup方法默认会自动把授权码交换成访问令牌/ID令牌,不会把原始授权码返回给你。如果要把授权码传给后端去交换令牌(尤其是要拿到刷新令牌,前端不能存这个),得调整MSAL配置和登录逻辑。
修复步骤
1. 调整MSAL实例配置
把MSAL实例改成**授权码流(PKCE)**配置,指定返回授权码:
const msalInstance = new PublicClientApplication({ auth: { clientId: "你的客户端ID", authority: "https://login.microsoftonline.com/common", redirectUri: "你的重定向地址", // 必须和Azure AD后台配置的一致 responseType: "code" // 关键:告诉MSAL返回授权码而非直接返回令牌 } });
2. 修改登录逻辑,手动捕获授权码
不用loginPopup(它会自动交换令牌),改用getAuthCodeUrl生成授权链接,通过弹窗打开并捕获授权码:
const loginAndGetAuthorizationCode = async () => { const loginRequest = { scopes: ["User.Read", "Mail.Read", "offline_access"], redirectUri: "你的重定向地址" // 和实例配置保持一致 }; try { // 生成授权URL const authCodeUrl = await msalInstance.getAuthCodeUrl(loginRequest); // 打开登录弹窗 const popupWindow = window.open(authCodeUrl, "_blank", "width=600,height=700"); if (!popupWindow) throw new Error("无法打开登录弹窗"); // 监听弹窗消息,获取授权码 const authCode = await new Promise<string>((resolve, reject) => { const messageListener = (event: MessageEvent) => { if (event.source === popupWindow && event.data.type === "msal:authCode") { window.removeEventListener("message", messageListener); popupWindow.close(); resolve(event.data.code); } }; window.addEventListener("message", messageListener); // 监听弹窗关闭,处理取消登录的情况 const interval = setInterval(() => { if (popupWindow.closed) { clearInterval(interval); window.removeEventListener("message", messageListener); reject(new Error("登录弹窗已关闭")); } }, 100); }); // 把授权码传给后端 await authorizeEmail(authCode); return authCode; } catch (error) { console.error("Login failed: ", error); } };
3. 处理重定向页面的授权码
在你配置的redirectUri对应的页面里,要捕获授权码并传给主窗口:
// 重定向页面的代码 window.addEventListener("load", async () => { const msalInstance = new PublicClientApplication({ auth: { clientId: "你的客户端ID", authority: "https://login.microsoftonline.com/common", redirectUri: "你的重定向地址" } }); const result = await msalInstance.handleRedirectPromise(); if (result && result.code) { // 把授权码发送给主窗口 window.opener?.postMessage({ type: "msal:authCode", code: result.code }, "*"); } });
4. 修正authorizeEmail函数
确保传递的是正确的授权码(不是ID令牌):
const authorizeEmail = async (authCode: string | undefined) => { if (!authCode) return; // 这里的token是你自身应用的用户令牌,用于验证后端请求合法性 const token = await getIdTokenClaims(); await axios.post( `${process.env.BASE_URL}/email/authorize/`, { code: authCode, // 现在传的是正确的授权码 deal_id: 22, }, { headers: { 'Content-Type': 'application/json', "Authorization": `Bearer ${token}`, }, } ); };
关键说明
offline_access权限必须加:只有加了这个,后端才能拿到刷新令牌,用于后续静默刷新访问令牌。- PKCE是自动处理的:MSAL会自动生成验证密钥,不用你手动处理,只要配置正确授权码流就行。
- 为什么不用
loginPopup:这个方法会自动完成授权码到令牌的交换,适合前端直接用令牌的场景;但要传授权码给后端,就必须手动走授权流程抓码。
内容的提问来源于stack exchange,提问作者WebDev
相关产品推荐
相关产品推荐

