ASP.NET Core MVC with Azure OAuth behind ingress goes to infinite login loop

10/12/2019

Using a simple ASP.NET Core MVC with a template:

From the cli :

dotnet new mvc --auth SingleOrg --client-id ***** --tenant-id 3**** --domain ***.onmicrosoft.com 

This creates and scaffolds the template, all works fine on localhost.

When building and setting up behind an ingress, I am getting an infinite loop when trying to login.

This is my ingress yaml :

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
  creationTimestamp: "2019-09-11T14:06:56Z"
  generation: 3
  name: secured-ingress
  namespace: default
  resourceVersion: "5022818"
  selfLink: /apis/extensions/v1beta1/namespaces/default/ingresses/secured-ingress
  uid: 69d948fa-d49d-11e9-ac98-3ab4552521b0
spec:
  rules:
  - host: authpr.westeurope.cloudapp.azure.com
    http:
      paths:
      - backend:
          serviceName: newad
          servicePort: 80
        path: /(.*)
  tls:
  - hosts:
    - authpr.westeurope.cloudapp.azure.com
    secretName: aks-authpr
status:
  loadBalancer:
    ingress:
    - {}

When hitting the url above it redirects me to Azure AD, then it returns back to the login infinitely.

Is there anything am missing in the code?

I have read a lot of articles, seems like there are many issue with this.

I tried to implement : https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-3.0&viewFallbackFrom=aspnetcore-2.0

Played a lot with the startup.cs file, but always get the same behavior.

Infinite loop.

When I look at the debug logs, I always see :

dbug: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
      Execution plan of result filters (in the following order): Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.SaveTempDataFilter
info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
      Authorization failed.

Here is my current startup.cs file :

services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
        .AddAzureAD(options => Configuration.Bind("AzureAd", options));

services.AddMvc(options =>
                {
                    var policy = new AuthorizationPolicyBuilder()
                                        .RequireAuthenticatedUser()
                                        .Build();
                    options.Filters.Add(new AuthorizeFilter(policy));
                })
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

if (string.Equals(Environment.GetEnvironmentVariable("ASPNETCORE_FORWARDEDHEADERS_ENABLED"), "true", StringComparison.OrdinalIgnoreCase))
{
    services.Configure<ForwardedHeadersOptions>(options =>
             {
                 options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | 
                                            ForwardedHeaders.XForwardedProto;
                 // Only loopback proxies are allowed by default.
                 // Clear that restriction because forwarders are enabled by  
                 // explicit configuration.
                 options.KnownNetworks.Clear();
                 options.KnownProxies.Clear();
             });
    }
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        // app.UseHsts();
    }

    app.Use((context, next) =>
        {
            context.Request.Scheme = "https";
            return next();
        });

    app.UseForwardedHeaders();
    // app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseCookiePolicy();
    app.UseAuthentication();

    // app.Use(async (context, next) =>
    //         {
    //              if (context.Request.IsHttps || context.Request.Headers["X-Forwarded-Proto"] == Uri.UriSchemeHttps)
    //              {
    //                   await next();
    //              }
    //              else
    //              {
    //                   string queryString = context.Request.QueryString.HasValue ? context.Request.QueryString.Value : string.Empty;
    //                   var https = "https://" + context.Request.Host + context.Request.Path + queryString;
    //                   context.Response.Redirect(https);
    //              }
    //          });

app.UseMvc(routes =>
           {
                routes.MapRoute(name: "default",
                                template: "{controller=Home}/{action=Index}/{id?}");
           });
}

The expected behavior should be able to access to the url after successful auth, could be that am missing some config in the startup.cs file

-- kobi
asp.net-mvc
c#
kubernetes-ingress
oauth

1 Answer

10/13/2019

i managed to solve the issue with the help of : https://github.com/kubernetes/ingress-nginx/issues/4675

first ingress yaml should be like this :

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/proxy-buffer-size: 128k
    nginx.ingress.kubernetes.io/proxy-buffering: "on"
    nginx.ingress.kubernetes.io/proxy-buffers-number: "4"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
  creationTimestamp: 2019-09-11T14:06:56Z
  generation: 4
  name: secured-ingress
  namespace: default
  resourceVersion: "5177035"
  selfLink: /apis/extensions/v1beta1/namespaces/default/ingresses/secured-ingress
  uid: 69d948fa-d49d-11e9-ac98-3ab4552521b0
spec:
  rules:
  - host: authpr.westeurope.cloudapp.azure.com
    http:
      paths:
      - backend:
          serviceName: newad
          servicePort: 80
        path: /
  tls:
  - hosts:
    - authpr.westeurope.cloudapp.azure.com
    secretName: aks-authpr
status:
  loadBalancer:
    ingress:
    - {}

then in the startup.cs you need to set the following on top of what is generated :

 public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                //Outside dev, require HTTPS and use HSTS
                app.UseHttpsRedirection();
                app.UseHsts();
            }

            app.UseStaticFiles();
             app.Use((context, next) =>
        {
            context.Request.Scheme = "https";
            return next();
        });

            app.UseForwardedHeaders();
            app.UseAuthentication();

            app.UseMvcWithDefaultRoute();
        }
-- kobi
Source: StackOverflow