Search code examples
authenticationasp.net-core-2.0openid-connecthttp-proxyhttp-status-code-401

Asp.Net Core 2 oidc middleware does not challenge after proxy request returns 401


I'm trying to build a centralised proxy that will intercept all requests and handle authentication with openidconnect.

Currently the proxied request simply returns 401, so the middleware suppose to challenge and redirect me to the login page. The issue is using .Net Core 1.1's implemtation it work, but it doesn't seem to work in .Net Core 2.

I've simplified the code but this works, I get redirected to google's signin page.

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddAuthentication();
        services.AddProxy();
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AutomaticAuthenticate = true,
        });

        app.UseGoogleAuthentication(new GoogleOptions
        {
            AutomaticChallenge = true,
            SignInScheme = "oidc",
            ClientId = "clientId",
            ClientSecret = "clientSecret",
        });

        app.MapWhen(
            context => context.RequestStartsWith("http://www.web1.com"),
            builder => builder.RunProxy(baseUri: new Uri("http://www.proxy1.com"))
        );
    }
}

And this doesn't work with .Net Core 2.0's implementation, I'm getting a 401 exception page

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddAuthentication(options =>
        {
            options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = GoogleDefaults.AuthenticationScheme;
        })
        .AddCookie()
        .AddGoogle(options =>
        {
            options.ClientId = "clientId";
            options.ClientSecret = "clientSecret";
        });

        services.AddProxy();
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseAuthentication();

        app.MapWhen(
            context => context.RequestStartsWith("http://www.web1.com"),
            builder => builder.RunProxy(baseUri: new Uri("http://www.proxy1.com"))
        );
    }
}

Any ideas?


Solution

  • After looking through the source code, it turns out that Authentication middleware in Asp.Net Core 2 does not challenge after response returns 401 status code, so simply return HttpUnauthorizedResult does not work anymore. The reason Authorize attribute works is it returns a ChallengeResult, which it will eventually will call ChallengeAsync.

    The work around is, I've created my own middleware which handles 401 Status Code

    public class ChallengeMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly IAuthenticationSchemeProvider _schemes;
    
        public ChallengeMiddleware(RequestDelegate next, IAuthenticationSchemeProvider schemes)
        {
            _next = next;
            _schemes = schemes;
        }
    
        public async Task Invoke(HttpContext context)
        {
            context.Response.OnStarting(async () =>
            {
                if (context.Response.StatusCode == 401)
                {
                    var defaultChallenge = await _schemes.GetDefaultChallengeSchemeAsync();
                    if (defaultChallenge != null)
                    {
                        await context.ChallengeAsync(defaultChallenge.Name);
                    }
                }
                await Task.CompletedTask;
            });
    
            await _next(context);
        }
    }