.Net Core 2.0 - 获取 AAD 访问令牌以与 Microsoft Graph 一起使用
.Net Core 2.0 - Get AAD access token to use with Microsoft Graph
使用 Azure AD 身份验证启动全新的 .Net Core 2.0 项目时,您会得到一个可以登录您的租户的工作示例,太棒了!
现在我想为登录用户获取一个访问令牌,并使用它来处理 Microsoft Graph API。
我没有找到有关如何实现此目的的任何文档。我只想要一种简单的方法来获取访问令牌并访问图形 API,使用在启动新的 .NET Core 2.0 项目时创建的模板。从那里我应该能够弄清楚其余的。
在 Visual Studio 中创建新的 2.0 MVC Core 应用程序时,它与按照 select 工作和学校帐户进行身份验证的过程创建的项目一起使用非常重要。
我写了一篇博客文章,展示了如何做到这一点:ASP.NET Core 2.0 Azure AD Authentication
TL;DR 是当您收到来自 AAD 的授权代码时,您应该添加这样的处理程序:
.AddOpenIdConnect(opts =>
{
Configuration.GetSection("Authentication").Bind(opts);
opts.Events = new OpenIdConnectEvents
{
OnAuthorizationCodeReceived = async ctx =>
{
var request = ctx.HttpContext.Request;
var currentUri = UriHelper.BuildAbsolute(request.Scheme, request.Host, request.PathBase, request.Path);
var credential = new ClientCredential(ctx.Options.ClientId, ctx.Options.ClientSecret);
var distributedCache = ctx.HttpContext.RequestServices.GetRequiredService<IDistributedCache>();
string userId = ctx.Principal.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
var cache = new AdalDistributedTokenCache(distributedCache, userId);
var authContext = new AuthenticationContext(ctx.Options.Authority, cache);
var result = await authContext.AcquireTokenByAuthorizationCodeAsync(
ctx.ProtocolMessage.Code, new Uri(currentUri), credential, ctx.Options.Resource);
ctx.HandleCodeRedemption(result.AccessToken, result.IdToken);
}
};
});
这里我的 context.Options.Resource
是 https://graph.microsoft.com
(Microsoft Graph),我从配置中绑定它以及其他设置(客户端 ID 等)。
我们使用 ADAL 兑换令牌,并将生成的令牌存储在令牌缓存中。
令牌缓存是您必须创建的东西,这里是 example app 中的示例:
public class AdalDistributedTokenCache : TokenCache
{
private readonly IDistributedCache _cache;
private readonly string _userId;
public AdalDistributedTokenCache(IDistributedCache cache, string userId)
{
_cache = cache;
_userId = userId;
BeforeAccess = BeforeAccessNotification;
AfterAccess = AfterAccessNotification;
}
private string GetCacheKey()
{
return $"{_userId}_TokenCache";
}
private void BeforeAccessNotification(TokenCacheNotificationArgs args)
{
Deserialize(_cache.Get(GetCacheKey()));
}
private void AfterAccessNotification(TokenCacheNotificationArgs args)
{
if (HasStateChanged)
{
_cache.Set(GetCacheKey(), Serialize(), new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(1)
});
HasStateChanged = false;
}
}
}
此处的令牌缓存使用分布式缓存来存储令牌,以便为您的应用提供服务的所有实例都可以访问令牌。它们是按用户缓存的,因此您以后可以为任何用户检索令牌。
然后当你想获得一个令牌并使用 MS 图表时,你会做类似的事情(GetAccessTokenAsync()
中的重要内容):
[Authorize]
public class HomeController : Controller
{
private static readonly HttpClient Client = new HttpClient();
private readonly IDistributedCache _cache;
private readonly IConfiguration _config;
public HomeController(IDistributedCache cache, IConfiguration config)
{
_cache = cache;
_config = config;
}
[AllowAnonymous]
public IActionResult Index()
{
return View();
}
public async Task<IActionResult> MsGraph()
{
HttpResponseMessage res = await QueryGraphAsync("/me");
ViewBag.GraphResponse = await res.Content.ReadAsStringAsync();
return View();
}
private async Task<HttpResponseMessage> QueryGraphAsync(string relativeUrl)
{
var req = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0" + relativeUrl);
string accessToken = await GetAccessTokenAsync();
req.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
return await Client.SendAsync(req);
}
private async Task<string> GetAccessTokenAsync()
{
string authority = _config["Authentication:Authority"];
string userId = User.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
var cache = new AdalDistributedTokenCache(_cache, userId);
var authContext = new AuthenticationContext(authority, cache);
string clientId = _config["Authentication:ClientId"];
string clientSecret = _config["Authentication:ClientSecret"];
var credential = new ClientCredential(clientId, clientSecret);
var result = await authContext.AcquireTokenSilentAsync("https://graph.microsoft.com", credential, new UserIdentifier(userId, UserIdentifierType.UniqueId));
return result.AccessToken;
}
}
在那里我们静默获取令牌(使用令牌缓存),并将其附加到对图形的请求。
使用 Azure AD 身份验证启动全新的 .Net Core 2.0 项目时,您会得到一个可以登录您的租户的工作示例,太棒了!
现在我想为登录用户获取一个访问令牌,并使用它来处理 Microsoft Graph API。
我没有找到有关如何实现此目的的任何文档。我只想要一种简单的方法来获取访问令牌并访问图形 API,使用在启动新的 .NET Core 2.0 项目时创建的模板。从那里我应该能够弄清楚其余的。
在 Visual Studio 中创建新的 2.0 MVC Core 应用程序时,它与按照 select 工作和学校帐户进行身份验证的过程创建的项目一起使用非常重要。
我写了一篇博客文章,展示了如何做到这一点:ASP.NET Core 2.0 Azure AD Authentication
TL;DR 是当您收到来自 AAD 的授权代码时,您应该添加这样的处理程序:
.AddOpenIdConnect(opts =>
{
Configuration.GetSection("Authentication").Bind(opts);
opts.Events = new OpenIdConnectEvents
{
OnAuthorizationCodeReceived = async ctx =>
{
var request = ctx.HttpContext.Request;
var currentUri = UriHelper.BuildAbsolute(request.Scheme, request.Host, request.PathBase, request.Path);
var credential = new ClientCredential(ctx.Options.ClientId, ctx.Options.ClientSecret);
var distributedCache = ctx.HttpContext.RequestServices.GetRequiredService<IDistributedCache>();
string userId = ctx.Principal.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
var cache = new AdalDistributedTokenCache(distributedCache, userId);
var authContext = new AuthenticationContext(ctx.Options.Authority, cache);
var result = await authContext.AcquireTokenByAuthorizationCodeAsync(
ctx.ProtocolMessage.Code, new Uri(currentUri), credential, ctx.Options.Resource);
ctx.HandleCodeRedemption(result.AccessToken, result.IdToken);
}
};
});
这里我的 context.Options.Resource
是 https://graph.microsoft.com
(Microsoft Graph),我从配置中绑定它以及其他设置(客户端 ID 等)。
我们使用 ADAL 兑换令牌,并将生成的令牌存储在令牌缓存中。
令牌缓存是您必须创建的东西,这里是 example app 中的示例:
public class AdalDistributedTokenCache : TokenCache
{
private readonly IDistributedCache _cache;
private readonly string _userId;
public AdalDistributedTokenCache(IDistributedCache cache, string userId)
{
_cache = cache;
_userId = userId;
BeforeAccess = BeforeAccessNotification;
AfterAccess = AfterAccessNotification;
}
private string GetCacheKey()
{
return $"{_userId}_TokenCache";
}
private void BeforeAccessNotification(TokenCacheNotificationArgs args)
{
Deserialize(_cache.Get(GetCacheKey()));
}
private void AfterAccessNotification(TokenCacheNotificationArgs args)
{
if (HasStateChanged)
{
_cache.Set(GetCacheKey(), Serialize(), new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(1)
});
HasStateChanged = false;
}
}
}
此处的令牌缓存使用分布式缓存来存储令牌,以便为您的应用提供服务的所有实例都可以访问令牌。它们是按用户缓存的,因此您以后可以为任何用户检索令牌。
然后当你想获得一个令牌并使用 MS 图表时,你会做类似的事情(GetAccessTokenAsync()
中的重要内容):
[Authorize]
public class HomeController : Controller
{
private static readonly HttpClient Client = new HttpClient();
private readonly IDistributedCache _cache;
private readonly IConfiguration _config;
public HomeController(IDistributedCache cache, IConfiguration config)
{
_cache = cache;
_config = config;
}
[AllowAnonymous]
public IActionResult Index()
{
return View();
}
public async Task<IActionResult> MsGraph()
{
HttpResponseMessage res = await QueryGraphAsync("/me");
ViewBag.GraphResponse = await res.Content.ReadAsStringAsync();
return View();
}
private async Task<HttpResponseMessage> QueryGraphAsync(string relativeUrl)
{
var req = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0" + relativeUrl);
string accessToken = await GetAccessTokenAsync();
req.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
return await Client.SendAsync(req);
}
private async Task<string> GetAccessTokenAsync()
{
string authority = _config["Authentication:Authority"];
string userId = User.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
var cache = new AdalDistributedTokenCache(_cache, userId);
var authContext = new AuthenticationContext(authority, cache);
string clientId = _config["Authentication:ClientId"];
string clientSecret = _config["Authentication:ClientSecret"];
var credential = new ClientCredential(clientId, clientSecret);
var result = await authContext.AcquireTokenSilentAsync("https://graph.microsoft.com", credential, new UserIdentifier(userId, UserIdentifierType.UniqueId));
return result.AccessToken;
}
}
在那里我们静默获取令牌(使用令牌缓存),并将其附加到对图形的请求。