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

Azure B2C IDP直通内部令牌无法刷新,调用Graph API失败求助

解决Azure B2C IDP直通模式下内部AD令牌自动刷新问题

在Blazor WASM应用中使用Azure B2C的IDP直通功能,搭配多AD自定义策略配置内部身份提供商时,会遇到这个问题:外层B2C的JWT Bearer令牌过期后能被MSAL自动刷新,但内部AD返回的idp_access_token过期后,B2C不会自动更新它。这直接导致依赖该令牌调用Microsoft Graph API发送邮件的功能失败,甚至引发站点崩溃。

以下是具体解决方案:


一、调整Azure B2C自定义策略,支持内部IDP令牌刷新

  1. 在自定义策略的RelyingParty节点中,添加idp_refresh_token到输出声明,确保B2C返回内部IDP的刷新令牌:
<RelyingParty>
  <DefaultUserJourney ReferenceId="SignUpOrSignIn" />
  <TechnicalProfile Id="PolicyProfile">
    <DisplayName>PolicyProfile</DisplayName>
    <Protocol Name="OpenIdConnect" />
    <OutputClaims>
      <!-- 保留原有声明 -->
      <OutputClaim ClaimTypeReferenceId="idp_access_token" />
      <OutputClaim ClaimTypeReferenceId="idp_refresh_token" /> <!-- 新增刷新令牌声明 -->
    </OutputClaims>
    <SubjectNamingInfo ClaimType="sub" />
  </TechnicalProfile>
</RelyingParty>
  1. 确保内部AD的技术配置文件维持IDP会话,在对应TechnicalProfile中添加会话管理引用:
<TechnicalProfile Id="AADMultiTenant-OpenIdConnect">
  <!-- 保留原有配置 -->
  <UseTechnicalProfileForSessionManagement ReferenceId="SM-SocialLogin" />
</TechnicalProfile>

二、通过MSAL.NET自动管理内部IDP令牌的刷新

不要直接从HttpContext.User的Claims中提取idp_access_token,而是利用MSAL的令牌缓存和On-Behalf-Of流程,让它自动处理令牌的获取与刷新:

修改后端API的令牌获取逻辑

using Microsoft.AspNetCore.Mvc;
using Microsoft.Graph;
using Microsoft.Identity.Client;
using System.Threading.Tasks;

namespace MyApp.Controllers
{
    [ApiController]
    public class SampleController : Controller
    {
        private readonly IConfidentialClientApplication _msalClient;

        // 注入MSAL机密客户端实例(需在Program.cs中提前配置)
        public SampleController(IConfidentialClientApplication msalClient)
        {
            _msalClient = msalClient;
        }

        [HttpGet("/api/testsendmail")]
        public async Task<ActionResult> Send()
        {
            try
            {
                // 从请求头获取用户的B2C ID令牌
                var b2cIdToken = HttpContext.Request.Headers["Authorization"].ToString().Replace("Bearer ", "");
                
                // 发起On-Behalf-Of请求,获取内部AD的Graph API访问令牌
                var result = await _msalClient.AcquireTokenOnBehalfOf(
                    new[] { "https://graph.microsoft.com/Mail.Send" },
                    new UserAssertion(b2cIdToken))
                    .ExecuteAsync();

                var graphClient = OnBehalfOfGraphClient.GetGraphClient(result.AccessToken);

                var mailRequest = graphClient.Me.SendMail(
                    new Message()
                    {
                        ToRecipients = new[]
                        {
                            new Recipient()
                            {
                                EmailAddress = new EmailAddress() { Address = "test@nowhere.local" }
                            }
                        },
                        Subject = "Test",
                        Body = new ItemBody() { ContentType = BodyType.Text, Content = "Test" }
                    }, true).Request();

                await mailRequest.PostAsync();

                return Ok();
            }
            catch (MsalUiRequiredException)
            {
                // 令牌无法静默刷新,需要用户重新认证
                return Unauthorized(new { Message = "请重新登录以获取有效权限" });
            }
            catch (ServiceException ex)
            {
                // 处理Graph API的其他业务错误
                return StatusCode((int)ex.StatusCode, new { Message = ex.Message });
            }
        }

        public static class OnBehalfOfGraphClient
        {
            public static GraphServiceClient GetGraphClient(string token)
            {
                var authProvider = new DelegateAuthenticationProvider(async (request) =>
                {
                    request.Headers.Authorization =
                        new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
                });

                return new GraphServiceClient(authProvider);
            }
        }
    }
}

在Blazor前端处理重新认证

当后端返回401时,触发MSAL的重新登录流程:

@inject MsalAuthenticationService MsalService

<button @onclick="SendMail">发送测试邮件</button>

@code {
    private async Task SendMail()
    {
        try
        {
            var response = await Http.GetAsync("/api/testsendmail");
            if (!response.IsSuccessStatusCode)
            {
                if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
                {
                    // 跳转至B2C登录页面重新认证
                    await MsalService.LoginRedirect(new[] { "openid", "profile", "Mail.Send" });
                }
                else
                {
                    var errorMsg = await response.Content.ReadFromJsonAsync<dynamic>();
                    Console.WriteLine($"请求失败:{errorMsg.Message}");
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"发送邮件出错:{ex.Message}");
        }
    }
}

三、关键说明

  • On-Behalf-Of流程:通过用户的B2C ID令牌,向内部AD申请Graph API的访问令牌,MSAL会自动利用内部IDP的刷新令牌更新过期的idp_access_token。
  • 错误捕获:MsalUiRequiredException表示无法静默刷新令牌,必须触发用户交互重新登录。
  • 权限配置:确保内部AD的应用注册已授予Mail.Send的委托权限,且B2C应用注册的权限范围包含该权限。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 04:55:03