Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When using an API route, return Http Response 401 instead of redirect to login page when not authorised

I'm building an ASP.NET Core 2.0 website using MVC and WebAPI to provide access to a series of microservices. Where a WebAPI controller requires a user to be authenticated and authorised (using the Authorize attribute), any unauthorised or not-logged in user gets the response back as the entire HTML for the MVC login page.

When unauthorised users access the API, I would like to return the HTTP status code 401 and its associated error message in the response, instead of an entire HTML page.

I've looked at a few existing questions and noticed that they either refer to ASP.NET MVC (such as SuppressDefaultHostAuthentication in WebApi.Owin also suppressing authentication outside webapi) which is no good for ASP.NET Core 2.0. Or they are using a hackaround for Core 1.x, which just doesn't seem right (ASP.Net core MVC6 Redirect to Login when not authorised).

Has a proper solution been implemented in Core 2.0 that anyone is aware of? If not, any ideas how it could be implemented properly?

For reference, there's part of a controller as an example:

[Authorize]
[ApiVersion("1.0")]
[Produces("application/json")]
[Route("api/V{ver:apiVersion}/Organisation")]
public class OrganisationController : Controller
{
    ...

    [HttpGet]
    public async Task<IEnumerable<string>> Get()
    {
        return await _organisationService.GetAllSubdomains();
    }

    ...
}

And the configurations within Statup.cs:

public void ConfigureServices(IServiceCollection services)
{
    ...

    // Add API version control
    services.AddApiVersioning(options =>
    {
        options.ReportApiVersions = true;
        options.AssumeDefaultVersionWhenUnspecified = true;
        options.DefaultApiVersion = new ApiVersion(1, 0);
        options.ErrorResponses = new DefaultErrorResponseProvider();
    });

    // Add and configure MVC services.
    services.AddMvc()
        .AddJsonOptions(setupAction =>
        {
            // Configure the contract resolver that is used when serializing .NET objects to JSON and vice versa.
            setupAction.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
         });

    ...
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    ...

    app.UseStatusCodePagesWithRedirects("/error/index?errorCode={0}");

    app.UseStaticFiles();

    app.UseAuthentication();

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

       });

    ...
}
like image 880
Dezzamondo Avatar asked Jan 04 '23 00:01

Dezzamondo


1 Answers

There is an easy way to suppress redirect to Login page for unathorized requests. Just add following call of ConfigureApplicationCookie extension method in your ConfigureServices:

services.ConfigureApplicationCookie(options =>
{
    options.Events.OnRedirectToLogin = context =>
    {
        context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
        return Task.CompletedTask;
    };
});

Or if you need custom error message in response body:

services.ConfigureApplicationCookie(options =>
{
    options.Events.OnRedirectToLogin = async context =>
    {
        context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
        await context.Response.WriteAsync("Some custom error message if required");
    };
});

As far as you're using redirects to custom error pages for error codes (UseStatusCodePagesWithRedirects() call in Configure method), you should add filter for 401 error. To achieve this, remove call to UseStatusCodePagesWithRedirects and use UseStatusCodePages extension method with skip of redirect for Unauthorized code:

//app.UseStatusCodePagesWithRedirects("/error/index?errorCode={0}");
app.UseStatusCodePages(context =>
{
    if (context.HttpContext.Response.StatusCode != (int)HttpStatusCode.Unauthorized)
    {
        var location = string.Format(CultureInfo.InvariantCulture, "/error/index?errorCode={0}", context.HttpContext.Response.StatusCode);
        context.HttpContext.Response.Redirect(location);
    }
    return Task.CompletedTask;
});
like image 112
CodeFuller Avatar answered Jan 10 '23 20:01

CodeFuller