ASP.NET Identity doesn't update Identity information on same request - c#

I am working on a single page application using AngularJS and ASP.NET Identity 2. I log the user in and the cookie is set; however, when I check the Identity of the user on the same request, it shows it as blank and IsAuthenticated is false. However, these are populated on subsequent requests. I was hoping to send back to the UI whether or not the user was logged in on the same request. Is this possible?
Code as requested (AngularJS makes AJAX post into WebAPI controller Login method)
[HttpPost]
[AllowAnonymous]
[Route("Login")]
public async Task<IHttpActionResult> Login(LoginModel loginModel)
{
var result = await _securityService.Login(loginModel.UserName, loginModel.Password);
if (!result)
{
ModelState.AddModelError("errorMessage", "Invalid username or password.");
return BadRequest(ModelState);
}
return Ok();
}
public async Task<bool> Login(string userName, string password, bool persistCookie = false)
{
var user = await _userManager.FindAsync(userName, password);
if (user != null)
await SignInAsync(user, persistCookie);
else
return false;
return true;
}
private async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
_authenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
_authenticationManager.SignIn(new AuthenticationProperties() {IsPersistent = isPersistent}, await CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie));
}
public Task<ClaimsIdentity> CreateIdentity(ApplicationUser user, string authenticationType)
{
return _userManager.CreateIdentityAsync(user, authenticationType);
}

You won't get a signed in identity until the next request because the call to SignIn is what's causing a cookie to be set on the response. That cookie will turn into the identity on subsequent requests, but its too late to change your current request's identity.

When using Owin authentication, the AuthenticationManager.SignIn() method barely sends a message to the cookie handler to set a cookie when the cookie handler gets to handle the request after the Web API Controller (see my blog post Understanding the Owin External Authentication Pipeline for details).
But the Login method returns true if the login was successful and false if not, so you can use that information in the Login action to send back information. If you don't only want to know if the login succeeded or not, but also want the actual identity you can change Login() to return the user in case of successful login and null if failed.

I log the user in and the cookie is set; however, when I check the Identity of the user on the same request, it shows it as blank and IsAuthenticated is false.
This is just a lack of knowledge on your part about how the ASP.Net pipeline works.
There is a fairly large pipeline of events that occur. I'm pretty sure MVC runs in the ProcessRequest method. This method is after the AuthenticateRequest event and the PostAuthenticateRequest event. This means that the entire ASP.Net authentication framework can never be updated during the ProcessRequest method. This is why you'll see almost all system do a redirect afterwards, so that the next request has all the authentication (IIdentity, IPrincipal, IsAuthenticated, etc).
I was hoping to send back to the UI whether or not the user was logged in on the same request. Is this possible?
How could the code not be able to? The first request either authenticates them or not, whatever code is doing that knows if they are authenticated.

I was hoping to send back to the UI whether or not the user was logged in on the same request. Is this possible?
Yes. As said in other responses, you can.
I just want to cover the case when you are in the same request but outside the context where the SignIn took place.
Through Owin, you could use something like this extension method:
/// <summary>
/// Check if the user was authenticated in the current request, or in a previous one
/// </summary>
public static bool IsUserAuthenticated(this IOwinContext context)
{
if (context.Request.User.Identity.IsAuthenticated)
return true;
if (null != context.Authentication.AuthenticationResponseGrant && null != context.Authentication.AuthenticationResponseGrant.Identity)
{
return context.Authentication.AuthenticationResponseGrant.Identity.IsAuthenticated;
}
return false;
}

Related

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

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

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.

user.identity.getuserid() return user id value in web api

I have an issue on sign out process as after sign out user.identity.getuserid() returns the userid value, whether it should return null.
Here is my code,
public async Task<IHttpActionResult> Logout()
{
return Ok();
}
The identity is stored in a cookie and sent to the server in the http request. Sign out tells the browser to expire/ delete the cookie. However you’ve set the breakpoint before sending back the response to the browser. Next request should have User as null

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

Some misunderstanding in authentication

I have some miss understanding in authentication using "User.Identity" and "FormsAuthentication.SetAuthCookie".
I have this action:
public ActionResult Login(string userName, string password)
{
if (Membership.ValidateUser(userName, password))
{
FormsAuthentication.SetAuthCookie(userName, true);
var isAuth = User.Identity.IsAuthenticated;
return View("Desktop");
}
return View("Login");
}
My question is why the value of the isAuth variable false after I set authentication ticket using this row(User.Identity.IsAuthenticated)?
By calling FormsAuthentication.SetAuthCookie you're simply dumping the authentication cookie to the HTTP response. At this stage the request is still considered as 'non-authenticated'.
Only the following requests (which will include the authentication cookie) will be considered as 'authenticated' and will have the User property set to the appropriate value.
If you want your HTTP request to immediately have the (just-authenticated) user set. Try this:
var user = new GenericPrincipal(new GenericIdentity(userName), null);
HttpContext.Current.User = Thread.CurrentPrincipal = currentUser;
MVC uses the standard ASP.Net pipeline. One part of the pipeline is authenticating the user. If a user is logged in after the pipeline has authenticated as anonymous, the only way to rerun the process is to redirect the user so the pipeline can authenticate the user. In your scenario, after you set the cookie, you'll need to do a redirect back to whatever action you want the user to go to.

Categories