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

MSAL On-Behalf-Of(OBO)流程令牌获取失败求助

问题描述

尝试通过MSAL的On-Behalf-Of(OBO)流程为用户获取令牌时,出现JSON解析错误:响应以'<'开头,无法解析为JSON内容。相关代码及配置如下:

获取令牌代码

[Authorize]
[RequiredScope(scopeRequiredByAPI)]
[ApiController]
[Route("[controller]")]
public class UserDetailController : ControllerBase
{
    const string scopeRequiredByAPI = "access_as_user";
    private readonly IUserService _userService;
    private readonly ILogger<UserDetailController> _logger;
    private readonly ITokenAcquisition _tokenAcquisition;
    private readonly AzureAdB2COpenIdConnectOptions _settings;
    public UserDetailController(ILogger<UserDetailController> logger, IUserService userService, ITokenAcquisition tokenAcquisition, IOptions<AzureAdB2COpenIdConnectOptions> settings)
    {
        _logger = logger;
        _userService = userService;
        _tokenAcquisition = tokenAcquisition;
        _settings = settings.Value;
    }

    [HttpGet]
    public async Task<User> GetAsync()
    {
        User currentUser = new User() { DisplayName = "Son" };
        //return currentUser;
        try
        {
            var scopes = new string[] {
                "https://graph.microsoft.com/.default",
                //"User.ReadWrite.All",
                };
            var user = HttpContext?.User;
            var result = await _tokenAcquisition.GetAuthenticationResultForUserAsync(scopes, _settings.TenantId, _settings.SignUpSignInPolicyId, user);

Web应用配置

builder.Services
.AddMicrosoftIdentityWebAppAuthentication(builder.Configuration, 
  Constants.AzureAdB2C)
  .EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
  .AddMicrosoftGraph(builder.Configuration.GetSection("GraphApi"))
  .AddInMemoryTokenCaches()

Web API配置

builder.Services
            .AddMicrosoftIdentityWebApiAuthentication(builder.Configuration, Constants.AzureAdB2C, Constants.AzureAdB2C)
            .EnableTokenAcquisitionToCallDownstreamApi()
            .AddMicrosoftGraph(options =>
            {
                options.BaseUrl = GraphApiOption?.EndPoint ?? "https://graph.microsoft.com/v1.0";
                options.Scopes = GraphApiOption?.Scopes;
            })
            .AddInMemoryTokenCaches();

错误日志

info: Microsoft.Identity.Web.TokenAcquisition[0]
  True MSAL 4.55.0.0 MSAL.NetCore .NET 7.0.9 Microsoft Windows 10.0.19045 [2023-08-13 18:22:08Z - d9208054-ea81-44cc-bf49-7ea6a2c1d874] === Token Acquisition (OnBehalfOfRequest) started:
    Authority: https://hellioshop.b2clogin.com/tfp/hellioshop.onmicrosoft.com/b2c_1_sign_in_gk/
    Scope: https://graph.microsoft.com/.default
    ClientId: 7cc33dc4-5218-4798-a1d3-f79a782a2824

warn: Microsoft.Identity.Web.TokenAcquisition[0]
True MSAL 4.55.0.0 MSAL.NetCore .NET 7.0.9 Microsoft Windows 10.0.19045 [2023-08-13 18:22:08Z - d9208054-ea81-44cc-bf49-7ea6a2c1d874] Only in-memory caching is used. The cache is not persisted and will be lost if the machine is restarted. It also does not scale for a web app or web API, where the number of users can grow large. In production, web apps and web APIs should use distributed caching like Redis. See https://aka.ms/msal-net-cca-token-cache-serialization
info: Microsoft.Identity.Web.TokenAcquisition[0]
True MSAL 4.55.0.0 MSAL.NetCore .NET 7.0.9 Microsoft Windows 10.0.19045 [2023-08-13 18:22:08Z - d9208054-ea81-44cc-bf49-7ea6a2c1d874] [Instance Discovery] Skipping Instance discovery for non-AAD authority.
info: Microsoft.Identity.Web.TokenAcquisition[0]
True MSAL 4.55.0.0 MSAL.NetCore .NET 7.0.9 Microsoft Windows 10.0.19045 [2023-08-13 18:22:08Z - d9208054-ea81-44cc-bf49-7ea6a2c1d874] [Internal cache] Total number of cache partitions found while getting access tokens: 0
info: Microsoft.Identity.Web.TokenAcquisition[0]
True MSAL 4.55.0.0 MSAL.NetCore .NET 7.0.9 Microsoft Windows 10.0.19045 [2023-08-13 18:22:08Z - d9208054-ea81-44cc-bf49-7ea6a2c1d874] [FindAccessTokenAsync] Discovered 0 access tokens in cache using partition key: Ey02OURgEZIIpcqKfoU7GyOswE8Xm_5eOtv-sJBftlU
info: Microsoft.Identity.Web.TokenAcquisition[0]
True MSAL 4.55.0.0 MSAL.NetCore .NET 7.0.9 Microsoft Windows 10.0.19045 [2023-08-13 18:22:08Z - d9208054-ea81-44cc-bf49-7ea6a2c1d874] [OBO request] Fetching tokens via normal OBO flow.
info: System.Net.Http.HttpClient.Default.LogicalHandler[100]
Start processing HTTP request POST https://hellioshop.b2clogin.com/tfp/hellioshop.onmicrosoft.com/b2c_1_sign_in_gk/oauth2/v2.0/token
info: System.Net.Http.HttpClient.Default.ClientHandler[100]
Sending HTTP request POST https://hellioshop.b2clogin.com/tfp/hellioshop.onmicrosoft.com/b2c_1_sign_in_gk/oauth2/v2.0/token
info: System.Net.Http.HttpClient.Default.ClientHandler[101]
Received HTTP response headers after 567.4847ms - 200
info: System.Net.Http.HttpClient.Default.LogicalHandler[101]
End processing HTTP request after 582.3227ms - 200
fail: Microsoft.Identity.Web.TokenAcquisition[0]
True MSAL 4.55.0.0 MSAL.NetCore .NET 7.0.9 Microsoft Windows 10.0.19045 [2023-08-13 18:22:09Z - d9208054-ea81-44cc-bf49-7ea6a2c1d874] System.Text.Json.JsonException: '<' is an invalid start of a value. Path: $ | LineNumber: 0 | BytePositionInLine: 0.
---&gt; System.Text.Json.JsonReaderException: '<' is an invalid start of a value. LineNumber: 0 | BytePositionInLine: 0.
at System.Text.Json.ThrowHelper.ThrowJsonReaderException(Utf8JsonReader&amp; json, ExceptionResource resource, Byte nextByte, ReadOnlySpan`1 bytes) at System.Text.Json.Utf8JsonReader.ConsumeValue(Byte marker) at System.Text.Json.Utf8JsonReader.ReadFirstToken(Byte first) at System.Text.Json.Utf8JsonReader.ReadSingleSegment() at System.Text.Json.Utf8JsonReader.Read() at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader&amp; reader, JsonSerializerOptions options, ReadStack&amp; state)
--- End of inner exception stack trace ---
at System.Text.Json.ThrowHelper.ReThrowWithPath(ReadStack&amp; state, JsonReaderException ex)
at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader&amp; reader, JsonSerializerOptions options, ReadStack&amp; state) at System.Text.Json.Serialization.JsonConverter`1.ReadCoreAsObject(Utf8JsonReader&amp; reader, JsonSerializerOptions options, ReadStack&amp; state)
at System.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1 utf8Json, JsonTypeInfo jsonTypeInfo, Nullable`1 actualByteCount)
at System.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1 json, JsonTypeInfo jsonTypeInfo) at System.Text.Json.JsonSerializer.Deserialize(String json, Type returnType, JsonSerializerContext context) at Microsoft.Identity.Client.Utils.JsonHelper.DeserializeFromJson[T](String json) at Microsoft.Identity.Client.OAuth2.OAuth2Client.CreateResponse[T](HttpResponse response, RequestContext requestContext) at Microsoft.Identity.Client.OAuth2.OAuth2Client.ExecuteRequestAsync[T](Uri endPoint, HttpMethod method, RequestContext requestContext, Boolean expectErrorsOn200OK, Boolean addCommonHeaders, Func`2 onBeforePostRequestData)
at Microsoft.Identity.Client.OAuth2.OAuth2Client.GetTokenAsync(Uri endPoint, RequestContext requestContext, Boolean addCommonHeaders, Func`2 onBeforePostRequestHandler) at Microsoft.Identity.Client.OAuth2.TokenClient.SendHttpAndClearTelemetryAsync(String tokenEndpoint, ILoggerAdapter logger) at Microsoft.Identity.Client.OAuth2.TokenClient.SendTokenRequestAsync(IDictionary`2 additionalBodyParameters, String scopeOverride, String tokenEndpointOverride, CancellationToken cancellationToken)
at Microsoft.Identity.Client.Internal.Requests.RequestBase.SendTokenRequestAsync(IDictionary`2 additionalBodyParameters, CancellationToken cancellationToken)
at Microsoft.Identity.Client.Internal.Requests.OnBehalfOfRequest.FetchNewAccessTokenAsync(CancellationToken cancellationToken)
at Microsoft.Identity.Client.Internal.Requests.OnBehalfOfRequest.RefreshRtOrFetchNewAccessTokenAsync(CancellationToken cancellationToken)
at Microsoft.Identity.Client.Internal.Requests.OnBehalfOfRequest.ExecuteAsync(CancellationToken cancellationToken)
at Microsoft.Identity.Client.Internal.Requests.RequestBase.RunAsync(CancellationToken cancellationToken)
解决建议

响应以'<'开头说明返回的是HTML错误页面而非预期的JSON令牌响应,按以下步骤排查修复:

  • 修正Token端点与Authority:B2C的OBO流程不能使用用户流(user flow)的端点,需改用租户级Token端点,格式为https://<tenant-name>.b2clogin.com/<tenant-name>.onmicrosoft.com/oauth2/v2.0/token,去掉路径中的tfp/<user-flow-id>部分。
  • 调整权限范围:OBO是委派权限流程,避免使用https://graph.microsoft.com/.default(该范围适用于应用权限),改用具体的委派权限,比如https://graph.microsoft.com/User.Read。
  • 检查应用注册配置:确认Web API的Azure AD B2C应用注册中,已添加所需的Microsoft Graph委派权限并完成管理员同意;同时确保Web API被允许代表用户发起OBO请求。
  • 验证传入令牌有效性:用JWT解码工具检查Web API收到的用户令牌,确认aud(受众)、iss(颁发者)声明正确,且令牌未过期。
  • 捕获实际响应内容:添加日志输出Token端点返回的完整HTML内容,明确具体错误原因(如权限不足、配置错误)。

内容的提问来源于stack exchange,提问作者Sơn Lưu Thuần

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 12:12:03