JwtBearerAuthentication doesnt return 403 Forbidden, always returns 401 Unauthorized - asp.net-core

If ClaimsIdentity set through JwtBearerAuthentication middleware doesnt have enough roles required through
[Authorize(Roles="whateverrole")]
it returns 401 instead of 403.
I am struggling with this in asp.net core web api whole night. I have also seen this question on stackoverflow but i havent seen any solution i could make work. The order of registering middleware nor AutomaticChallange setting did the job.
I dont know if i am missing something but it seems shocking that this hasn't been solved properly for years. It is so annoying.
Is there any normal, usual, non-workaround, non-hack way of solving this?
UPDATE (in response to comment from #juunas)
I have tried that and roles are mapped correctly from Claims.
So, if i remove Roles requirement from attribute, for all roles that user is assigned to (in JWT token) User.IsInRole(x) returns true. So mapping works just fine.
About moving from roles based authorization to policies...can you provide some some link with some best practices, recommendations or something that you base that statement on?
I am not saying its not something to be done but would just like to understand it.

It's important to understand the difference in these to errors to understand why you will get one and not the other.
401 is for authentication. If you are getting this error then you have to ask yourself is the user logged in, or does the user have a current token provided by a valid token provider? If the token has expired or the provider is not valid then you can get a 401. If this is what you are getting then you need to check User.Identity.IsAuthenticated does this return true? Most likely it returns false.
403 is for authorization. This would mean the user has a valid token, but the token they have does not give them access to the resource they are requesting. This is where you would want to check User.IsInRole() and it would return false.
If you're getting 401 it means that the user hasn't been authenticated i.e. they have not logged in, their login was invalid, the token has expired... etc. As far as your application is concerned the user hasn't proved they are who they say they are yet.
Edit: Apologies for assuming the user wasn't Authenicated, I didn't see where you stated that they where in your first post. It's hard to help without seeing code but my next guess is that the Claims check hasn't been added to the services pipeline.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddAuthorization(options =>
{
options.AddPolicy("whateverrole", policy => policy.RequireClaim("whateverrole"));
});
}
This is in your Startup.cs. MS doc is here https://learn.microsoft.com/en-us/aspnet/core/security/authorization/claims
Last update: Simply put using the default Authorize attribute tag you can't change it. MS designs I this way due to the number of layers in the pipeline that could impact authentication. I was unaware of this because I use a custom Authorize attribute and forgot that I over wrote the way it handled status codes.
However I found a nice solution that might suite your needs https://github.com/aspnet/Security/issues/872#issuecomment-232624106
It adds an error page to the pipeline prior to the app.UseMvc() that redirects authentication errors to an error page that returns the correct status code.

Related

Appropriate HTTP status for redirecting to authentication in a REST api

I'm kind of surprised that, after searching for this for a while, I didn't find as many answers as I thought would be out there (well I found none), so maybe by asking it here we can help improve search results.
I'm building a REST api which has JWT-based authentication. There is an /auth/login route which returns the token after login/password verification, and the token is subsequently sent in every route in a Authorization http header.
Not, suppose that someone queries another route (say, /cars), without sending the token (that is, before logging in). If I return a 401 unauthorized, I can make the frontend query /auth/login to get the token.
But, strictly speaking, this does not conform to the REST specification, because every resource should be discoverable from the initial one, and a client accessing /cars and receiving a 401 will not know about /auth/login.
So another option would be a redirection like 302. But this semantics means that the resource was temporarily moved, and this is not the case (the resource is still /cars, you just need to authenticate first).
So, what is the correct way to do this procedure in a "true" rest api?
I 100% agree, and that's why I proposed this standard:
https://datatracker.ietf.org/doc/html/draft-pot-authentication-link-01
The idea is that for cases like this, you should be able to return a Link header with an authentication rel, so the client can discover how to proceed.

What could cause `UserManager` to return the wrong user?

Something rather scary is happening on my ASP.NET Core 2.1.0 MVC site. While I was browsing, all of a sudden it shows I am logged in as a different user (who also happens to be browsing the site at that time).
I can't pinpoint whether there is a specific use case that triggers this, but this has happened twice now. Navigating to other pages still shows I am logged in as the other user. It even seems I take over the claims of the user I am incorrectly logged in as.
My question is: what could make this happen?
EDIT: I have since changed userManager and notificationService to 'scoped' and this issue occurred again, thus the potential issue reported here cannot be the cause.
Trying to look into this, I believe the culprit might be the following call in _Layout.cshtml:
#inject UserManager<ApplicationUser> userManager
#inject NotificationService notificationService
#inject CommunityService communityService
#{
ApplicationUser user = await userManager.GetUserAsync( User );
}
The returned user is used to show user information and do calls to notificationService and communityService. These were also showing data pertaining to the incorrect (not me) user.
If it matters, this is how ApplicationDbContext is set up in Startup.cs:
// Add framework services.
services
.AddDbContext<ApplicationDbContext>( options => options
.UseLazyLoadingProxies()
.UseSqlServer(_configuration.GetConnectionString( "DefaultConnection" ) ) );
services
.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
I recalled that 'scoped' is the recommended lifetime to use when registering Entity Framework for dependency injection. Both NotificationService and CommunityService, however, are registered as 'transient' and request ApplicationDbContext through constructor injection to access data.
services.AddTransient<CommunityService, CommunityService>();
services.AddTransient<NotificationService, NotificationService>();
Could this have anything to do with it? Currently, I do not understand whether this could make any difference. I cannot seem to replicate this issue.
Good news! I was causing this myself (I believe, help me figure this out by reading the details below). You can thus rest assured that unless you are making the same mistake as I am, the ASP MVC authentication mechanism is not to blame here (at least, that is my current understanding).
I will document what exactly I did wrong, and how to replicate, since others might possibly make the same mistake.
In short: I called SignInManager<TUser>.RefreshSignInAsync(TUser) with the 'wrong' user (the one I ended up being logged in as), causing me to be logged in as that user.
Why did I do this? In my specific use case, I wanted to hand out a claim to another user based on the action of the currently logged in user. I therefore called:
await _userManager.AddClaimAsync( userToGiveClaim, newClaim );
await _signInManager.RefreshSignInAsync( userToGiveClaim );
I called RefreshSignInAsync since I wanted to prevent the user who had been given the claim from having to log out and in for the new claim to go into effect. From the RefreshSignInAsync documentation I got the impression this should work:
Regenerates the user's application cookie, whilst preserving the
existing AuthenticationProperties like rememberMe, as an asynchronous
operation.
Parameters
user The user whose sign-in cookie should be refreshed.
I'm still not entirely clear why the user that is currently logged in when this call is triggered gets the identity of the user passed to this call. That is still not how I understand the documentation (I filed this as a bug report), but since this is reproducible I am now more inclined to believe I simply misunderstood the documentation.

