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

无法缓存Graph与SharePoint多令牌,Graph令牌获取失败求助

问题:多令牌缓存冲突导致Graph令牌获取失败

背景

我们的ASP.Net Core应用集成了Microsoft Graph,支持微软账户登录、日历访问,当前正在实现OneDrive文件选择器,用于用户查看/选择OneDrive及SharePoint站点文件。

应用通过令牌获取机制为GraphServiceClient获取访问令牌,Startup.cs配置如下:

var initialScopes = configuration.GetValue<string>("Authentication:DownstreamApi:Scopes")?.Split(' ');

authenticationBuilder.AddMicrosoftIdentityWebApp(configuration.GetSection("Authentication:AzureAd"), 
        OpenIdConnectDefaults.AuthenticationScheme)    
        .EnableTokenAcquisitionToCallDownstreamApi(initialScopes)    
        .AddMicrosoftGraph(configuration.GetSection("Authentication:DownstreamApi"))
        .AddDistributedTokenCaches();

services.AddDistributedSqlServerCache(options =>
{
    options.ConnectionString = distributedCacheConnection.ConnectionString; 
    options.SchemaName = "dbo";
    options.TableName = "MicrosoftTokenCache";
    options.DefaultSlidingExpiration = TimeSpan.FromMinutes(90);
});

使用Graph客户端时,通过自定义SignedInUserTokenProvider从令牌获取服务获取令牌:

var signedInAuthProvider = new BaseBearerTokenAuthenticationProvider(new 
SignedInUserTokenProvider(_tokenAcquisition));
_signedInUserGraphClient = new GraphServiceClient(signedInAuthProvider);

SignedInUserTokenProvider.cs代码:

public class SignedInUserTokenProvider : IAccessTokenProvider
{
private readonly ITokenAcquisition _tokenAcquisition;

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

public async Task<string> GetAuthorizationTokenAsync(Uri uri, Dictionary<string, object> additionalAuthenticationContext = default,
    CancellationToken cancellationToken = default)
{         
    return await _tokenAcquisition.GetAccessTokenForUserAsync
        (
            new string[] { },
            authenticationScheme: OpenIdConnectDefaults.AuthenticationScheme
        );
}

public AllowedHostsValidator AllowedHostsValidator { get; }
}

这部分功能运行正常,可完整使用Graph各项功能。

我们已实现OneDrive文件选择器,该功能需两个不同令牌(分别用于OneDrive和SharePoint),调用令牌获取服务时使用不同作用域:

OneDrive令牌调用代码:

var pickerSite = new Uri("https://companyname-my.sharepoint.com");
string[] scopes = new string[] {  
    $"https://{pickerSite.DnsSafeHost}/AllSites.FullControl" };

var token = await _tokenAcquisition.GetAccessTokenForUserAsync
(
    scopes,
    authenticationScheme: OpenIdConnectDefaults.AuthenticationScheme
);

return token;

SharePoint令牌调用仅将作用域地址改为https://companyname.sharepoint.com,功能运行正常,可访问OneDrive和SharePoint。

问题现象

三个令牌(一个Graph令牌、两个SharePoint令牌)均存储于分布式SQL缓存中,获取SharePoint令牌后,Graph令牌获取功能失效,报错:

Multiple access tokens found for matching authority, client_id, user and scopes.
{"The cache contains multiple tokens satisfying the requirements. Try to clear token cache. "}

我们预期每个用户可缓存多个不同作用域的令牌,对应不同资源访问需求,但当前无法实现,需要解决令牌缓存冲突问题。

原因分析

问题核心在于SignedInUserTokenProvider的GetAuthorizationTokenAsync方法中,调用GetAccessTokenForUserAsync时传入了空的作用域数组。MSAL查找缓存令牌时,空作用域会被视为匹配所有令牌的作用域条件,而此时缓存中已存在多个同用户、同客户端ID的令牌(Graph和两个SharePoint的),导致无法确定返回哪一个,从而抛出冲突错误。

此外,MSAL的令牌缓存按用户、客户端ID、权限(作用域)、资源区分条目,空作用域会跳过作用域匹配逻辑,引发歧义。

解决方案

1. 修改SignedInUserTokenProvider,传入明确的Graph作用域

不要传入空数组,而是使用配置中定义的Graph作用域,或者直接指定Graph所需的作用域。修改后的代码如下:

public class SignedInUserTokenProvider : IAccessTokenProvider
{
private readonly ITokenAcquisition _tokenAcquisition;
private readonly IConfiguration _configuration;

public SignedInUserTokenProvider(ITokenAcquisition tokenAcquisition, IConfiguration configuration)
{
    _tokenAcquisition = tokenAcquisition;
    _configuration = configuration;
}

public async Task<string> GetAuthorizationTokenAsync(Uri uri, Dictionary<string, object> additionalAuthenticationContext = default,
    CancellationToken cancellationToken = default)
{         
    var graphScopes = _configuration.GetValue<string>("Authentication:DownstreamApi:Scopes")?.Split(' ') ?? new string[0];
    return await _tokenAcquisition.GetAccessTokenForUserAsync
        (
            graphScopes,
            authenticationScheme: OpenIdConnectDefaults.AuthenticationScheme
        );
}

public AllowedHostsValidator AllowedHostsValidator { get; }
}

2. 确保所有令牌请求使用明确作用域

无论是Graph还是SharePoint/OneDrive的令牌请求,都必须传入非空且明确的作用域数组,让MSAL能准确匹配缓存中的对应令牌,避免歧义。

3. 临时清理冲突缓存

先手动清理分布式SQL缓存中的MicrosoftTokenCache表数据,消除已存在的冲突令牌,再测试修复后的代码。

额外注意事项

  • 不同资源的令牌作用域要明确区分:Graph的作用域以https://graph.microsoft.com/开头,SharePoint/OneDrive的作用域对应各自的站点域名。
  • 确保AddMicrosoftGraph配置的作用域与GetAccessTokenForUserAsync传入的作用域一致,避免不必要的令牌请求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 15:35:34