如何在不使用会话的情况下获取图形令牌?

Posted

技术标签:

【中文标题】如何在不使用会话的情况下获取图形令牌?【英文标题】:How to acquire graph token without using sessions? 【发布时间】:2018-07-03 17:08:11 【问题描述】:

我有一个使用 Azure AD 身份验证的应用程序。我还需要访问 Microsoft Graph API 以获取用户数据。我发现向 Graph API 发出请求的每个示例都使用缓存的会话令牌,但由于我使用的是 JWT,显然我不需要存储会话状态。如何使用以我的应用为受众的 JWT 获得合适受众的 JWT?

例如,这是一个从Microsoft Graph AspNetCore Sample 检索令牌的请求:

_userTokenCache = new SessionTokenCache(userId, _memoryCache).GetCacheInstance();

var cca = new ConfidentialClientApplication(
    _appId,
    _redirectUri,
    _credential,
    _userTokenCache,
    null);
var result = await cca.AcquireTokenSilentAsync(_scopes, cca.Users.First());
return result.AccessToken;

它利用内存缓存从带有 OpenId Connect cookie 的 Challenge() 重定向登录中提取令牌。但是,由于我使用的是 JWT,我已经有了不记名令牌,但是权限错误。我需要做什么才能获得可用于访问 Graph API 的新令牌?我仍然希望为我的应用程序 ID 授权令牌,因此我需要一个新令牌,允许我通过服务器端休息请求访问 API。

编辑:错误地标记为 Azure AD Graph,重新标记为 Microsoft Graph。

编辑编辑:澄清一下,到目前为止我看到的每个示例都使用会话 cookie:

services.AddAuthentication(sharedOptions => 
        sharedOptions.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    )
    .AddAzureAd(options => Configuration.Bind("AzureAd", options))
    .AddCookie();

但是,我使用的是 JWT,所以我没有缓存令牌:

app.UseJwtBearerAuthentication(new JwtBearerOptions 
    Authority = $"instancetenant",
        Audience = audience,
        SaveToken = true
);

我从对login.microsoftonline.com 的请求中获得的 JWT 将我的应用程序作为受众,而这些示例生成的 JWT 将 https://graph.microsoft.com 作为受众。所以我需要(我认为至少)只使用我从标准身份验证请求中获得的令牌来为这个观众获取一个令牌。

【问题讨论】:

您能解释一下“我正在使用 JWT”是什么意思吗? JWT 只是令牌格式,您可以从任何 AAD OAUTH 方法中获得。 应用程序发布到 login.microsoftonline.com,我从该服务获得 JWT。所描述的示例似乎都在使用会话 cookie,因此只需调用 app.AddMemoryCache() 即可使用实时令牌填充它们的内存缓存。我得到的令牌是针对不同的受众的,因此被图形 api 拒绝。 @MarcLaFleur 我已经更新了帖子,提供了有关应用程序身份验证方案的更多信息。 【参考方案1】:

不要将您管理令牌(即令牌缓存)的方式与令牌本身混淆。缓存令牌的原因很简单,以便您可以根据需要请求刷新的令牌 (refresh_token)。刷新令牌仅适用于某些场景(即,当使用authorization_code并且您已请求offline_access 范围时)。

如果您使用的是没有刷新令牌的流(即implicitclient_credentials),那么您可能不需要缓存您的令牌。您通常仍应缓存它们,因为从 AAD 获取令牌会有开销成本,并且缓存允许您仅在现有令牌过期时检索新令牌。

将 DelegateAuthenticationProvider 与现有令牌一起使用

说了这么多,听起来你已经拿到了一个令牌。由于 MSAL 的全部意义(这是 ConfidentialClientApplication 的来源)它为您检索和管理令牌,我不确定您为什么要这样做。我会完全跳过 MSAL,只使用您现有的令牌。

如果您使用的是 Microsoft Graph .NET Client Library,您可以完全放弃 MSAL,只需通过 DelegateAuthenticationProvider 使用您现有的令牌 (access_token):

var graphServiceClient = new GraphServiceClient(
    new DelegateAuthenticationProvider((requestMessage) => 
        requestMessage.Headers.Authorization =
            new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", token.access_token);

        return Task.FromResult(0);
    )
);

