Is there a way to get the Auth Cookie data on Anonymous endpoints? - c#

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()

Related

Authorize Attribute always returning false ASP.net MVC Identity

When I decorate a method with an Authorize roles attribute it returns false everytime. I'm trying to limit access to an admin page for users in the "Admin" role only.
I have verified that the user im currently logged in as is in fact in the "Admin" role.
I have tried to use a custom authorization attribute. Same result. I can add the code if needed.
I have found that the authorization attribute works for Users but not for Roles.
I believe this problem is somehow tied into the fact that the following does not work in my application:
User.IsInRole("Admin").
However, this statement does work:
userManager.IsInRole(user.Id, "Admin")
Here is my code:
public class AdminController : Controller
{
//[AuthLog(Roles = "Admin")] //Custom authorization attribute
[Authorize(Roles = "Admin")]
public ActionResult Users()
{
return View();
}
}
Maybe this can help with debugging:
Microsoft.AspNet.Identity.Core: V.2.1.0
Microsoft.AspNet.Identity.EntityFramework: V.2.1.0
I am open to suggestions on anything else I can post from my project in order to debug easier. I have scoured the stack for 2 weeks now.
Update 1: How user is logged in
// POST: /account/login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(AccountLoginModel viewModel)
{
// Ensure we have a valid viewModel to work with
if (!ModelState.IsValid)
return View(viewModel);
// Verify if a user exists with the provided identity information
var user = await _manager.FindByEmailAsync(viewModel.Email);
// If a user was found
if (user != null)
{
// Then create an identity for it and sign it in
await SignInAsync(user, viewModel.RememberMe);
// If the user came from a specific page, redirect back to it
return RedirectToLocal(viewModel.ReturnUrl);
}
// No existing user was found that matched the given criteria
ModelState.AddModelError("", "Invalid username or password.");
// If we got this far, something failed, redisplay form
return View(viewModel);
}
private async Task SignInAsync(IdentityUser user, bool isPersistent)
{
// Clear any lingering authencation data
FormsAuthentication.SignOut();
// Create a claims based identity for the current user
var identity = await _manager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
// Write the authentication cookie
FormsAuthentication.SetAuthCookie(identity.Name, isPersistent);
}
The
FormsAuthentication.SetAuthCookie(identity.Name, isPersistent);
unfortunately doesn't store any roles with the identity. Thus, when the identity is recreated from the cookie, you have no roles. To verify try
this.User.IsInRole("Admin")
and you'll get false, even though the userManager tells you otherwise.
There are multiple workarounds.
You could for example switch to any other identity persistor, like the SessionAuthenticationModule which could store your username and roles in the cookie. You could follow my tutorial on that.
Another approach would be to have an explicit role manager and use its feature that automatically causes your roles to be stored in another cookie, separate from the forms authentication cookie. This involves configuring the role provider and writing your own role provider that would be an adapter over the user manager.
Finally, you could forget forms authentication and use Identity's native way of issuing cookies, which would involve calling SignInAsync on the authentication manager.

Correct way to support multiple authorization attributes on ASP.Net Web API 2

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")]

how to address this security breach in web api

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.

Check if user is logged in with Token Based Authentication in ASP.NET Core

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;

How to Logout of Owin Providers?

I am following this tutorial yet it does not tell you how to logout. I tried to do
Request.GetOwinContext().Authentication.SignOut(Microsoft.AspNet.Identity.DefaultAuthenticationTypes.ExternalCookie);
Request.GetOwinContext().Authentication.SignOut()
Request.GetOwinContext().Authentication.SignOut(Microsoft.AspNet.Identity.DefaultAuthenticationTypes.ApplicationCookie);
You can get the sample code here: https://github.com/AndersAbel/SocialLoginWithoutIdentity
Just need to add one more action
public ActionResult SignOut()
{
Request.GetOwinContext().Authentication.SignOut(Microsoft.AspNet.Identity.DefaultAuthenticationTypes.ExternalCookie);
return RedirectToAction("Index", "Home");
}
This method plus any one of the 3 lines of I posted above
My result right now is, I login, I go to secure page and can see it, I then proceed to my signout and then after signout try to go back to the secure page and I am allowed back to that secure page.
So it actually did not really sign me out.
As mentioned in the tutorial, the middleWare used use the default authentication type but don't override it.
By using only externalCookie as parameter for Owin you are clearing the cookie for Asp, but not the one used to store the Google provider,
to do so, you will have to get the array of all current cookies.
It can be done the easy way like this:
Request.GetOwinContext()
.Authentication
.SignOut(HttpContext.GetOwinContext()
.Authentication.GetAuthenticationTypes()
.Select(o => o.AuthenticationType).ToArray());
This is where it is said on the Tutorial:
The call to UseGoogleAuthentication should be quite obvious why it’s needed.
But the first one toSetDefaultSignInAsAuthenticationType is not as
obvious.
login middleware normally relies on the external cookie middleware
registered before the social login middleware.
external cookie middleware, it sets itself as the default signin type.
That’s how the social login middleware knows that it should use the
external cookie. In this setup there is no external cookie, so we have
to manually set the main cookie middleware as the default signin type.
The cookie middleware will only issue a cookie if the
AuthenticationType matches the one in the identity created by the
social login middleware.Looking at the owin external authentication pipeline a socialIn the setup of the
Try setting the cache control headers.
public ActionResult SignOut() {
var authenticationTypes = new string[] {
DefaultAuthenticationTypes.ApplicationCookie,
DefaultAuthenticationTypes.ExternalCookie
};
AuthenticationManager.SignOut(authenticationTypes);
// HACK: Prevent user from being able to go back to a logged in page once logged out
Response.Cache.SetExpires(DateTime.UtcNow.AddMinutes(-1));
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetNoStore();
// now redirect
return RedirectToAction("Index", "Home");
}
private IAuthenticationManager AuthenticationManager {
get {
return Request.GetOwinContext().Authentication;
}
}
There is no stopping the user clicking the back button on the browser, unless you try JavaScript, which can be disabled. The user can go back a page and view what was on the previous page, but if they try to click any protected links or refresh the page, they will be redirected to log in.
Use the [Authorize] attribute on classes which need authorization:
[Authorize]
public class MeController : ApiController
{
// GET api/<controller>
public IEnumerable<object> Get()
{
var identity = User.Identity as ClaimsIdentity;
return identity.Claims.Select(c => new
{
Type = c.Type,
Value = c.Value
});
}
}
source: http://www.asp.net/aspnet/overview/owin-and-katana/owin-oauth-20-authorization-server

Categories