如何在MSAL.NET中缓存移动端获取的JWT及访问/刷新令牌?
解决方案:将移动端令牌存入MSAL.NET用户令牌缓存
由于新版MSAL.NET的AcquireTokenOnBehalfOf不再将刷新令牌持久化到用户令牌缓存,你可以通过手动构造令牌缓存条目并添加到MSAL缓存的方式,实现从移动端传递的令牌持久化,具体步骤和代码如下:
核心思路
MSAL.NET的用户令牌缓存本质是可序列化的存储结构,你可以直接将移动端获取的合法令牌(刷新令牌、访问令牌、ID令牌)构造为符合MSAL格式的缓存条目,添加到缓存后触发持久化操作,后续即可正常调用Graph API时自动复用缓存中的令牌。
关键注意事项
- 移动端传递的刷新令牌必须是针对后端Web应用的客户端ID生成的,刷新令牌与客户端ID绑定,跨客户端无法复用。
- 需从ID令牌中解析出用户唯一标识(如
oid或sub声明),确保缓存与用户身份正确关联。 - 自定义缓存提供者(如你的
MuTokenCacheProvider)需正确实现缓存的初始化与持久化逻辑。
代码实现示例
方法1:直接构造MsalTokenCacheItem添加到缓存
public async Task<ActionResult> CacheMobileTokens( string idToken, string refreshToken, string accessToken, DateTimeOffset expiresOn, string[] scopes, string tenantId, string userId) { // 初始化ConfidentialClientApplication var app = ConfidentialClientApplicationBuilder.Create(clientId) .WithClientSecret(clientSecret) .WithRedirectUri(redirectUrl) .Build(); // 关联自定义缓存提供者到当前用户 var tokenCacheProvider = ((MuTokenCacheProvider)_cacheProvider); tokenCacheProvider.Initialize(app.UserTokenCache, userId); // 从ID令牌构造用户ClaimsPrincipal var jwtToken = new JwtSecurityToken(idToken); var claimsIdentity = new ClaimsIdentity(jwtToken.Claims, "JWT"); var user = new ClaimsPrincipal(claimsIdentity); // 构造MSAL令牌缓存条目 var tokenCacheEntry = new MsalTokenCacheItem( clientId: app.ClientId, homeAccountId: $"{userId}.{tenantId}", // 格式:用户ID.租户ID realm: tenantId, secret: accessToken, refreshToken: refreshToken, expiresOn: expiresOn.UtcDateTime, scopes: scopes, idToken: idToken, user: user); // 将条目添加到缓存 app.UserTokenCache.AddTokenCacheItem(tokenCacheEntry); // 触发缓存持久化(根据自定义缓存提供者逻辑调整) await app.UserTokenCache.SaveAsync(); return Ok(); }
方法2:通过MSAL V3格式JSON反序列化加载缓存
如果更倾向于序列化方式,可构造符合MSAL V3缓存规范的JSON,再反序列化到缓存:
public async Task<ActionResult> CacheMobileTokensViaJson(string cacheJson) { var app = ConfidentialClientApplicationBuilder.Create(clientId) .WithClientSecret(clientSecret) .WithRedirectUri(redirectUrl) .Build(); // 初始化自定义缓存提供者 var tokenCacheProvider = ((MuTokenCacheProvider)_cacheProvider); tokenCacheProvider.Initialize(app.UserTokenCache, myUserId); // 将JSON反序列化到令牌缓存 var cacheBytes = Encoding.UTF8.GetBytes(cacheJson); app.UserTokenCache.DeserializeMsalV3(cacheBytes); // 持久化缓存 await app.UserTokenCache.SaveAsync(); return Ok(); }
对应的JSON格式示例:
{ "HomeAccountId": "用户ID.租户ID", "Environment": "https://login.microsoftonline.com", "Realm": "租户ID", "LocalAccountId": "用户ID", "Username": "user@example.com", "RefreshToken": "移动端获取的刷新令牌", "IdToken": "移动端获取的ID令牌", "AccessToken": "移动端获取的访问令牌", "ExpiresOn": "2024-12-31T23:59:59.000Z", "Scopes": ["email", "Calendars.ReadWrite"] }
后续使用
完成令牌缓存后,后续调用Graph API时,直接使用AcquireTokenSilent即可自动复用缓存中的令牌:
var result = await app.AcquireTokenSilent(scopes, user) .ExecuteAsync();
内容的提问来源于stack exchange,提问作者Matt Roberts
相关产品推荐
相关产品推荐

