7

I have a .NET Core 2.0 website, running on Linux, listening on port 5000, behind an NGinX reverse proxy, which is listening on port 80 and 442. NGinX is configured to redirect HTTP traffic to HTTPS, and handle all communication over HTTPS. The NGinx Reverse Proxy and Asp.Net Core app are within their own docker containers, on a shared network.

This setup is currently working as described.

However, I would now like to Authenticate my users against our Azure Active Directory. I have run into a problem and don't know where to go from here.

First, let me show you how I have most of this configured, and then I'll explain my error.

NGinx (nginx.conf)

worker_processes 2;

events { worker_connections 1024; }

http {
    sendfile on;

    upstream docker-dotnet {
        server mycomp_prod:5000;
    }

    server {
            listen 80;
            server_name sales.mycompany.com;
            return 301 https://$host$request_uri;
    }

    server {
        listen 443 ssl;
        server_name sales.mycompany.com;
        ssl_certificate     /etc/nginx/ssl/combined.crt;
        ssl_certificate_key /etc/nginx/ssl/salesmycompany.key;

        location / {
            proxy_pass         http://docker-dotnet;
            proxy_redirect     off;
            proxy_set_header   Host $host;
            proxy_set_header   X-Real-IP $remote_addr;
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header   X-Forwarded-Host $server_name;
        }
    }

}

Dotnet:Startup.cs ConfigureServices()

services.AddAuthentication(sharedOptions =>
{
     sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
     sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddAzureAd(options => Configuration.Bind("AzureAd", options))
.AddCookie();

Dotnet:Startup.cs Configure()

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

Dotnet: appsettings.json

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "Domain": "mycompany.com",
    "TenantId": "my-tenant-id",
    "ClientId": "my-client-id",
    "CallbackPath": "/signin-oidc"
  },

In My Azure Active Directory Tentant

I have configured 2 "Reply URLs"

Now for the error / My Question

When I hit the site, I sign in. Afterward signing in, and choosing whether or not to "Stay signed in", I'm taken to a page with an error.

enter image description here

You can see in the error, that the Reply Address that is attempting to be redirected to, is HTTP, not HTTPS. I think this is coming from the line in appsettings.json that says:

"CallbackPath": "/signin-oidc"

And since my .NET Core Site (via Kestrel on port 5000) is not on HTTPS, it's trying to load http://example.com/signin-oidc, instead of HTTPS. Remember that my .NET Core Site is behind an NGinX reverse Proxy, which is configured to handle traffic on 443.

Thank you,

Captainlonate
  • 4,878
  • 4
  • 25
  • 35
  • What's your `BaseUrl` in `appsettings.json`? Should read `https://...` which then gets appended `CallbackPath` when matched against the AAD reply URL. I don't see it in your listing so try adding it. – evilSnobu Mar 17 '18 at 22:15
  • Within appsettings.json, is 'BaseUrl' a property on "AzureAd", or is it under some other Object? I looked on learn.microsoft.com, and can't seem to find documentation about this property. Can you show me to the documentation for this property? – Captainlonate Mar 17 '18 at 23:06

3 Answers3

5

I saw this and was able to get it working: https://github.com/aspnet/Security/issues/1702

        var fordwardedHeaderOptions = new ForwardedHeadersOptions
        {
            ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
        };
        fordwardedHeaderOptions.KnownNetworks.Clear();
        fordwardedHeaderOptions.KnownProxies.Clear();

        app.UseForwardedHeaders(fordwardedHeaderOptions);
charlierlee
  • 180
  • 2
  • 8
  • 1
    You sir, are FABULOUS! I've spent hours trying to figure this out and your code snippet did the trick. Thank-you. – Mike Oct 09 '18 at 14:22
  • 1
    In my testing with nginx, it helped to configure only `XForwardedProto` and not `XForwardedFor`. No need to clear anything, which I suspect can lead to other problems. Source: https://github.com/aspnet/Security/issues/1702#issuecomment-377396765 – angularsen Feb 02 '21 at 23:17
1

You should check out the documentation on Hosting on Linux.

Make sure you use UseForwardedHeaders in the middleware pipeline before UseAuthentication:

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

app.UseAuthentication();

Nginx will assign a header for the protocol used before it forwarded the call to Kestrel (among other things). This middleware will then check those headers to set the right protocol.

juunas
  • 54,244
  • 13
  • 113
  • 149
  • I should have mentioned it, but I do have that code in my Configure() method, before I call app.UseAuthentication(); I'll update my question to include that code block. – Captainlonate Mar 17 '18 at 20:54
  • Alright :) I'll keep the answer here anyway since it might be useful to someone else with a similar problem. – juunas Mar 17 '18 at 20:56
  • Thanks. But do you have any other ideas? – Captainlonate Mar 17 '18 at 20:56
  • This was the first thing I thought of, nothing else comes to mind right now. I'll get back to you if I think of something. – juunas Mar 17 '18 at 20:57
  • 1
    You need to set the x-forwarded-proto header. – Tratcher Mar 17 '18 at 22:16
  • @Tratcher I've added the following 2 lines to my nginx.conf file: Line 1: proxy_set_header X-Forwarded-Proto https; Line 2: proxy_set_header X-Forwarded-Ssl on; This seems to have had no affect. I'm running the NGinX reverse proxy, and the dotnet app within two different docker containers by the way. – Captainlonate Mar 17 '18 at 23:08
  • I don't see how the reply URL problem has anything to do with having a reverse proxy fronting the app. The `redirect_url` that gets appended to the AAD login URL looks incorrect, and that's coming from the app login logic. I would track that down, to me it seems the app isn't aware it's running at `https://something` but `http://something`. Now i really don't know how that's being constructed in ASP.NET Core.. Go and manually change `redirect_uri` in the browser login page to point to `https://`, see if that validates this finding. – evilSnobu Mar 18 '18 at 09:10
  • The redirect URL is built using `Request.Scheme`, so as far as ASP.NET Core is concerned it is running on HTTP. The X-Forwarded-Proto header should allow the app to know what protocol it is using really. – juunas Mar 18 '18 at 21:33
0

In startup.cs, you can try to force hostname and schema. The following code could help. Important: place it before setting authentication.

app.Use((context, next) =>
{
   context.Request.Host = new HostString(hostname + (port.HasValue ? ":" + port : null));
   context.Request.Scheme = protocol;
   return next();
});
Fabrizio Accatino
  • 2,284
  • 20
  • 24