Why is IdentityServer redirecting to http rather than https?

4.7k views Asked by At

I have a very simple MVC5 website that I'm trying to secure with IdentityServer3.

Both my website and my IdentityServer instance are hosted as separate sites in AppHarbor. Both are behind https.

When I hit a resource in my website that is protected by an [Authorize] attribute (e.g., /Home/About), I am successfully redirected to IdentityServer, and I can successfully authenticate.

When IdentityServer POSTs its response back to the website (via app.FormPostResponse.js), the website responds with a 302 redirect to the requested resource - as expected. However, this redirect is to http, not https (see the network trace below).

I'm sure this is just something wrong with my IdentityServer config, but I'd appreciate any pointers as to what I've got wrong.

(AppHarbor uses a reverse proxy (nginx I believe) in front of IIS, where SSL terminates - so I have RequireSsl = false for this scenario, as per the IdentityServer documentation.)

Here is my website's Startup.cs

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = "Cookies"
        });

        app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
        {
            Authority = "https://<my-idsrv3>.apphb.com/identity",

            ClientId = "<my-client-id>",
            Scope = "openid profile roles email",
            RedirectUri = "https://<my-website>.apphb.com",
            ResponseType = "id_token",

            SignInAsAuthenticationType = "Cookies",

            UseTokenLifetime = false
        });

        JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
    }
}

Here is Startup.cs from my IdentityServer3 instance:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.Map("/identity", idsrvApp =>
        {
            idsrvApp.UseIdentityServer(new IdentityServerOptions
            {
                SiteName = "My Identity Server",
                SigningCertificate = Certificates.LoadSigningCertificate(),
                RequireSsl = false,
                PublicOrigin = "https://<my-idsrv3>.apphb.com",

                Factory = new IdentityServerServiceFactory()
                    .UseInMemoryUsers(Users.Get())
                    .UseInMemoryClients(Clients.Get())
                    .UseInMemoryScopes(Scopes.Get())
            });
        });
    }
}

Here is the definition of my website Client:

new Client
{
    Enabled = true,
    ClientName = "My Website Client",
    ClientId = "<my-client-id>",
    Flow = Flows.Implicit,

    RedirectUris = new List<string>
    {
        "https://<my-website>.apphb.com"
    },

    AllowAccessToAllScopes = true
}

Here is the trace from Chrome, after clicking 'Yes, Allow' on the IdentityServer consent screen:

Chrome network trace

3

There are 3 answers

0
SLP On BEST ANSWER

So it looks like this issue was caused by my client website being behind an SSL-terminating nginx front-end.

With reference to this GitHub issue, I added the following to the start of my website's app configuration:

app.Use(async (ctx, next) =>
{
    string proto = ctx.Request.Headers.Get("X-Forwarded-Proto");
    if (!string.IsNullOrEmpty(proto))
    {
        ctx.Request.Scheme = proto;
    }
    await next();
});

This makes the website aware that incoming requests were over https; this in turn appears to ensure that the IdentityServer3 middleware generates https uri's.

2
w5l On

Had the same issue running identityserver4 in an Azure App Service. Even with forced https, the generated urls in .well-known/openid-configuration were still http://.

Fixed using the same solution as the other answer, but using AspNetCore ForwardedHeadersExtensions:

var forwardOptions = new ForwardedHeadersOptions
{
    ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto,
    // Needed because of mixing http and https.
    RequireHeaderSymmetry = false,
};

// Accept X-Forwarded-* headers from all sources.
forwardOptions.KnownNetworks.Clear();
forwardOptions.KnownProxies.Clear();

app.UseForwardedHeaders(forwardOptions);

See also https://github.com/IdentityServer/IdentityServer4/issues/1331 for more discussion on this subject.

0
Enrico On

Add forwarded headers in your startup

    services.Configure<ForwardedHeadersOptions>(options =>
    {
        options.ForwardedHeaders =
            ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedHost;
    });

and

    app.UseForwardedHeaders(new ForwardedHeadersOptions()
    {
        ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
    });

Finally tell the config it has to replace the http to https in the redirect url. I'm still looking for a better way to implement this.

in your .addopenidconnect() add:

 Func<RedirectContext, Task> redirectToIdentityProvider = (ctx) =>
                     {
                         if (!ctx.ProtocolMessage.RedirectUri.StartsWith("https") && !ctx.ProtocolMessage.RedirectUri.Contains("localhost"))
                             ctx.ProtocolMessage.RedirectUri = ctx.ProtocolMessage.RedirectUri.Replace("http", "https");
                         return Task.FromResult(0);
                     };
                     opt.Events = new OpenIdConnectEvents
                     {
                         OnRedirectToIdentityProvider = redirectToIdentityProvider
                     };