在 Asp.Net Core Web 应用程序中使用 EasyAuth 对 Azure 应用服务上的 AAD 进行身份验证时,无法填充 ClaimsPrincipal
Posted
技术标签:
【中文标题】在 Asp.Net Core Web 应用程序中使用 EasyAuth 对 Azure 应用服务上的 AAD 进行身份验证时,无法填充 ClaimsPrincipal【英文标题】:Trouble getting ClaimsPrincipal populated when using EasyAuth to authenticate against AAD on Azure App Service in a Asp.Net Core web app 【发布时间】:2017-05-20 23:23:49 【问题描述】:我们有一个基于 Asp.Net 核心的网络应用程序。它不包含任何配置的身份验证中间件。
我们托管在 Azure 应用服务上,并使用身份验证/授权选项 (EasyAuth) 对 Azure AD 进行身份验证。
身份验证运行良好 - 我们插入了必要的标头,我们可以在 /.auth/me 中看到经过身份验证的身份。但是 HttpContext.User 属性没有被填充。
这是 Asp.Net 核心的兼容性问题吗?还是我做错了什么?
【问题讨论】:
@chris-gillum - 如果你能提供帮助,那就太好了...... 【参考方案1】:我创建了一个自定义中间件,用于填充用户属性,直到 Azure 团队解决此问题。
它从应用服务身份验证中读取标头并创建一个用户,该用户将被[Authorize]
识别并拥有name
的声明。
// Azure app service will send the x-ms-client-principal-id when authenticated
app.Use(async (context, next) =>
// Create a user on current thread from provided header
if (context.Request.Headers.ContainsKey("X-MS-CLIENT-PRINCIPAL-ID"))
// Read headers from Azure
var azureAppServicePrincipalIdHeader = context.Request.Headers["X-MS-CLIENT-PRINCIPAL-ID"][0];
var azureAppServicePrincipalNameHeader = context.Request.Headers["X-MS-CLIENT-PRINCIPAL-NAME"][0];
// Create claims id
var claims = new Claim[]
new System.Security.Claims.Claim("http://schemas.microsoft.com/identity/claims/objectidentifier", azureAppServicePrincipalIdHeader),
new System.Security.Claims.Claim("name", azureAppServicePrincipalNameHeader)
;
// Set user in current context as claims principal
var identity = new GenericIdentity(azureAppServicePrincipalIdHeader);
identity.AddClaims(claims);
// Set current thread user to identity
context.User = new GenericPrincipal(identity, null);
;
await next.Invoke();
);
【讨论】:
@ChrisGillum 非常感谢这篇文章,它帮助了我。我有一个后续问题。我在后端使用 ASP Membership 进行授权。将用户添加到 Context 似乎并没有让他们登录。有没有办法可以从同一段代码调用 SignInManager 来做到这一点?【参考方案2】:是的,这是一个兼容性问题。不幸的是,ASP.NET Core 不支持将身份信息从 IIS 模块(如 Easy Auth)流向应用程序代码。这意味着 HttpContext.User 和类似的代码不会像普通的 ASP.NET 那样工作。
目前的解决方法是从您的服务器代码调用您的网络应用程序的 /.auth/me 端点来获取用户声明。然后,您可以使用 x-ms-client-principal-id 请求标头值作为缓存键来适当地缓存此数据。 /.auth/me 调用需要进行正确的身份验证,就像对 Web 应用程序的调用需要进行身份验证一样(身份验证 cookie 或请求标头令牌)。
【讨论】:
谢谢你,克里斯。我们还需要根据我们从 AAD 获得的身份从应用程序数据库中添加额外的声明。所以,我想我会考虑添加一个自定义中间件的选项,该中间件通过 .auth/me 和数据库一起读取信息,并从那里创建 ClaimsPrincipal。它将允许我们保留 Asp.Net Core 身份验证/授权框架的其余部分。 现在已经快 2 年了,azure/Microsoft 仍未更新 azure web[/api/mobile] 应用程序 EasyAuth 实现更新,该更新自动处理将 EasyAuth 身份验证用户 X-MS-* 标头数据映射到this.User 对象是否类似于您在 asp.net 核心 Web 应用程序中自己实现 oauth 或 openid 连接身份验证代码时显示的位置?我刚刚完成了对 azure function app v2 的测试,他们的 EasyAuth 故事为您完成了这项工作,并允许您将 ClaimsPrincipal 依赖注入到您想要的任何函数 [/控制器方法] 中。 @myusrn ASP.NET Core 的设计不允许自动注入。不幸的是,这需要在应用代码中完成。 很公平,但是包含一个 nuget 包,它的工作方式类似于某些人已经破解的工作,这难道不是有意义的吗? 嗨@Chris Gilliam,我不应该期望easyauth过滤器总是采用浏览器客户端openid连接或本机应用程序oauth授权标头承载令牌身份验证,并从中创建X-MS-CLIENT-PRINCIPAL -NAME、X-MS-CLIENT-PRINCIPAL-IDP、X-MS-CLIENT-PRINCIPAL、X-MS-TOKEN-AAD-ID-TOKEN 等我可以信任并用于创建 this.Context.User 的请求标头导致 azurewebsites.net/.auth/me 不需要网络请求?【参考方案3】:为此,我编写了一个小的基本中间件。它将基于 .auth/me 端点创建一个身份。身份在身份验证管道中创建,以便 [授权] 属性和策略与身份一起使用。
你可以在这里找到它:
https://github.com/lpunderscore/azureappservice-authentication-middleware
或在 nuget 上:
https://www.nuget.org/packages/AzureAppserviceAuthenticationMiddleware/
添加后,只需将此行添加到您的启动:
app.UseAzureAppServiceAuthentication();
【讨论】:
我查看了 Repo 并尝试了它但没有成功。在我看来,您似乎没有将诸如 X-ZUMO-AUTH 之类的标头传递给 /.auth/me。您正在发送 cookie。这可能是问题吗? GitHub 存储库已更新为也可以使用标头。我在我的应用中成功使用了它 @n00b 感谢您的贡献,您的更改已合并,我会尽快更新 nuget。 嗨,我做了一些工作以使其与 asp.net core 2.0 及更高版本一起使用。我会很感激这里的一些反馈:github.com/kirkone/KK.AspNetCore.EasyAuthAuthentication @KirKone 感谢更新,因为我正在尝试使用 asp.net core 2.1+ Web 应用程序项目来做所有事情,以便在 azure 函数应用程序、Web 应用程序和容器部署方案中具有一定的可移植性。我尝试了 user3159405 nuget,它在运行时爆炸了。我尝试了您的解决方案,它在运行时没有崩溃,但我遇到的问题是它适用于 easyauth 浏览器 openid 连接会话 cookie 请求,但不适用于 easyauth 桌面/移动应用程序 oauth 授权标头承载令牌安全请求。将问题发布到您的 GitHub 存储库,以尝试合作解决问题。【参考方案4】:以下代码从 Azure 应用服务 HTTP 标头解密 AAD 令牌,并使用声明填充 HttpContext.User。这很粗糙,因为您希望缓存配置而不是在每个请求中查找它:
OpenIdConnectConfigurationRetriever r = new OpenIdConnectConfigurationRetriever();
ConfigurationManager<OpenIdConnectConfiguration> configManager = new ConfigurationManager<OpenIdConnectConfiguration>(options.Endpoint, r);
OpenIdConnectConfiguration config = await configManager.GetConfigurationAsync();
var tokenValidationParameters = new TokenValidationParameters
ValidateIssuerSigningKey = true,
IssuerSigningKeys = config.SigningKeys.ToList(),
ValidateIssuer = true,
ValidIssuer = config.Issuer,
ValidateAudience = true,
ValidAudience = options.Audience,
ValidateLifetime = true,
ClockSkew = new TimeSpan(0, 0, 10)
;
JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
ClaimsPrincipal principal = null;
SecurityToken validToken = null;
string token = context.Request.Headers["X-MS-TOKEN-AAD-ID-TOKEN"];
if (!String.IsNullOrWhiteSpace(token))
principal = handler.ValidateToken(token, tokenValidationParameters, out validToken);
var validJwt = validToken as JwtSecurityToken;
if (validJwt == null) throw new ArgumentException("Invalid JWT");
if (principal != null)
context.User.AddIdentities(principal.Identities);
它仅适用于 Azure AD。要支持其他 ID 提供者(Facebook、Twitter 等),您必须检测相关标头并弄清楚如何解析每个提供者的令牌。但是,它应该只是上述主题的变体。
【讨论】:
只需要解析JWT。 Azure 应用服务已经对其进行了验证。这将大大简化您的解决方案。【参考方案5】:你可以试试这个库。我遇到了类似的问题并创建了这个以简化使用。
https://github.com/dasiths/NEasyAuthMiddleware
用于 ASP.NET 的 Azure 应用服务身份验证 (EasyAuth) 中间件 CORE 具有完全可定制的组件,支持本地 调试
它通过注册自定义身份验证处理程序来补充 HttpContext.User。为了在本地运行时更轻松,它甚至可以使用 json 文件来加载模拟声明。
【讨论】:
以上是关于在 Asp.Net Core Web 应用程序中使用 EasyAuth 对 Azure 应用服务上的 AAD 进行身份验证时,无法填充 ClaimsPrincipal的主要内容,如果未能解决你的问题,请参考以下文章
ASP.NET Core Web 应用程序系列- 在ASP.NET Core中使用AutoMapper进行实体映射
ASP.NET Core 入门教程 1使用ASP.NET Core 构建第一个Web应用
ASP.NET Core 入门教程 1使用ASP.NET Core 构建第一个Web应用
ASP.NET Core Web 应用程序系列- 在ASP.NET Core中使用Autofac替换自带DI进行批量依赖注入(MVC当中应用)