MADC0D3R
MADC0D3R

Reputation: 331

Trying to change OIDC OnRedirectToIdentityProvider from 302 to 401

We are using Identity Server 4 with a custom login page. We've been testing what happens when the cookie expires or the token is manually modified (part of hardening).

The application wants to redirect us to https://localhost:44349/signin-oidc, and returns a 302 redirect, but /signin-oidc doesn't exist. We'd like to throw a 401 and redirect them to /home.

I've found where I can capture the events

options.Events = new OpenIdConnectEvents
    {
      OnRedirectToIdentityProvider = context => {
      var newRedirect =  context.ProtocolMessage.RedirectUri.ToString().Replace("signin-oidc", "home");
      var builder = new UriBuilder(newRedirect);
      builder.Scheme = "https";
      context.ProtocolMessage.RedirectUri = builder.ToString();
      context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
      return Task.FromResult(0);
     }
    };

But this doesn't seem to work and we simply throw a 302 error without any way to report back to user what happened.

Upvotes: 5

Views: 6996

Answers (2)

Michael Sandino
Michael Sandino

Reputation: 1958

MADC0D3R's answer works great, except that the CORS configuration is ignored for some reason.

Wasn't able to find out why, but I came up with a workaround:

OnRedirectToIdentityProvider = async context => {
  if (context.Request.Path != "/Account/Login") {
    context.Response.Clear();
    context.Response.StatusCode = 401;

    // Global CORS middleware does not work here, need to add the headers manually
    await ManuallyAddCorsHeaders(context.HttpContext, context.Response);

    context.HandleResponse();
  }
}

Implementation of ManuallyAddCorsHeaders:

private static async Task ManuallyAddCorsHeaders(HttpContext httpContext, HttpResponse response) {
    var corsService = httpContext.RequestServices.GetRequiredService<ICorsService>();
    var corsPolicyProvider = httpContext.RequestServices.GetRequiredService<ICorsPolicyProvider>();
    var defaultPolicy = await corsPolicyProvider.GetPolicyAsync(httpContext, new CorsOptions().DefaultPolicyName);
    if (defaultPolicy == null)
      return;

    var result = corsService.EvaluatePolicy(httpContext, defaultPolicy);
    corsService.ApplyResult(result, response);
}

I'm putting this here in case someone else is struggling with it, took me a bit to figure it out. Also grateful for alternative approaches.

Upvotes: 1

MADC0D3R
MADC0D3R

Reputation: 331

Found this little gem here

OnRedirectToIdentityProvider = ctx =>
{
    if (ctx.Request.Path.StartsWithSegments("/api"))
    {
        if (ctx.Response.StatusCode == (int)HttpStatusCode.OK)
        {
            ctx.Response.StatusCode = 401;
        }

        ctx.HandleResponse();
    }

    return Task.CompletedTask;
}

It does exactly what I need, which is to return a 401. The trick was in the HandleResponse().

Upvotes: 18

Related Questions