Microsoft Graph .NET SDK v5中AcquireTokenInteractive重复触发问题求助
问题现象
使用Microsoft Graph .NET SDK v5时,每次调用graphClient.Me相关接口都会触发AcquireTokenInteractive。首次调用await graphClient.Me.GetAsync()弹出登录界面符合预期,但第二次调用await graphClient.Me.Calendars.GetAsync()时再次弹出登录界面,属于异常行为。
原因分析
问题核心出在TokenProvider类的GetAuthorizationTokenAsync方法中:每次调用该方法都会重新创建PublicClientApplication实例。MSAL的账号、Token缓存与PublicClientApplication实例绑定,新实例无法读取之前登录缓存的账号信息,导致AcquireTokenSilent找不到可用账号,抛出MsalUiRequiredException,进而触发交互式登录流程。
此外,Button1_Click方法每次执行都会新建TokenProvider和GraphServiceClient实例,进一步加剧了缓存失效问题。
解决方案
1. 仅初始化一次PublicClientApplication
将PublicClientApplication的创建逻辑移到TokenProvider的构造函数中,确保整个生命周期内只初始化一次:
public class TokenProvider : IAccessTokenProvider { public AllowedHostsValidator AllowedHostsValidator => throw new NotImplementedException(); public IPublicClientApplication publicClientApp; // 在构造函数中初始化PublicClientApplication public TokenProvider() { publicClientApp = PublicClientApplicationBuilder.Create("0c-a5-42-a-92") .WithRedirectUri("http://localhost") .Build(); } public async Task<string> GetAuthorizationTokenAsync(Uri uri, Dictionary<string, object> additionalAuthenticationContext = null, CancellationToken cancellationToken = default) { var scopes = new[] { "Calendars.Read", "User.Read" }; AuthenticationResult authResult; try { var accounts = await publicClientApp.GetAccountsAsync(); authResult = await publicClientApp.AcquireTokenSilent(scopes, accounts.FirstOrDefault()).ExecuteAsync(); return authResult.AccessToken; } catch (MsalUiRequiredException ex) { authResult = await publicClientApp.AcquireTokenInteractive(scopes).ExecuteAsync(); return authResult.AccessToken; } } public async Task SignOut() { var accounts = await publicClientApp.GetAccountsAsync(); foreach (var account in accounts) { await publicClientApp.RemoveAsync(account); } } }
2. 复用GraphServiceClient及相关实例
在windows1.cs中,将TokenProvider、BaseBearerTokenAuthenticationProvider和GraphServiceClient的初始化移到类的构造函数中,避免每次点击按钮都新建实例:
TokenProvider tp; BaseBearerTokenAuthenticationProvider authenticationProvider; GraphServiceClient graphClient; // 在类构造函数中初始化实例 public Windows1() { tp = new TokenProvider(); authenticationProvider = new BaseBearerTokenAuthenticationProvider(tp); graphClient = new GraphServiceClient(authenticationProvider); } public async Task<bool> Button1_Click() { Microsoft.Graph.Models.User user = null; user = await graphClient.Me.GetAsync(); // 首次触发登录界面,符合预期 return true; } public async Task<List<string>> Button2_Click() { var calendars = await graphClient.Me.Calendars.GetAsync(); // 复用缓存的Token,不再触发登录 // 处理日历数据并返回 return calendars.Value.Select(c => c.Name).ToList(); }
补充说明
- MSAL的账号和Token缓存默认存储在本地文件系统中,仅同一个
PublicClientApplication实例可访问这些缓存。 - 生产环境中需实现
AllowedHostsValidator,配置合法的主机列表以规避安全风险。
内容的提问来源于stack exchange,提问作者Team Freytag

