Identityserver4: Need HTTPS URL for contents of .well-known/openid-configuration (using Load Balancer for HTTPS)

Created on 19 Aug 2016  路  5Comments  路  Source: IdentityServer/IdentityServer4

Currently I'm using HAProxy to proxy traffic both to my ASP.NET Core MVC application and to IdentityServer4-beta5. HAProxy allows users to connect to each using HTTPS, but Kestrel is hosting the MVC app and IdentityServer4 instances and can't use HTTPS directly. When I disable HTTPS, for IdentityServer4, everything works great. I've worked through major challenges with enabling HTTPS as well, like getting it to accept the Certificate. My trouble is that the MVC application will make a successful HTTPS connection to IdentityServer through HAProxy to retrieve .well-known/openid-configuration, BUT that configuration provides HTTP NOT HTTPS schemes for all the URLs even though I've set the following configuration:
var builder = services.AddIdentityServer(options =>
{
options.UserInteractionOptions.LoginUrl = "/ui/login";
options.UserInteractionOptions.LogoutUrl = "/ui/logout";
options.UserInteractionOptions.ConsentUrl = "/ui/consent";
options.UserInteractionOptions.ErrorUrl = "/ui/error";
options.RequireSsl = true;
options.IssuerUri = "https://loadbalancer:1943";
})

Instead, it returns:
位 docker exec -i -t c6126b44ee20 /bin/bash
root@c6126b44ee20:/app# curl https://loadbalancer:1943/.well-known/openid-configuration
{"issuer":"http://loadbalancer:1943","jwks_uri":"http://loadbalancer:1943/.well-known/openid-configuration/jwks","authorization_endpoint":"http://loadbalancer:1943/connect/authorize","token_endpoint":"http://loadbalancer:1943/connect/token","userinfo_endpoint":"http://loadbalancer:1943/connect/userinfo","end_session_endpoint":"http://loadbalancer:1943/connect/endsession","check_session_iframe":"http://loadbalancer:1943/connect/checksession","introspection_endpoint":"http://loadbalancer:1943/connect/introspect","http_logout_supported":true,"scopes_supported":["openid","profile","email","roles","offline_access","api1","api2"],"claims_supported":["sub","name","family_name","given_name","middle_name","nickname","preferred_username","profile","picture","website","gender","birthdate","zoneinfo","locale","updated_at","email","email_verified","role"],"response_types_supported":["code","token","id_token","id_token token","code id_token","code token","code id_token token"],"response_modes_supported":["form_post","query","fragment"],"grant_types_supported":["authorization_code","client_credentials","password","refresh_token","implicit","custom"],"subject_types_supported":["public"],"id_token_signing_alg_values_supported":["RS256"],"token_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post"]}

Well port 1943 is the HTTPS port I'm using but the scheme shows HTTP.

This is what happens when the MVC app retrieves the configuration:

web2_1 | fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[0]
web2_1 | An unhandled exception has occurred while executing the request
web2_1 | System.InvalidOperationException: IDX10803: Unable to obtain configuration from: 'https://loadbalancer:1943/.well-known/openid-configuration'. ---> System.IO.IOException: IDX10804: Unable to retrieve document from: 'http://loadbalancer:1943/.well-known/openid-configuration/jwks'. ---> System.Net.Http.HttpRequestException: An error occurred while sending the request. ---> System.Net.Http.CurlException: Server returned nothing (no headers, no data)
web2_1 | at System.Net.Http.CurlHandler.ThrowIfCURLEError(CURLcode error)
web2_1 | at System.Net.Http.CurlHandler.MultiAgent.FinishRequest(StrongToWeakReference`1 easyWrapper, CURLcode messageResult)

notice the https://loadbalancer:1943/.well-known/openid-configuration works great, but then you see http://loadbalancer:1943/.well-known/openid-configuration/jwks

My assumption is that the options above in the configuration (options.RequireSsl = true; and options.IssuerUri = "https://loadbalancer:1943";) should force the correct URL. Am I doing something wrong or is this a bug?

question

Most helpful comment

Great, thanks for giving some direction! Here's what I ended up using and it worked great! I added something similar to my MVC application as well to eliminate the need for other workarounds I've implemented.

        app.Use(async (context, next) =>
        {
            context.Request.Scheme = "https";
            HostString httpsHostString = new HostString("loadbalancer", 1943);
            context.Request.Host = httpsHostString;
            await next.Invoke();
        });

All 5 comments

If you're doing load balancing, then you should put middleware in the IdSvr pipeline that changes the incoming request's host header to be the right one (the pubic one).

Great, thanks for giving some direction! Here's what I ended up using and it worked great! I added something similar to my MVC application as well to eliminate the need for other workarounds I've implemented.

        app.Use(async (context, next) =>
        {
            context.Request.Scheme = "https";
            HostString httpsHostString = new HostString("loadbalancer", 1943);
            context.Request.Host = httpsHostString;
            await next.Invoke();
        });

If your load balancer uses the X-Forwarded-* or X-Original-* headers, the ForwardedHeadersMiddleware could help.

Thanks for pointing out those headers tillig. I will look into them.

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings