Authentication 在Asp.Net核心web应用程序中使用EasyAuth对Azure应用程序服务上的AAD进行身份验证时,无法填充ClaimsPrincipal

Authentication 在Asp.Net核心web应用程序中使用EasyAuth对Azure应用程序服务上的AAD进行身份验证时,无法填充ClaimsPrincipal,authentication,asp.net-core,azure-web-app-service,azure-active-directory,Authentication,Asp.net Core,Azure Web App Service,Azure Active Directory,我们有一个基于Asp.Net核心的web应用程序。它不包含在其中配置的任何身份验证中间件 我们在Azure应用程序服务上托管,并使用身份验证/授权选项(EasyAuth)对Azure AD进行身份验证 身份验证工作得很好-我们插入了必要的头,并且可以在/.auth/me处看到经过身份验证的标识。但是没有填充HttpContext.User属性 这是Asp.Net核心的兼容性问题吗?还是我做错了什么?是的,这是一个兼容性问题。不幸的是,ASP.NET Core不支持将身份信息从IIS模块(如Eas

我们有一个基于Asp.Net核心的web应用程序。它不包含在其中配置的任何身份验证中间件

我们在Azure应用程序服务上托管,并使用身份验证/授权选项(EasyAuth)对Azure AD进行身份验证

身份验证工作得很好-我们插入了必要的头,并且可以在/.auth/me处看到经过身份验证的标识。但是没有填充HttpContext.User属性


这是Asp.Net核心的兼容性问题吗?还是我做错了什么?

是的,这是一个兼容性问题。不幸的是,ASP.NET Core不支持将身份信息从IIS模块(如Easy Auth)传递到应用程序代码。这意味着HttpContext.User和类似的代码不会像普通ASP.NET那样工作


目前的解决方法是从服务器代码调用web应用程序的/.auth/me端点以获取用户声明。然后,可以使用x-ms-client-principal-id请求头值作为缓存键来缓存该数据。/.auth/me调用需要以与对web应用的调用需要进行身份验证(auth cookie或请求头令牌)相同的方式进行正确身份验证。

我创建了一个自定义中间件,该中间件将填充用户属性,直到Azure团队解决此问题

它从应用程序服务身份验证读取标题,并创建一个用户,该用户将被
[Authorize]
识别,并对
名称
拥有声明权

// 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();
});

以下代码从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);
        }
    }
OpenIdConnectConfigurationRetriever r=新的OpenIdConnectConfigurationRetriever();
ConfigurationManager configManager=新的ConfigurationManager(options.Endpoint,r);
OpenIdConnectConfiguration配置=等待configManager.GetConfigurationAsync();
var tokenValidationParameters=新的tokenValidationParameters
{
ValidateSuersigningKey=true,
IssuerSigningKeys=config.SigningKeys.ToList(),
validateisuer=true,
ValidIssuer=config.Issuer,
ValidateAudience=true,
Validudience=选项。观众,
ValidateLifetime=true,
时钟偏移=新的时间跨度(0,0,10)
};
JwtSecurityTokenHandler=新的JwtSecurityTokenHandler();
ClaimsPrincipal principal=null;
SecurityToken validToken=null;
字符串令牌=context.Request.Headers[“X-MS-token-AAD-ID-token”];
如果(!String.IsNullOrWhiteSpace(标记))
{
principal=handler.ValidateToken(令牌、令牌验证参数、out validToken);
var validJwt=作为JwtSecurityToken的validToken;
如果(validJwt==null){抛出新的ArgumentException(“无效JWT”);}
if(主体!=null)
{
context.User.addidentity(主体.标识);
}
}

它只适用于Azure广告。要支持其他ID提供商(Facebook、Twitter等),您必须检测相关的头并找出如何解析每个提供商的令牌。然而,它应该只是上述主题的变体。

我编写了一个小型的基本中间件来实现这一点。它将基于.auth/me端点创建一个标识。身份在身份验证管道中创建,以便[授权]属性和策略与身份一起工作

你可以在这里找到它:

或者在nuget上:

添加后,只需将此行添加到启动:


app.UseAzureAppServiceAuthentication()

您可以试试这个库。我遇到了一个类似的问题,并创建了这个来简化使用

ASP.NET的Azure应用程序服务身份验证(EasyAuth)中间件 以完全可定制的组件为核心,支持本地 调试


它通过注册自定义身份验证处理程序来对HttpContext.User进行水合物化。为了在本地运行时更方便,它甚至能够使用json文件加载模拟声明。

@chris gillum-如果您能提供帮助,那将非常好……谢谢您,chris。我们还需要根据从AAD获得的身份从应用程序数据库中添加额外的声明。因此,我想我将考虑添加一个自定义中间件的选项,该中间件通过.auth/me和数据库一起读取信息,并从那里创建ClaimsPrincipal。它将允许我们保留Asp.Net核心身份验证/授权框架的其余部分。现在已经过去了将近2年,azure/Microsoft仍然没有更新azure web[/api/mobile]自动处理将EasyAuth身份验证用户X-MS-*标头数据映射到此中的应用程序EasyAuth实现更新。用户对象是否与在asp.net核心web应用程序中自己实现oauth或openid连接身份验证代码时显示的位置相似?我刚刚通过测试azure function app v2,他们的EasyAuth故事为您做到了这一点,并允许您将ClaimsPrincipal依赖注入任何您想要的函数[/controller方法]。@myusrn ASP.NET Core的设计不允许自动注入。不幸的是,这需要在应用程序代码中完成。这很公平,但包含一个nuget包,它的工作原理是否与一些人所接受的类似?嗨,Chris Gilliam,难道我不应该期望easyauth筛选器将始终采用浏览器客户端openid connect或本机应用oauth授权标头承载令牌身份验证,并从中创建X-MS-client-PRINCIPAL-NAME、X-MS-client-PRINCIPAL-IDP、X-MS-client-PRINCIPAL、X-MS-token-AAD-ID-token、,等。我可以信任并用于创建此.Context.User的请求头,从而无需向azurewebsites.net发送网络请求/