ASP.NET Core 2.2 中的 Azure AD 身份验证

Posted

技术标签:

【中文标题】ASP.NET Core 2.2 中的 Azure AD 身份验证【英文标题】:Azure AD Authentication in ASP.NET Core 2.2 【发布时间】:2019-12-13 03:13:59 【问题描述】:

我目前正在努力将 ASP.NET Core 2.2 Web API 连接到现有的 Azure AD。我的配置基于 ASP.NET Core 团队的 this sample code。 Cookie 已替换为 JWT。

无法从元数据地址检索文档

现在我遇到以下错误消息:

IOException: IDX10804: Unable to retrieve document from: MetadataAdress.

- Microsoft.IdentityModel.Protocols.HttpDocumentRetriever+<GetDocumentAsync>d__8.MoveNext()
- System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
- System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
- Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectConfigurationRetriever
      +<GetAsync>d__3.MoveNext()
- System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
- System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
- Microsoft.IdentityModel.Protocols.ConfigurationManager+<GetConfigurationAsync>d__24.MoveNext()

当我直接调用 URL 时,我会收到包含配置文件的即时响应。但是,代码似乎无法做到这一点。我不确定是什么原因。

Azure AD 配置语法

此问题的最可能原因是配置错误。也许我弄错了字段的语法或遗漏了一个重要的值。

连接信息字段

连接信息字段的提供方式如下:

    TenantId:     Tenant-GUID
    Authority:    https://login.microsoftonline.com/TenantId
    Resource:     https://resource-endpoint.resource-domain
    ClientId:     Client-GUID
    ClientSecret: ClientSecret

服务配置

Startup.cs中的认证服务配置如下:

    services
        .AddAuthentication(options =>   
             options.DefaultScheme          = JwtBearerDefaults.AuthenticationScheme;
             options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
        )
        .AddJwtBearer()
        .AddOpenIdConnect(options => 
            options.ClientId             = this.ClientId;
            options.ClientSecret         = this.ClientSecret;
            options.Authority            = this.Authority;
            options.Resource             = this.Resource;
            options.ResponseType         = OpenIdConnectResponseType.CodeIdToken;
            options.SignedOutRedirectUri = "/signed-out";
            options.Events               = new OpenIdConnectEvents()
            
                OnAuthorizationCodeReceived = async context =>
                
                    var request = context.HttpContext.Request;
                    var currentUri = UriHelper.BuildAbsolute(
                        request.Scheme, request.Host, request.PathBase, request.Path
                    );
                    var credentials = new ClientCredential(this.ClientId, this.ClientSecret);
                    var authContext = new AuthenticationContext(
                        this.Authority,
                        AuthPropertiesTokenCache.ForCodeRedemption(context.Properties)
                    );

                    var result = await authContext.AcquireTokenByAuthorizationCodeAsync(
                        context.ProtocolMessage.Code,
                        new System.Uri(currentUri),
                        credentials,
                        this.Resource
                    );

                    context.HandleCodeRedemption(result.AccessToken, result.IdToken);
                 
             ;

             // Custom
             options.MetadataAddress      = $"this.Authority/federationmetadata/2007-06/federationmetadata.xml";
             options.RequireHttpsMetadata = false; // Dev env only
        

现有 API

有很多现有的 Web API 连接到这个 Azure AD。遗憾的是,他们都在使用完整的 .NET Framework。他们使用来自Microsoft.Owin.Security.ActiveDirectory 命名空间的WindowsAzureActiveDirectoryBearerAuthenticationExtensionsUseWindowsAzureActiveDirectoryBearerAuthentication 方法。

他们使用的另一件事是HostAuthenticationFilter,其身份验证类型为Bearer

问题

有什么问题?

我该如何解决这个问题?

如何将这些组件一起使用?

ASP.NET Core 2.2 JWT 承载认证 Azure AD(仅令牌验证 + 声明提取 - 创建由其他服务处理)

【问题讨论】:

【参考方案1】:

您正在使用 OpenIDConnect 库并将它们指向 WS-Federation 元数据 (/federationmetadata/2007-06/federationmetadata.xml)。这是行不通的。

OpenIDConnect 的正确元数据端点是 /.well-known/openid-configuration。这是described here。首先更改它,然后返回 cookie。

更新

我监督的是,您正在保护 WebAPI。您说中间件使用JwtBearer 作为默认身份验证方案,但您还包括一个挑战方案为OIDC。这对我来说真的没有意义。为什么要为 WebAPI 提供 OIDC 质询方案?

在这里您可以找到 ASP.NET Core 示例about JwtBearer。这里是Azure AD samples demoing WebApp calling WebApi(也是WebAPI的承载者,应用前端的OIDC。

没有使用 OIDC 质询的 JWT Bearer Auth 示例。你为什么要实现它?这是什么情况?您可能正在考虑实现多个身份验证方案,这是可能的。但是没有一种用于身份验证的方案和另一种用于质询的方案...

如果通过更新/删除错误的元数据更改了错误消息,请将其包含在您的原始问题中。就像现在一样 - 纯粹的错误消息是 OIDC 中间件无法解析 WS-Federation 元数据。这是预期的。

【讨论】:

关于元数据:我同意这感觉不对。可悲的是,删除它并没有什么不同,除了错误消息中显示的 url。我保留了它,因为以前的 API 仍在使用它。 cookie 中间件只是一种传输机制。如果用户身份是通过 cookie 或 JWT 不记名令牌传输的,则应该不相关。它们都包含相同的信息 - 因此,如果配置正确,它应该检测授权标头中的令牌,对其进行验证并从中提取用户身份。 你对这两者的结合有一个有效的观点。我的猜测是原作者确实想要JwtBearer 身份验证,但混淆了一些概念。我已经删除了 OIDC 中间件并验证了正确的行为。【参考方案2】:

问题的根源

经过一些测试,我设法确定了问题:显然这个问题的主要原因是网络相关。当我从我们公司切换到不受限制的网络时,身份验证成功。

修复

我必须配置一个代理并将其提供给JwtBearerOpenIdConnect 中间件。这看起来像这样:

var proxy = new HttpClientHandler

    Proxy = new WebProxy("ProxyUrl:ProxyPort")  UseDefaultCredentials = true; ,
    UseDefaultCredentials = true
;

services
    .AddJwtBearer(options => 
        // ... other configuration steps ...
        options.BackchannelHttpHandler = proxy;
    )
    .AddOpenIdConnect(options => 
        // ... other configuration steps ...
        options.BackchannelHttpHandler = proxy;
    )

元数据地址

@astaykov 是正确的,元数据地址确实不正确。我也有这种感觉,但由于以前的 API 可以成功运行,所以我一直保持这种感觉。在问题测试期间,我也将其删除,但由于网络问题,它不会产生影响。

网络问题解决后,使用默认元数据地址即可。自定义失败 - 使用不同的身份验证架构时符合预期。

【讨论】:

以上是关于ASP.NET Core 2.2 中的 Azure AD 身份验证的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET Core 2.2 - 密码重置在 Azure 上不起作用(无效令牌)

ASP.NET Core 应用程序日志未写入 Azure 应用服务中的 Blob

获取 ASP.NET-Core 2.2 控制器中的控制器名称和方法名称

ASP.NET Core在Azure Kubernetes Service中的部署和管理

Azure AD 与 ASP.NET Core MVC 中的标识

Azure Service Fabric 中 ASP.NET Core 应用程序中的 WebpackDevMiddleware