AspNet.Security.OpenIdConnect.Server (ASP.NET vNex

2019-07-14 04:00发布

I am using Visual Studio 2015 Enterprise and ASP.NET vNext Beta8 to build an endpoint that both issues and consumes JWT tokens as described in detail here. As explained in that article the endpoint uses AspNet.Security.OpenIdConnect.Server (AKA OIDC) to do the heavy lifting.

While standing this prototype up in our internal development environment we have encountered a problem using it with a load balancer. In particular, we think it has to do with the "Authority" setting on app.UseJwtBearerAuthentication and our peculiar mix of http/https. With our load balanced environment, any attempt to call a REST method using the token yields this exception:

WebException: The remote name could not be resolved: 'devapi.contoso.com.well-known' HttpRequestException: An error occurred while sending the request.
IOException: IDX10804: Unable to retrieve document from: 'https://devapi.contoso.com.well-known/openid-configuration'.


Consider the following steps to reproduce (this is for prototyping and should not be considered production worthy):

  • We created a beta8 prototype using OIDC as described here.

  • We deployed the project to 2 identically configured IIS 8.5 servers running on Server 2012 R2. The IIS servers host a beta8 site called "API" with bindings to port 80 and 443 for the host name "devapi.contoso.com" (sanitized for purposes of this post) on all available IP addresses.

  • Both IIS servers have a host entry that point to themselves:

    127.0.0.1 devapi.contoso.com

  • Our network admin has bound a * certificate (*.contoso.com) with our Kemp load balancer and configured the DNS entry for https://devapi.contoso.com to resolve to the load balancer.

  • Now this is important, the load balancer has also been configured to proxy https traffic to the IIS servers using http (not, repeat, not on https). It has been explained to me that this is standard operating procedure for our company because they only have to install the certificate in one place. We're not sure why our network admin bound 443 in IIS since it, in theory, never receives any traffic on this port.

  • We make a secure post via https to https://devapi.contoso.com/authorize/v1 to fetch a token, which works fine (the details of how to make this post are here ):

    {
    "sub": "todo",
    "iss": "https://devapi.contoso.com/",
    "aud": "https://devapi.contoso.com/",
    "exp": 1446158373,
    "nbf": 1446154773
    }

  • We then use this token in another secure get via https to https://devapi.contoso.com/values/v1/5.

  • OpenIdConnect.OpenIdConnectConfigurationRetriever throws the exception:

WebException: The remote name could not be resolved: 'devapi.contoso.com.well-known' HttpRequestException: An error occurred while sending the request.
IOException: IDX10804: Unable to retrieve document from: 'https://devapi.contoso.com.well-known/openid-configuration'.


We think this is happening because OIDC is attempting to consult the host specified in "options.Authority", which we set at startup time to https://devapi.contoso.com/. Further we speculate that because our environment has been configured to translate https traffic to non https traffic between the load balancer and IIS something is going wrong when the framework tries to resolve https://devapi.contoso.com/. We have tried many configuration changes including even pointing the authority to non-secure http://devapi.contoso.com to no avail.

Any assistance in helping us understand this problem would be greatly appreciated.

1条回答
Root(大扎)
2楼-- · 2019-07-14 04:50

@Pinpoint was right. This exception was caused by the OIDC configuration code path that allows IdentityModel to initiate non-HTTPS calls. In particular the code sample we were using was sensitive to missing trailing slash in the authority URI. Here is a code fragment that uses the Uri class to combine paths in a reliable way, regardless of whether the Authority URI has a trailing slash:

public void Configure(IApplicationBuilder app, IOptions<AppSettings> appSettings)
{
    .
    .
    .
    // Add a new middleware validating access tokens issued by the OIDC server.
    app.UseJwtBearerAuthentication
    (
        options => 
        {
            options.AuthenticationScheme    = JwtBearerDefaults.AuthenticationScheme                ;
            options.AutomaticAuthentication = false                                                 ;
            options.Authority               = new Uri(appSettings.Value.AuthAuthority).ToString()   ;
            options.Audience                = new Uri(appSettings.Value.AuthAuthority).ToString()   ;

            // Allow IdentityModel to use HTTP
            options.ConfigurationManager = 
                new ConfigurationManager<OpenIdConnectConfiguration>
                (
                    metadataAddress : new Uri(new Uri(options.Authority), ".well-known/openid-configuration").ToString(),
                    configRetriever : new OpenIdConnectConfigurationRetriever()                                         ,
                    docRetriever    : new HttpDocumentRetriever { RequireHttps = false }
                );
        }
    );
    .
    .
    .
}

In this example we're pulling in the Authority URI from config.json via "appSettings.Value.AuthAuthority" and then sanitizing/combining it using the Uri class.

查看更多
登录 后发表回答