I am using OAuth token based authentication in my web api based project.
If user is authenticated,an access token is generated as below.
{"access_token":"FFz_DC6zzEDD4mGOCk9172ijj3sGxCUWnk-tGanm9wGk76hMB8sHI8ImeWtdUKHHGNXv465ZSlbb-3fr_hr9DqUHc9Dm9OBI7XjJhdjdOpAGAGSFOpE0Y17LCEWTjCmEZotuf42Mpgl81ewoS7OlnH4b5w4PrtzJbIBpSAMoWObziL_U3mTkeFKvWrcWOfvlSCvhhBA9Dc3UTXv3HiHKWQk0T3-pvVy7ZuW2oac-IIuaq_GYaVkIZh7s9-YjX9KAL2Z9yfrPrVOQXZe_5OcNd7nS3tdT5odchEAiuWRYQ6t7Tfb2si4T6VdAe73OYefE0se1FeQsxbOiNaLyF8OwBqymEUzEG8tEHJ-cejVbhPw","token_type":"bearer","expires_in":1799,"as:client_id":"","user":"1","role":"1",".issued":"Thu, 16 Feb 2017 09:37:44 GMT",".expires":"Thu, 16 Feb 2017 10:07:44 GMT"}
Below is one of the api method.
[Authorize]
[HttpGet]
[Route("{userId}/{type}/")]
public IHttpResponse GetCustomerDetails(int userId, string type)
{
//my api stuff
}
I am using Postman for testing api. When I pass parameters as
http://localhost:50684/api/customer/1/gold
--along the access token in token in header--
It returns the desired json.
But if I use the same token & pass the customer id = 2,still it allows the access to the other customer(with id=2).
http://localhost:50684/api/customer/2/gold
--Access token in header--
It should NOT allow to access the resource to user with id=2 since the generated access token is valid for user with id =1.
How do I prevent this security breach?
Any help/suggestion highly appreciated.
Thanks
The problem is that you send the userId as a parameter which by itself is bad design.
The simple solution is to get the current user from the context instead
[Authorize]
[HttpGet]
[Route("{type}/")]
public IHttpResponse GetCustomerDetails(string type)
{
var user = RequestContext.Principal.Identity.Name;
//my api stuff
}
You can store user id and token in some storage(session, db).
And write own MVC authorization fileter like Authorize filter, which compare token with user id stored in storage.
Currently WebApi doesn't match the concept of user id and authenticated user's id. And it shouldn't, because the only thing you specify is the route of the controller's method with some parameter. You only require the user to be authenticated, by using "Authorize" attribute, but once the access is granted, no further validation is run. To make this method available to only specific subset of your users you could either write your own generic validation (i.e. in this case check against the claims of the user which can be accessed by "User" property within controller scope, or use some out-of-the-box external implementations of handling authentication.
Related
I have this case where I have an anonymous endpoint can work on it's own but if a user have already done authentication with the cookie I need to get from that authenticated cookie session the data about the authenticated user, but as I searched online I haven't found a solution to this and the User.Identity.IsAuthenticated is always false despite the user has the authenticated session cookie but on a Anonymous endpoint it's like being ignored so I can't figure out if the user data in the cookie.
Is there any way to accomplish this?
Example of the needed behaviour:
[HttpGet,AllowAnonymous]
public async Task<IActionResult> GetSomething()
{
if(User.Identity.IsAuthenticated){
//Get data from User.Claims and act upon it
} else {
//Get data without user logic
}
}
You still must use the Authorize attribute. The AllowAnonymous attribute serves to still allow access if the user isn't authorized.
[HttpGet,Authorize,AllowAnonymous]
public async Task<IActionResult> GetSomething()
My ASP.Net Web API app created a JWT Token upon successful login.
public IHttpActionResult LogOn([FromBody] LoginRequest request)
{
var result = _service.LogOn(request);
if (result.Success)
{
var token = CreateToken(request.UserName);
return Ok(OpResult<string>.SuccessResult(token));
}
return Ok(result);
}
I have all controller methods decorated with an "Authorize" attribute which delegates to my TokenValidationHandler (inherits from DelegatingHandler) to validate the token in subsequent requests.
[HttpGet]
[Authorize]
public IHttpActionResult GetAccount(){ // get user details here}
Now I have a requirement to not let the user in unless they have created an account and verified their eMail address. So my idea is that in the first method (user login), instead of just checking result.success and issuing a token, I'd check also check if the retrieved account is eMail verified. If not I'd issue a jwt token with an additional claim "emailverified" set to false. So users hwo haven't activated their eMail can still login and get this jwt token, but the only operation they are allowed is VerifyEmail.
How do I go about implementing this VerifyEmail controller method? Ideally I want it to look like below
[HttpGet]
[AuthorizeEvenIfEmailNotVerified]
public IHttpActionResult GetAccount()
How do I implement AuthorizeEvenIfEmailNotVerified ? Is it another handler that inherits from DelegatingHandler ? But if I have two such handlers (my existing handelr for the regular authorize and this new one), then how does the ASP.Net engine know which handler to send [Authorize] attribute to and which to send [AuthorizeEvenIfEmailNotVerified] to ?
Or should I be using an AuthenticationFilter?
But in that case, it seems weird that I have two attributes doing pretty much the same thing (one authenticating a verified user and the other authenticating a non verified user). yet one of those is implemented via [Authorize] backed by handler inheriting DelegatingHandler whereas the other is implemented via an attribute backed by an AuthenticationFilter?
Or am I going about this the wrong way? For the record I'd prefer to keep the project free of any MVC related libraries unless absolutely needed. Also this is .Net Framework 4.7 project.
Probably Roles will be easiest solution. Generate token with relevant claim:
identity.AddClaim(new Claim(ClaimTypes.Role, "Verified")); //verified email
identity.AddClaim(new Claim(ClaimTypes.Role, "NotVerified")); //not verified email
Next add attribute to controller:
[Authorize(Roles="NotVerified")]
I managed to implement this token based authentication system in my application, but I have a little question. How can I check if a user is signed it (eg if the there is a valid token in the request) within the method? So with the [Authorize] ?
So I have controller, and in that controller I want to check if the user is signed in. I thought of using this:
if (_signInManager.IsSignedIn(ClaimsPrincipal.Current))
{
...
}
but it does not work since ClaimsPrincipal.Current is always null
You don't need to use the SigninManager or something similar. The user is injected on the pipeline (on the User property of the base controller) and it's info is filled automatically by the authentication middleware (cookie or token). So, on your controller:
bool isAuthenticated = User.Identity.IsAuthenticated;
yes . put [Authorize] attribute above your class or methods to check if user is authenticate or not. You can get user by this code:
var principal = User as ClaimsPrincipal;
var check = User.Identity.IsAuthenticated;
I am implemented basic authentication and authorization in web api using AuthenticationHandler : DelegatingHandler.
So before calling any api, the code in this handler gets executed which basically checks whether the user is authenticated or not.
This handler gets executed for each api call. Now my problem is that for few api like login or registration etc. where user is not logged in and I don't need to check the user authentication, how can I bypass this?
You should not get confused between authentication and authorization.
Basically, your AuthenticationHandler should only authenticate the user and sets the user identity.
The point of authentication is to say who this user is (a manager, a teller, an anonymous user,...). You should not reject the request here, it's for authorization. Example code:
public class AuthHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
//authenticate with your data storage (user,password), or decrypt the information from request's token (I don't know what approach you're doing)
// here I hardcode just for demo
//If the user is authenticated (not an anonymous user)
//create a identity for that user and set the roles for
//the user. The roles could come from your db or your decrypted token depending on how you implement your code.
GenericIdentity MyIdentity = new ClaimsIdentity("MyUser");
String[] MyStringArray = {"Manager", "Teller"};
GenericPrincipal MyPrincipal = new GenericPrincipal(MyIdentity, MyStringArray);
//Set the authenticated principal here so that we can do authorization later.
Thread.CurrentPrincipal = MyPrincipal;
if (HttpContext.Current != null)
HttpContext.Current.User = MyPrincipal;
return await base.SendAsync(request, cancellationToken);
}
}
Authorization happens after authentication to verify whether the user has the rights to access a function. That could be accomplished by applying AuthorizeAttribute:
On the action method.
On the controller. All the action methods require the user to be not anonymous. You can override this in each action method by applying AllowAnonymousAttribute
Globally by adding the AuthorizeAttribute to the application filters collection. You can use the same technique with AllowAnonymousAttribute for specific action methods. Example code extracted from the link:
In your case, you can:
Add the AuthorizeAttribute globally to your application filters collection.
Set an identity in your AuthHandler based on the authenticated user.
Apply AllowAnonymousAttribute on your login, registration action methods.
Side note: The most prominent authorization approach today is claims based security. If you have time, you should spend some time to investigate that. Basically, the idea is similar, just that we use claims instead of roles for authorization.
With claims-based in web api, you could subclass ClaimsAuthorizationManager to implement your authorization rules by overriding the CheckAccess method.
I am using ASP.NET MVC4 SimpleMembership and SimpleRoleProvider to determine authorization before exposing certain methods.
For example:
[Authorize(Roles = "Admin,Corporate")]
public ActionResult Edit(int id = 0)
{
//some code
return View(model)
}
If the user is not in the "Admin" or "Corporate" role (or their session has expired), they are correctly sent to the /Account/Login page.
However, one tester brought up a good point that once on this Login page, there is no hint as to why the user was sent here. If they simply aren't authorized to access the page they are trying to access, they keep logging in again and again and thinking the site is broken.
Ordinarily, I would add a property to the model or pass an optional parameter in the url with a message such as,
You do not have adequate permissions to access that page.
Please log in as an administrator.
or something to that effect. However, because the filter happens before they enter the method, where / how would I add the message?