Suppress redirect on API URLs in ASP.NET Core
Asked Answered
E

3

22

I have an ASP.NET Core site that uses cookie authentication for most pages. For those pages, the default server response of providing a 302 redirect for an unauthorized client is desirable. However, the site also accepts API requests; they use API keys and have no use for cookies.

Ideally, I'd like to turn off cookie processing for the API URLs altogether, but minimally, I need to ensure that if an API client is unauthorized, the server doesn't respond with a 302 redirect.

Erechtheum answered 3/2, 2017 at 17:48 Comment(0)
E
45

Replace the redirect event handler with one that uses the default behavior only if the path is not an API. In Startup.ConfigureServices, add this:

services.ConfigureApplicationCookie(options => {
    options.Events.OnRedirectToAccessDenied = ReplaceRedirector(HttpStatusCode.Forbidden, options.Events.OnRedirectToAccessDenied);
    options.Events.OnRedirectToLogin = ReplaceRedirector(HttpStatusCode.Unauthorized, options.Events.OnRedirectToLogin);
});

Use this helper method to replace the redirect methods:

static Func<RedirectContext<CookieAuthenticationOptions>, Task> ReplaceRedirector(HttpStatusCode statusCode, Func<RedirectContext<CookieAuthenticationOptions>, Task> existingRedirector) =>
    context => {
        if (context.Request.Path.StartsWithSegments("/api")) {
            context.Response.StatusCode = (int)statusCode;
            return Task.CompletedTask;
        }
        return existingRedirector(context);
    };

With this in place, the API controller methods can call Unauthorized() and Forbid() without causing redirects.

Update: The above is for ASP.NET Core 2. The code for ASP.NET Core 1 is different.

Erechtheum answered 3/2, 2017 at 17:48 Comment(5)
+1. It should be noted that the services.ConfigureApplicationCookie() call should come after the call that sets up your authentication (in my case services.AddIdentity()), otherwise these settings will be overwrittenArgal
This seems to be a really clean way when used with spa.Ptyalin
I tried to implement your solution in my .NET Core 3.0 Web API project, but without success. It seems the action delegate parameter that is passed to ConfigureApplicationCookie is never called; I set a breakpoint on its first line, but it's never hit. And my test client app keeps getting HTTP status code 500 instead of 403 when my test API's controller calls Forbid. Any ideas?Chrysanthemum
Same problem here with .NET Core 3.1. This code doesn't seem to work at all. Any ideas?Confucianism
This is a brilliant solution and can confirm it works with .NET Core 5.0Norinenorita
A
5

For .net core 2.x here's a fix (based on Edward's answer) :

services.ConfigureApplicationCookie(options =>
        {
            options.Events = new CookieAuthenticationEvents
            {
                OnRedirectToAccessDenied = ReplaceRedirector(HttpStatusCode.Forbidden, context => options.Events.RedirectToAccessDenied(context)),
                OnRedirectToLogin = ReplaceRedirector(HttpStatusCode.Unauthorized, context => options.Events.RedirectToLogin(context))
            };
        });

where ReplaceRedirector:

Func<RedirectContext<CookieAuthenticationOptions>, Task> ReplaceRedirector(HttpStatusCode statusCode, Func<RedirectContext<CookieAuthenticationOptions>, Task> existingRedirector) =>
context =>
{
    if (context.Request.Path.StartsWithSegments("/api"))
    {
        context.Response.StatusCode = (int)statusCode;
        return Task.CompletedTask;
    }
    return existingRedirector(context);
};
Amata answered 30/11, 2017 at 20:56 Comment(1)
I updated my answer to include ASP.NET Core 2. In my update, I didn't replace Events, but rather just set its relevant properties.Erechtheum
K
2

Other simple way

 .AddCookie(options =>
            {
                options.AccessDeniedPath = "/Home/401";
                options.Events = new CookieAuthenticationEvents
                {
                    OnRedirectToAccessDenied = context => 
                    {
                        if (context.Request.Path.StartsWithSegments("/api"))
                        {
                            context.Response.StatusCode = (int)(HttpStatusCode.Unauthorized);
                        }
                        return Task.CompletedTask;
                    },
                };
            })
Kayseri answered 30/5, 2019 at 19:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.