var user = UserManager.Find(...);
ClaimsIdentity identity = UserManager.CreateIdentity(
user, DefaultAuthenticationTypes.ApplicationCookie );
var claim1 = new Claim(
ClaimType = ClaimTypes.Country, ClaimValue = "Arctica", UserId = user.Id );
identity.AddClaim(claim1);
AuthenticationManager.SignIn(
new AuthenticationProperties { IsPersistent = true }, identity );
var claim2 = new Claim(
ClaimType = ClaimTypes.Country, ClaimValue = "Antartica", UserId = user.Id );
identity.AddClaim(claim2);
Both claim1
and claim2
are persisted across requests only for the time ClaimsIdentity
user is logged in. In other words, when user logs out by calling SignOut()
, the two claims are also removed and as such the next time this user logs in, it is no longer a member of these two claims ( I assume the two claims don't exist anymore )
The fact that claim2
is persisted across requests ( even though authentication cookie was already created when claim2
was added to the user ) suggests that claims don't get persisted across requests via authentication cookie, but via some other means.
So how are claims persisted across requests?
EDIT:
1) As far as I can tell, claims of type IdentityUserClaim
are never persisted in a cookie?
var user = UserManager.Find(...);
/* claim1 won't get persisted in a cookie */
var claim1 = new IdentityUserClaim
{ ClaimType = ClaimTypes.Country, ClaimValue = "Arctica", UserId = user.Id };
user.Claims.Add(claim1);
ClaimsIdentity identity = UserManager.CreateIdentity(
user, DefaultAuthenticationTypes.ApplicationCookie );
AuthenticationManager.SignIn(
new AuthenticationProperties { IsPersistent = true }, identity );
If my assumption is correct, is the reason why IdentityUserClaim
instances aren't persisted in a cookie because it is assumed that these claims should be stored in a DB and as such could in subsequent requests be retrieved from a DB, while claims of type Claim
usually aren't stored in a DB and hence why they need to be persisted in a cookie?
2)
If you'd like to have a deeper look how it all works, check out the source code of Katana Project
I thought Asp.net Identity 2 was not part of the Katana project ( namely, I've seen people asking when will Microsoft release the source code for Asp.Net Identity, even though Katana source code is already available )?!
thank you
Good question. Even made me do a little experiment.
This line:
AuthenticationManager.SignIn(
new AuthenticationProperties { IsPersistent = true }, identity );
Does not set a cookie. Only sets Identity
object for the later callback.
Cookie is only set when the control is passed to middleware and some OWIN internal method called Response.OnSendingHeaders
.
So your code is just adding claim2
on the identity
object that is stored in memory for later user. In theory you can even set claim1
after you have done the AuthenticationManager.SignIn
. And it will be persisted in the cookie anyway.
If you try to add a cliam like this in a controller:
public ActionResult AddNonPersistedClaim()
{
var identity = (ClaimsIdentity)ClaimsPrincipal.Current.Identity;
identity.AddClaim(new Claim("Hello", "World"));
return RedirectToAction("SomeAction");
}
This claim won't be set in the cookie and you will not see it in the next request.
If you'd like to have a deeper look how it all works, check out the source code of Katana Project, look on Microsoft.Owin.Security
and Microsoft.Owin.Security.Cookies
projects. Along with AuthenticationManager
in Microsoft.Owin.Net45
project.
Update
To answer your Edit 1 - IdentityUserClaim
is indeed persisted into the database and this is the way you can assign persisted claims to the user. You add these on the user through UserManager
await userManager.AddClaimAsync(userId, new Claim("ClaimType", "ClaimValue"));
This creates records in your database table that represents IdentityUserClaim. When next time user is logged in, these claims are read from the database and added to the identity and are available on ClaimsIdentity.Current
via property .Claims
or by method .HasClaim()
.
IdentityUserClaim
does not do anything else - just way to serialise Claim
object into the database. You don't usually access these directly, unless you want to go "bare knuckles" and write to that table yourself, outside of UserManager
.
To put it another way - Identity does not set the cookie. OWIN creates the cookie. Have a look on this piece of code:
public async Task SignInAsync(IAuthenticationManager authenticationManager, ApplicationUser applicationUser, bool isPersistent)
{
authenticationManager.SignOut(
DefaultAuthenticationTypes.ExternalCookie,
DefaultAuthenticationTypes.ApplicationCookie,
DefaultAuthenticationTypes.TwoFactorCookie,
DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie,
DefaultAuthenticationTypes.ExternalBearer);
var identity = await this.CreateIdentityAsync(applicationUser, DefaultAuthenticationTypes.ApplicationCookie);
identity.AddClaim(new Claim(ClaimTypes.Email, applicationUser.Email));
authenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
}
Here Authentication manager is part of OWIN. Identity
is part of System.Security.Claims
. All that belongs to Identity project is CreateIdentityAsync
method - that is basically converts user from the database into ClaimsIdentity
with all the persisted roles and claims.
To answer your Edit 2: You are correct, AspNet Identity is not part of Katana project, but Identity uses OWIN (part of Katana) for cookie handling and authorisation. Identity project mostly deals with user/roles/claims persistence and user management, like locking-out, user creation, sending emails with password resetting, 2FA, etc.
What was a surprise for me is that ClaimsPrincipal along with ClaimsIdentity and Claim are part of .Net framework that is available outside of OWIN or Identity. These are used not only in Asp.Net, but in Windows applications. Good thing that .Net now has open-source and you can browse through all these - gives you a better understanding how it all works together. Also if you are doing unit-testing, it is invaluable to know the internals, so you can stub-out all the functionality without using mocks.