我正在使用MSAL实现身份验证,我需要一些指导来处理刷新令牌。
我的Angular Web App正在使用MSAL通过ASP.NET Web API进行身份验证。 Web API需要访问Microsoft Graph的某些范围,因此它使用"On Behalf Of" OAuth 2.0 flow获取用于调用MS Graph的访问令牌。这部分已经完成并且可以工作。
问题是当访问令牌到期时,.NET守护程序(使用OBO流)将在一段时间后调用MS Graph。
我需要通过Web API获取刷新令牌并将其缓存(例如在SQL数据库中),以便守护程序可以读取它并用于获取有效的访问令牌。
我认为用于机密客户端应用程序的TokenCache是执行此操作的正确方法,但是我不确定如何通过守护程序应用程序获取有效的访问令牌。
这是我要用于从AAD获取访问令牌的守护程序应用程序的代码:
var userAssertion = new UserAssertion(
<accessToken>,
"urn:ietf:params:oauth:grant-type:jwt-bearer");
var authority = authEndpoint.TrimEnd('/') + "/" + <tenant> + "/";
var clientCredencial = new ClientCredential(<clientSecret>);
var authClient = new ConfidentialClientApplication(<clientId>, authority, <redirectUri>,
clientCredencial, <userTokenCache>, null);
try
{
var authResult =
await authClient.AcquireTokenOnBehalfOfAsync(<scopes>, userAssertion, authority);
activeAccessToken = authResult.AccessToken;
}
catch (MsalException ex)
{
throw;
}
我应该提供<userTokenCache>
来获取刷新令牌表单缓存吗?如果是,UserAssertion
要求提供<accessToken>
,但我不知道应该使用什么值。
还是我应该自己制作一个token request并从响应中获取刷新令牌,因为MSAL不支持该刷新令牌?然后,我可以将刷新令牌存储在数据库中,并在守护程序应用程序中将其用作<accessToken>
,将null
用作<userTokenCache>
。
我认为可以使用MSAL获得刷新令牌,但我发现了it is not。
更新
我忘了说我所有的应用程序都使用相同的应用程序ID(这是由于AADv2端点的限制,尽管我只是发现it was removed from the docs at Nov 2nd 2018)。
为什么客户端凭据不流动?
可以在Web API(使用OBO流)中执行与MS Graph的通信,但是用户可能会延迟任务,例如8小时后发送邮件(Web API会将任务存储在数据库中)。针对这种情况的解决方案是按计划运行的应用程序(守护程序),可从数据库获取任务并执行对MS Graph的调用。我不希望管理员同意我的任何应用,因为获得用户同意非常重要。如果同意被撤销,则不应调用MS Graph。因此,守护程序应使用刷新令牌从AAD获取访问令牌以访问MS Graph(使用OBO流)。
我希望现在已经清楚了。也许我不应该这样。任何建议将不胜感激。
答案 0 :(得分:1)
MSAL确实处理刷新令牌,您只需要处理缓存序列化。 -userTokenCache
由OBO调用使用,您可以通过先调用AcquireTokenSilentAsycn来使用刷新令牌(这是刷新令牌的功能)
-applicationTokenCache由客户端凭据流(AcquireTokenForApplication)使用。
我建议您看一下以下说明OBO的示例:https://github.com/Azure-Samples/active-directory-dotnet-native-aspnetcore-v2,尤其是TodoListService/Extensions/TokenAcquisition.cs#L275-L294
代码是:
var accounts = await application.GetAccountsAsync();
try
{
AuthenticationResult result = null;
var allAccounts = await application.GetAccountsAsync();
IAccount account = await application.GetAccountAsync(accountIdentifier);
result = await application.AcquireTokenSilentAsync(scopes.Except(scopesRequestedByMsalNet), account);
return result.AccessToken;
}
catch (MsalUiRequiredException ex)
{
...
现在,缓存本身是从客户端发送到Web API的承载令牌初始化的。见
TodoListService/Extensions/TokenAcquisition.cs#L305-L336
private void AddAccountToCacheFromJwt(IEnumerable<string> scopes, JwtSecurityToken jwtToken, AuthenticationProperties properties, ClaimsPrincipal principal, HttpContext httpContext)
{
try
{
UserAssertion userAssertion;
IEnumerable<string> requestedScopes;
if (jwtToken != null)
{
userAssertion = new UserAssertion(jwtToken.RawData, "urn:ietf:params:oauth:grant-type:jwt-bearer");
requestedScopes = scopes ?? jwtToken.Audiences.Select(a => $"{a}/.default");
}
else
{
throw new ArgumentOutOfRangeException("tokenValidationContext.SecurityToken should be a JWT Token");
}
var application = CreateApplication(httpContext, principal, properties, null);
// Synchronous call to make sure that the cache is filled-in before the controller tries to get access tokens
AuthenticationResult result = application.AcquireTokenOnBehalfOfAsync(scopes.Except(scopesRequestedByMsalNet), userAssertion).GetAwaiter().GetResult();
}
catch (MsalUiRequiredException ex)
{
...