TokenCache是​​否应用于MSAL的脱机访问?

时间:2018-12-06 12:36:08

标签: c# azure-active-directory microsoft-graph msal refresh-token

我正在使用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流)。

我希望现在已经清楚了。也许我不应该这样。任何建议将不胜感激。

1 个答案:

答案 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)
  {
   ...