至于“适当的受众”,我不确定我是否理解上下文。您的令牌需要包含 Microsoft Graph 的范围,但您如何定义它们在一定程度上取决于您获取令牌的方式。

v1 端点

如果您使用的是旧的 Azure AD OAUTH 端点(又名 v1 端点),那么您需要通过 Azure 门户配置您的应用程序权限。为了在不同的 API(称为“资源”)之间切换,您需要请求 offline_access 并使用 refresh_token。切换涉及在传入新的resource 时请求刷新的令牌。然后,生成的令牌将与该资源一起使用。

例如,如果我的默认资源是 SharePoint Online 实例 (https://tenant.sharepoint.com),那么我通常会使用以下内容刷新我的令牌:

private async Task<string> RequestTokenAsync() 
    var data = new Dictionary<string, string>();
    data.Add("grant_type", "refresh_token");
    data.Add("client_id", _clientId);
    data.Add("client_secret", _clientSecret);
    data.Add("resource", "https://tenant.sharepoint.com");
    data.Add("redirect_uri", RedirectUri);
    data.Add("refresh_token ", refresh_token);

    HttpClient httpClient = new HttpClient();
    var response = await httpClient.PostAsync(_tokenUri, new FormUrlEncodedContent(data));
    response.EnsureSuccessStatusCode();
    var result = await result.Content.ReadAsStringAsync();

现在,如果我想调用 Microsoft Graph,我首先需要获取 https://graph.microsoft.com 资源的令牌:

private async Task<string> RequestTokenAsync() 
    var data = new Dictionary<string, string>();
    data.Add("grant_type", "refresh_token");
    data.Add("client_id", _clientId);
    data.Add("client_secret", _clientSecret);
    data.Add("resource", "https://graph.microsoft.com");
    data.Add("redirect_uri", RedirectUri);
    data.Add("refresh_token ", refresh_token);

    HttpClient httpClient = new HttpClient();
    var response = await httpClient.PostAsync(_tokenUri, new FormUrlEncodedContent(data));
    response.EnsureSuccessStatusCode();
    var result = await result.Content.ReadAsStringAsync();

现在我有两个令牌,一个用于 SharePoint,一个用于 Microsoft Graph。我可以通过简单地刷新适当资源的令牌来在资源之间切换。我必须确保我正确刷新但是因为如果我的refresh_token 在我可以替换它之前过期,我已经完全丢失了我的凭据。

如果这听起来很复杂,那就是。通常,您需要构建一些机制来管理哪些令牌是活动的、哪些令牌需要被替换等。这就是令牌缓存的全部内容,因为 MSAL/ADAL 会为您处理这个问题。

v2 端点

较新的 v2 端点更易于使用。它使用scopes 而不是resources。这些范围包括资源标识符,可以根据需要动态分配。

因此,虽然在 v1 中我们可能会从 Microsoft Graph 分配 user.read 并从 Outlook Rest API 分配 user.read,但我们现在可以通过同时请求 https://graph.microsoft.com/user.readhttps://outlook.office.com/user.read 在单个令牌中同时分配两者。这意味着我们获得了一个可以与任一 API 一起使用的令牌,而无需从上面进入“刷新切换资源”业务。

v2 的缺点是目前只有有限数量的 API 支持它。如果您需要跨多个 API 工作,出于这个原因,您最好还是使用 v1。

希望这会有所帮助。

【讨论】:

以上是关于如何在不使用会话的情况下获取图形令牌?的主要内容,如果未能解决你的问题,请参考以下文章

如何在不登录的情况下从 Instagram 获取 oauth 2 访问令牌(隐式流程)?

如何在不扩展令牌的情况下获取 PATH 环境变量的值?

如何在不请求通知权限的情况下获取 Firebase 云消息传递的注册令牌?

如何在不关闭当前 Firebase 会话的情况下创建用户身份验证 [重复]

如何在不打开CAD文件的情况下,获取该文件中起作用的标注样式中的全局比例和图形范围。

如何在不退出的情况下更新会话值?