Where is the authentication (user/password check) in Jhipster with JWT

If I use JHipster + JWT and log in with wrong data for the first time, I get an error message (this is correct, of course). Unfortunately, I can't find where the authentication(user/password check) takes place.
The client calls'api/authenticate' and lands in the JWT filter. jwt does not yet exist here. And so it goes on in the chain.There are now 12 spring filters, but unfortunately I haven't found the check in any of them.
The'DomainUserDetailsService','UserJWT-Controller' are not called.
Question: Does anyone know where the authentication happens?
Thanks for the answer in advance
It's done in the AuthenticationManager which is built in SecurityConfiguration.java and uses DomainUserDetailsService
This is called when sending a POST to /api/authenticate which is handled by UserJWTController
I suppose you only looked at GET on /api/authenticate in AccountResource.java which is used only for checking that user is authenticated with a token.

ForbidAsync Vs ChallengeAsync why and when to use them

There are two method on AuthenticationManager class, ForbidAsync() and ChallengeAsync(), I know that I can execute HttpContext.Authentication.ForbidAsync or return a result of type ForbidResult in my controller and it has the same effect, same is true for ChallengeAsync. But it seems that they produce the same result:
public ForbidResult ForbidResult()
{
return Forbid();
}
public ChallengeResult ChallengeResult()
{
return Challenge();
}
There are not much documentation on the use of them or any example at this point, I was wondering how and why to use them.
Update: By the way, I complied my research in this area to an article by the name of Asp.Net Core Action Results Explained.
A challenge result should generally be used in cases where the current visitor is not logged in, but is trying to access an action that requires an authenticated user. It will prompt a challenge for credentials. It could also be used for an authenticated user, who is not authorised for the action, and where you want to prompt for higher privileged credentials.
A forbid result should be used in cases where the current visitor is logged in as a user in your system, but is trying to access an action that their account does not have permission to perform.
With the standard ASP.NET Core CookieAuthentication added by Identity, default paths are set to handle each case and the user gets redirected.
By default...
Access denied - i.e. forbidden looks to redirect to /Account/AccessDenied
Unauthenticated - i.e. challenge looks to redirect to /Account/Login
Without redirection, forbidden will return a 403 status code, challenge will return a 401.
In your case, as redirects are occurring as specified in the default options, you're seeing the 302 found status codes instead.
I've not looked deep into code around this, but that's my general understanding.

What OWIN Middleware Redirects After User Grants Client?

I've looked hard into this article about OAuth Authorization Server with OWIN/Katana: http://www.asp.net/aspnet/overview/owin-and-katana/owin-oauth-20-authorization-server
The article does tell us how to set up a basic Auth server but seems to omit a lot of information and code. I'm particularly interested in the implicit grant flow. They did provide the login page and the "permissions" page, but I'm confused:
Where is the code that decides whether the authenticated user has granted the client? This can't be done "behind the scenes" because we NEVER told any middleware "component" the path "/OAuth/Authorize".
Where is the code that actually redirects the user back to the client's website, along with the auto-generated access_token and other values?
I'm suspecting that there is a proper way to "construct" the ClaimsIdentity object (particularly the scope claims) before passing it to authentication.SignIn(claimsIdentity) in /OAuth/Authorize, so that it would automatically redirect the user back to the client with access and refresh tokens.
The MVC Actions of /OAuth/Authorize and /Accounts/Login seem to always return View() even after successful authentication and granting, thus never forwards the user back to the client's website. This seems like I would have to manually determine when to return Redirect(Request.QueryString["RedirectUrl"]);, and figure out the encrypted values to pass along with it. This doesn't seem like I should be generating the exact response.
What did I overlook?
As #(LittleBobby Tables) said your questions are very broad.
Based on how you asked the question you actual understand the topics but not the how?
I suggest you look at the full source code at
http://code.msdn.microsoft.com/OWIN-OAuth-20-Authorization-ba2b8783/file/114932/1/AuthorizationServer.zip
Your answers are either present or will lead you in the right direction