【发布时间】:2017-04-23 04:15:17
【问题描述】:
我的 ASP.NET MVC Core 应用程序使用 OWIN 中间件以及以下模块对 Azure AD 执行 OpenIdConnect 身份验证:
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Microsoft.Azure.ActiveDirectory.GraphClient;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.Azure.ActiveDirectory.GraphClient.Extensions;
OWIN 中间件执行一系列任务,包括
- 通过 Azure Graph API 获取 Azure AD 组和角色
- 从数据库中获取用户配置文件数据
- 从第 1 步和第 2 步创建声明
- 发布cookie
- 中间件自动处理刷新令牌
- 中间件将令牌缓存在数据库中,并且能够通过 Graph 客户端的机制
AcquireTokenSilentAsync进行检索。
MVC 应用程序提供单个 Razor 视图,从那时起,我使用 Aurelia JavaScript 框架(很可能是 Angular、Knockout、React,并不重要),它只通过 AJAX 向我的 Api 控制器执行 API 请求。
所以我的问题是如何将服务器上处理的所有这些身份验证和授权步骤转换为客户端上针对 Azure AD 的基于 JWT 的身份验证?
诚然,我的问题相当幼稚,因为在下面的代码中 OWIN 中间件组件正在执行大量工作。所以我正在寻找一个起点、辅助库和可行性。在我确信可以使用 AJAX 和 JWT 身份验证复制此流程之前,我没有信心删除所有中间件代码和服务器端身份验证。
我做了一些研究,答案可能涉及以下内容
- adal.js
- ASP.NET Core 中的 JWT 中间件
- HTML 网络存储
- Azure AD Graph REST API(而不是 C# Graph 客户端)
以下是当前 OWIN 中间件代码,该代码在服务器上针对 Azure AD 执行 OpenIdConnect 身份验证:
app.UseCookieAuthentication();
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
ClientId = Configuration["Authentication:AzureAd:ClientId"],
ClientSecret = Configuration["Authentication:AzureAd:ClientSecret"],
Authority = Configuration["Authentication:AzureAd:AADInstance"] + Configuration["Authentication:AzureAd:TenantId"],
CallbackPath = Configuration["Authentication:AzureAd:CallbackPath"],
ResponseType = OpenIdConnectResponseType.CodeIdToken,
Events = new OpenIdConnectEvents()
{
OnAuthorizationCodeReceived = async (context) =>
{
var code = context.TokenEndpointRequest.Code;
var identity = context.Ticket.Principal.Identity as ClaimsIdentity;
userObjectID = identity.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
signedInUserID = identity.FindFirst(ClaimTypes.NameIdentifier).Value;
ClientCredential credential =
new ClientCredential(
Configuration["Authentication:AzureAd:ClientId"],
Configuration["Authentication:AzureAd:ClientSecret"]);
var authority = Configuration["Authentication:AzureAd:AADInstance"]
+ Configuration["Authentication:AzureAd:TenantId"];
AuthenticationContext authContext =
new AuthenticationContext(authority, new ADALTokenCacheService(signedInUserID, Configuration));
await authContext.AcquireTokenByAuthorizationCodeAsync(
context.TokenEndpointRequest.Code,
new Uri(context.TokenEndpointRequest.RedirectUri, UriKind.RelativeOrAbsolute),
credential,
Configuration["Authentication:AzureAd:GraphResource"]);
context.HandleCodeRedemption();
ActiveDirectoryClient activeDirectoryClient = GetActiveDirectoryClient();
// Get currently logged in User from Graph
IPagedCollection<IUser> users = await activeDirectoryClient.Users.Where(u => u.ObjectId.Equals(userObjectID)).ExecuteAsync();
IUser user = users.CurrentPage.ToList().First();
// Get User's AD Groups
IEnumerable<string> userGroupIds = await user.GetMemberGroupsAsync(false);
List<string> userGroupIdList = userGroupIds.ToList();
// Transform User's AD Groups into Claims
foreach (var groupObjectId in userGroupIdList)
{
var group = await activeDirectoryClient.Groups.GetByObjectId(groupObjectId).ExecuteAsync();
Claim newClaim = new Claim(
CustomClaimValueTypes.ADGroup,
group.DisplayName,
ClaimValueTypes.String,
"AAD GRAPH");
((ClaimsIdentity)(context.Ticket.Principal.Identity)).AddClaim(newClaim);
}
// Get User's Application permissions from Database
upn = identity.FindFirst(ClaimTypes.Upn).Value;
DbContext db =
new DbContext(Configuration["ConnectionStrings:DefaultConnection"]);
if (db.PortalUsers.FirstOrDefault(b => (b.UPN == upn)) == null)
{
throw new System.IdentityModel.Tokens.SecurityTokenValidationException("You are not registered to use this application.");
}
var applications = from permissions in db.PortalPermissions
where permissions.PortalUser.UPN == upn
//orderby permissions.Application.SortOrder ascending
select permissions.PortalApplication;
// Transform User's Application permissions into Claims
foreach (var application in applications)
{
Claim newClaim = new Claim(
CustomClaimValueTypes.Application,
application.Name,
ClaimValueTypes.String,
"DATABASE");
((ClaimsIdentity)(context.Ticket.Principal.Identity)).AddClaim(newClaim);
}
},
OnRemoteFailure = (context) =>
{
if (context.Failure.Message == "You are not registered to use this application.")
{
context.Response.Redirect("/AuthenticationError");
}
else
{
context.Response.Redirect("/Error");
}
context.HandleResponse();
return Task.FromResult(0);
}
}
});
app.UseFileServer(new FileServerOptions
{
EnableDefaultFiles = true,
EnableDirectoryBrowsing = false
});
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Start}/{id?}");
});
}
private ActiveDirectoryClient GetActiveDirectoryClient()
{
Uri servicePointUri = new Uri(Configuration["Authentication:AzureAd:GraphResource"]);
Uri serviceRoot = new Uri(servicePointUri, Configuration["Authentication:AzureAd:TenantId"]);
ActiveDirectoryClient activeDirectoryClient = new ActiveDirectoryClient(
serviceRoot, async () => await GetTokenForApplicationAsync());
return activeDirectoryClient;
}
private async Task<string> GetTokenForApplicationAsync()
{
ClientCredential clientCredential =
new ClientCredential(
Configuration["Authentication:AzureAd:ClientId"],
Configuration["Authentication:AzureAd:ClientSecret"]);
AuthenticationContext authenticationContext =
new AuthenticationContext(
Configuration["Authentication:AzureAd:AADInstance"] +
Configuration["Authentication:AzureAd:TenantId"],
new ADALTokenCacheService(signedInUserID, Configuration));
AuthenticationResult authenticationResult = await authenticationContext.AcquireTokenSilentAsync(
Configuration["Authentication:AzureAd:GraphResource"],
clientCredential,
new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
return authenticationResult.AccessToken;
}
【问题讨论】:
-
你现在解决这个问题了吗?
标签: authentication asp.net-core jwt openid-connect azure-active-directory