Royi Namir
Royi Namir

Reputation: 148524

Asp.net Core action does not return 401 when using Authorize attribute on a thread without a principle?

I wanted to add a principle onto the thread by myself , without using the Identity mechanism which really reminds me the old membership/forms authentication mechanics.

So I've managed (successfully) to create a request with principle :

MyAuthMiddleware.cs

public class MyAuthMiddleware
{
    private readonly RequestDelegate _next;


    public MyAuthMiddleware(RequestDelegate next )
    {
        _next = next;

    }


    public async Task Invoke(HttpContext httpContext)
    {
        var claims = new List<Claim>  
        {  
            new Claim("userId", "22222222")  
        };  
        ClaimsIdentity userIdentity = new ClaimsIdentity(claims ,"MyAuthenticationType");  
        ClaimsPrincipal principal = new ClaimsPrincipal(userIdentity);

         httpContext.User = principal;
         await _next(httpContext);
    }
}

The Configure method:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            //app.UseAuthentication(); //removed it. I will set the thread manually
            if (env.IsDevelopment()) app.UseDeveloperExceptionPage();

            app.UseMyAuthMiddleware(); // <---------- Myne

            app.UseMvc();
            app.Run(async context => { await context.Response.WriteAsync("Hello World!"); });
        }

And here is the Action in the Controller: (Notice Authorize attribute)

    [HttpGet]
    [Route("data")]
    [Authorize]
    public IActionResult GetData()
    {

        var a=User.Claims.First(f => f.Type == "userId");
        return new JsonResult(new List<string> {"a", "b",a.ToString() , User.Identity.AuthenticationType});
    }

Ok Let's try calling this method, Please notice that this does work :

enter image description here

So where is the problem 😊?

Please notice that there is an [Authorize] attribute. Now let's remove
setting principle on the thread ( by removing this line ) :

//httpContext.User = principal; // line is remarked

But now when I navigate to :

http://localhost:5330/api/cities/data

I'm being redirected to :

http://localhost:5330/Account/Login?ReturnUrl=%2Fapi%2Fcities%2Fdata

But I'm expecting to see Unauthorized error.

I'm after WebApi alike responses. This is not a website but an API.

Question:

Why don't I see the Unauthorized error ? And how can I make it appear?

Nb here is my ConfigureServices:

 public void ConfigureServices(IServiceCollection services)
        {
             services.AddAuthentication( CookieAuthenticationDefaults.AuthenticationScheme ) 
                  .AddCookie( CookieAuthenticationDefaults.AuthenticationScheme,  a =>
                  {
                      a.LoginPath = "";
                      a.Cookie.Name = "myCookie";


                  });

            services.AddMvc();
        }

EDIT

Currently What I've managed to do is to use OnRedirectionToLogin :

enter image description here

But it will be really disappointing if that's the way to go. I'm expecting it to be like webapi.

Upvotes: 7

Views: 2524

Answers (1)

Kirk Larkin
Kirk Larkin

Reputation: 93043

The default implementation of the OnRedirectToLogin delegate looks like this:

public Func<RedirectContext<CookieAuthenticationOptions>, Task> OnRedirectToLogin { get; set; } = context =>
{
    if (IsAjaxRequest(context.Request))
    {
        context.Response.Headers["Location"] = context.RedirectUri;
        context.Response.StatusCode = 401;
    }
    else
    {
        context.Response.Redirect(context.RedirectUri);
    }
    return Task.CompletedTask;
};

As is clear from the code above, the response that gets sent to the client is dependent upon the result of IsAjaxRequest(...), which itself looks like this:

private static bool IsAjaxRequest(HttpRequest request)
{
    return string.Equals(request.Query["X-Requested-With"], "XMLHttpRequest", StringComparison.Ordinal) ||
        string.Equals(request.Headers["X-Requested-With"], "XMLHttpRequest", StringComparison.Ordinal);
}

This means that the response will be a 401 redirect if either a X-Requested-With request header or query-string value is set to XMLHttpRequest. When you hit your endpoint directly from the browser or from within e.g. Fiddler, this value is not set and so the response is a 302, as observed. Otherwise, when using XHR or Fetch in the browser, this value gets set for you as a header and so a 401 is returned.

Upvotes: 4

Related Questions