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

如何通过Azure AD B2C混合流获取客户端已登录用户的access_token?

问题:Azure AD B2C保护的ASP.NET Core应用客户端获取已登录用户的Access Token

场景描述

我有一个受Azure AD B2C认证保护的ASP.NET Core 7 Web应用,服务端认证功能正常,可通过AuthorizeForScopes属性和ITokenAcquisition的GetAuthenticationResultForUserAsync方法获取access_token。现在需要在客户端获取用户的access_token,以便直接用JavaScript调用Web API,无需用户重新登录。

当前客户端代码

<script src="https://alcdn.msauth.net/browser/2.5.1/js/msal-browser.js"></script>

<script type="module">

    const msalInstance = new msal.PublicClientApplication({
        auth: {
            clientId: "bbd0f8e1-a81a-43c6-82d8-xxxxxx",
            authority: "https://login.microsoftonline.com/cfc1d2e8-3626-4dca-9cd7-xxxxxx",
        },
        cache: {
            cacheLocation: "localStorage",
            storeAuthStateInCookie: false,
        }
    });

    const silentRequest = {
        scopes: ["https://xxxxxx.onmicrosoft.com/webapi/access_as_user"],
        loginHint: "me@email.com"
    };

    const loginResponse = await msalInstance.ssoSilent(silentRequest);


</script>

配置与错误信息

  • Azure AD B2C配置:已勾选“管理>认证>隐式授权和混合流>ID令牌(用于隐式和混合流)”
  • 问题:ssoSilent方法已被废弃,运行代码时出现错误:Unsafe attempt to initiate navigation for frame with origin xxx to the /authorize endpoint,代码尝试重新认证用户而非从Cookie获取会话信息。

解决方案

一、错误原因分析

  1. ssoSilent方法已被MSAL官方废弃,存在会话同步问题
  2. Azure AD B2C隐式流配置不全,未勾选访问令牌(仅勾选了ID令牌,无法获取access_token)
  3. MSAL实例的authority格式错误(Azure AD B2C应使用b2clogin.com域名,而非login.microsoftonline.com)
  4. 未先检查本地缓存中的已登录账户,直接发起静默请求导致触发导航

二、修正后的客户端实现(使用MSAL.js v3.x)

1. 调整Azure AD B2C配置

在应用注册的认证>隐式授权和混合流中,同时勾选:

  • ID令牌(用于隐式和混合流)
  • 访问令牌(用于隐式流)

2. 更新MSAL.js代码

<script src="https://alcdn.msauth.net/browser/3.14.0/js/msal-browser.min.js"></script>

<script type="module">
    // 初始化MSAL实例,注意B2C的authority格式
    const msalInstance = new msal.PublicClientApplication({
        auth: {
            clientId: "bbd0f8e1-a81a-43c6-82d8-xxxxxx",
            // 替换为你的B2C租户、租户ID/域名和用户流名称
            authority: "https://your-tenant.b2clogin.com/your-tenant.onmicrosoft.com/B2C_1_signupsignin",
            redirectUri: window.location.origin // 需与Azure AD B2C配置的重定向URI一致
        },
        cache: {
            cacheLocation: "localStorage",
            storeAuthStateInCookie: true // 跨域或旧浏览器场景建议开启
        }
    });

    async function getAccessToken() {
        let accessToken = null;
        // 先检查本地缓存中是否存在已登录账户
        const accounts = msalInstance.getAllAccounts();
        
        if (accounts.length > 0) {
            const request = {
                scopes: ["https://xxxxxx.onmicrosoft.com/webapi/access_as_user"],
                account: accounts[0] // 使用已缓存的账户发起请求
            };

            try {
                // 使用推荐的acquireTokenSilent替代ssoSilent
                const response = await msalInstance.acquireTokenSilent(request);
                accessToken = response.accessToken;
                console.log("成功获取Access Token:", accessToken);
                // 在此处调用你的Web API
            } catch (error) {
                // 静默请求失败(比如Token过期),触发交互式请求
                if (error instanceof msal.InteractionRequiredAuthError) {
                    try {
                        const response = await msalInstance.acquireTokenPopup(request);
                        accessToken = response.accessToken;
                    } catch (popupError) {
                        console.error("弹出式认证失败:", popupError);
                    }
                } else {
                    console.error("获取Token失败:", error);
                }
            }
        } else {
            console.error("未检测到已登录账户,请先通过服务端完成认证");
        }
        return accessToken;
    }

    // 调用函数获取Token
    getAccessToken();
</script>

三、更简单的替代方案:服务端传递Token到客户端

如果不需要客户端自主刷新Token,可直接在服务端获取Token后传递到视图:

服务端控制器代码

using Microsoft.Identity.Web;
using Microsoft.AspNetCore.Mvc;

public class HomeController : Controller
{
    private readonly ITokenAcquisition _tokenAcquisition;

    public HomeController(ITokenAcquisition tokenAcquisition)
    {
        _tokenAcquisition = tokenAcquisition;
    }

    public async Task<IActionResult> Index()
    {
        // 获取指定 scope 的 access_token
        var authResult = await _tokenAcquisition.GetAuthenticationResultForUserAsync(
            new[] { "https://xxxxxx.onmicrosoft.com/webapi/access_as_user" });
        
        ViewData["AccessToken"] = authResult.AccessToken;
        return View();
    }
}

客户端视图代码

<script>
    // 从服务端视图数据中获取Token
    const accessToken = '@ViewData["AccessToken"]';
    
    // 使用Token调用Web API示例
    async function callWebApi() {
        const response = await fetch("https://your-web-api-url/api/resource", {
            headers: {
                "Authorization": `Bearer ${accessToken}`
            }
        });
        const data = await response.json();
        console.log("API响应:", data);
    }

    callWebApi();
</script>

内容的提问来源于stack exchange,提问作者fred_

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 03:10:16