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

On-Behalf-Of流调用AcquireTokenOnBehalfOf断言过期如何实现令牌刷新

问题根因

你当前的实现存在两个核心问题,直接导致id_token过期后无法自动刷新:

  • 你将登录瞬间生成的id_token作为固定值写入用户Claim持久化存储,该令牌默认有效期1小时,存储后不会自动更新,过期后作为OBO流的用户断言传入时,必然触发断言时间范围无效的错误。
  • 你虽然已经通过EnableTokenAcquisitionToCallDownstreamApi、AddInMemoryTokenCaches配置了微软身份类库内置的令牌缓存、自动刷新能力,但实际获取令牌时绕过了这套官方逻辑,手动实例化客户端、传入固定的过期id_token构造UserAssertion调用OBO接口,完全没有用到内置的刷新机制。
修复方案

第一步:清理Startup中冗余的令牌存储逻辑

你不需要手动把id_token存到Claim里,AddMicrosoftIdentityWebApp在开启SaveTokens = true后会自动管理用户的初始令牌、刷新令牌以及缓存,手动存固定值反而会拿到过期凭证。修改后的OnTicketReceived逻辑只保留业务必需的自定义Claim即可:

services.AddAuthentication("SomeCustomType")
    .AddMicrosoftIdentityWebApp(options =>
    {
        Configuration.Bind("AzureAD", options);
        options.SaveTokens = true;

        options.Events.OnTicketReceived = async context =>
        {
            var claimsIdentity = (ClaimsIdentity)context.Principal.Identity;
            claimsIdentity.AddClaim(new Claim("DirectAadAuth", "true"));
            // 移除原有手动存储userIdToken到Claim的逻辑
            await Task.CompletedTask;
        };
    })
    .EnableTokenAcquisitionToCallDownstreamApi(Configuration.GetSection("CustomConfig").Get<CustomConfigModel>().DownstreamApiScopes)
    .AddInMemoryTokenCaches();

注意:你原有代码在OnTicketReceived中二次调用SignInAsync切换到CustomType认证方案,如果不是业务强需求建议移除,多套认证方案会导致令牌上下文丢失;必须保留的话,后续获取令牌时要明确指定认证方案为CustomType。

第二步:改造TokenClient,使用官方令牌获取服务

不要手动创建UserAssertion、直接调用clientApplication.AcquireTokenOnBehalfOf,直接注入框架提供的ITokenAcquisition服务即可,它会自动处理令牌缓存、过期刷新、OBO断言有效性校验:当缓存中的令牌快过期时,会自动用刷新令牌换领新令牌,完全不需要手动处理id_token过期问题。
首先通过构造函数注入ITokenAcquisition:

private readonly ITokenAcquisition _tokenAcquisition;
private readonly string[] _scopes;

public TokenClient(ITokenAcquisition tokenAcquisition, IConfiguration configuration)
{
    _tokenAcquisition = tokenAcquisition;
    _scopes = configuration.GetSection("CustomConfig").Get<CustomConfigModel>().DownstreamApiScopes;
}

然后重写获取令牌的方法:

public async Task<string> GetAccessTokenAsync(ClaimsPrincipal user)
{
    // 如果使用自定义认证方案,需要在参数中指定authenticationScheme: "CustomType"
    var accessToken = await _tokenAcquisition.GetAccessTokenForUserAsync(_scopes, user: user);
    return accessToken;
}

特殊场景兼容方案

如果因业务限制必须直接操作IConfidentialClientApplication手动调用AcquireTokenOnBehalfOf,需要做两个调整:

  • 不要从Claim中读取固定存储的id_token,每次调用前通过HttpContext.GetTokenAsync("id_token")从当前认证上下文读取最新的有效令牌构造UserAssertion
  • 调用接口时捕获MsalUiRequiredException异常,当捕获到断言过期错误时,触发OpenID Connect的无感知刷新流程,不需要用户手动重新登录即可拿到新的有效令牌
  • 如果是分布式部署场景,不要使用内存令牌缓存AddInMemoryTokenCaches,替换为Redis、数据库等分布式缓存实现,避免应用重启、请求跨节点时缓存丢失触发强制登录。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 05:42:13