I am struggling to work out which .Net authentication concepts are still relevant in the world of OWIN, and which are now obsolete. From the pre-OWIN ASP.Net days, I am used to dealing with the .Net constructs: FormsAuthentication, FormsAuthCookie, IPrincipal, IIdentity and also custom implementations of IPrincipal (inheriting from GenericPrincipal). With the latest version of MVC (5) much of the authentication seems to have been changed be to OWIN based. Two things I am trying to understand in particular:
1)Where does IPrincipal and IIdentity and GenericPrincipal fit in?
With FormsAuthentication, custom data could be stored in the FormsAuth cookie. This could then used in the ASP.Net PostAuthenticate event to create a CustomPrincipal object, and override the default IPrincipal on the HTTPContext (per code example below). How (or does) OWIN change this?:
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
//Decrypt forms authentication cookie and retrieve some userdata
...
//Create CustomPrincipal (which inherits from GenericPrincipal)
var principal = new CustomPrincipal(userId, roles, someAdditionalUserDataFromCookie);
//Replace standard IPrincipal object on HTTPContext with custom principal
HttpContext.Current.User = newUser
}
2) Where can custom auth data be stored? In the pre-OWIN days I used the UserData value of the AuthCookie to store custom identification information (in addition to the username) - such as OrgID. Can this now be stored as a Claim in the ClaimsIdentity object? Is this a good idea? Can it still be stored in the AuthenticationTicket? Am I looking at this all wrong?!
Thanks for any help.
You will use CookieAuthenticationMiddleware instead of FormsAuthenticationModule. CookieAuthenticationMiddleware still creates a cookie with an authentication ticket but the format is different. With CookieAuthenticationMiddleware, things are designed for claims from the ground up. So, by default, you get ClaimsPrincipal with ClaimsIdentity although these classes implement IPrincipal and IIdentity.
Regarding custom authentication data, store them as claims part of the identity. One good thing about the new world is that you no longer need to use PostAuthenticate to restore your principal based on the custom data in the ticket. If you create your identity with all the required claims before calling SignIn, CookieAuthenticationMiddleware takes care of serializing claims part of identity into the ticket in the cookie and back into the identity in its entirety. Also, you will not use HttpContext.Current.User to read the principal. You will read from the OWIN context using the extension method available on the request object like so.
Request.GetOwinContext().Authentication.User returns ClaimsPrincipal
Request.GetOwinContext().Request.User returns same as above but as IPrincipal
From the controller, you can use User which is IPrincipal, which again returns the one from the context.
Related
What is the different between the following 3 methods to retrieve the claim?
Called in a ApiController:
((ClaimsIdentity) HttpContext.Current.User.Identity).Claims
((ClaimsIdentity) Thread.CurrentPrincipal.Identity).Claims
((ClaimsIdentity) User.Identity).Claims
The first two attributes have stored the same data but the last one has stored the data from the previous session.
This is done in the logout method:
UserCache.Instance.Clear();
FederatedAuthentication.SessionAuthenticationModule.SignOut();
HttpContext.Current.User = new GenericPrincipal(new GenericIdentity(string.Empty), null);
Update
Mixed WebForms, WebApi, MVC Application
Most of the application is build using WebForms.
If you are working with WebApi, then HttpContext.Current should not be available directly (see this answer). So I'm guessing you are using MVC as well and you see MVC context there.
Thread.CurrentPrincipal is dangerous to use because it contains thread principle which can be something you never expect, like user that actually runs IIS (AppPool user). Most of the time it is what you think, but sometimes it is not. And this will cause you endless bug-chasing that you can never recreate yourself.
User.Identity as ClaimsIdentity is the correct way to get what you need and it is used in the default template from VS. However if you see the data from "previous session" - means your cookies are not cleared properly. And the way you sign-out user looks suspicious:
What is UserCache.Instance?
SignOut method does not actually sign out user until the request is complete. So if you call this and then check for user identity within the same request, you'll see the same identity intact.
Assigning HttpContext.Current.User will not give you much within the request. See very first point if we are talking about pure WebAPI.
Default sign-out is done via IAuthenticationManager
private IAuthenticationManager Authentication
{
get { return Request.GetOwinContext().Authentication; }
}
[Route("Logout")]
public IHttpActionResult Logout()
{
Authentication.SignOut(CookieAuthenticationDefaults.AuthenticationType);
return Ok();
}
Try this and then adjust for your needs.
I've just watched the Introduction to Identity and Access Control in .NET 4.5 video on Pluralsight. I am trying to convert an old webforms app (not MVC and not OWIN) from this...
Dim authTicket As New FormsAuthenticationTicket(1, "Bob", DateTime.UtcNow.ToLocalTime(), DateTime.UtcNow.ToLocalTime.AddMinutes(60), False, "Master")
Dim authCookie As New HttpCookie("MyApp", FormsAuthentication.Encrypt(authTicket))
...to a ClaimsIdentity and claims-based approach. Although ClaimsIdentity and its Claims are covered in the video, there is little mention of how to plug it all together and persist a user across pages (the stuff that's been done automatically before now).
For example, is this a valid alternative to the above for setting up the user's claims?
Dim fd As New FormsIdentity(New FormsAuthenticationTicket("MyApp", True, 60))
fd.AddClaims(New List(Of Claim) From {
New Claim(ClaimTypes.Name, "Bob"),
New Claim(ClaimTypes.Role, "Master")
})
Dim p As New ClaimsPrincipal(fd)
If so, how is ClaimsPrincipal then stored/persisted/retrieved on subsequent pages?
Currently I'm not using OWIN, but can introduce it if it will benefit this scenario.
AFAIK A ClaimsPrincipal is always converted to some kind of SecurityToken when "serialized". If it is sent by an STS this is a SAML or JWT security token. If is is remembered during a session then it is a SessionSecurityToken. Each of these tokens has a corresponding SecurityTokenHandler class.
In classical ASP.NET/MVC, You have two http modules : WSFederationAuthenticationModule and SessionAuthenticationModule. The latter makes sure the ClaimsPrincipal is stored across a session. By default, WIF uses a SessionSecurityTokenHandler and stores the ClaimsPrincipal in a bunch of cookies.
So your question becomes easier once you know that the road to serializaing a ClaimsPrincipal goes over a SecurityToken. You first convert it to a SecurityToken and then use a handler to "convert it to a string".
For you it boils down to instantiating the correct SecurityTokenHandler derivate. To deserialize it, you just call ValidateToken (which is a great method name to deserialize a token). This gives you a list of claims, which can easily be converted in a ClaimsIdentity and a ClaimsPrincipal.
To serialize it, you might have to pass over a SecurityTokenDescriptor (where you put your claims in) to convert your ClaimsPrincipal into a SecurityToken, then the SecurityTokenHandler can convert this into a "string".
I have a question regarding the claims in MVC 5.
So basically imagine I have a registered user in DB, now the user is going to log in, like so:
private async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
// Add more custom claims here if you want. Eg HomeTown can be a claim for the User
var homeclaim = new Claim(ClaimTypes.Country, user.HomeTown);
identity.AddClaim(homeclaim);
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
}
So in this case i add a new claim to the identity and then i sign in this identity.
Now my questions are:
What is the use of setting this claim? (because i can also get this from the db if i need it, what is the point in this case of claim)
And how do i use it later on in the code?
Setting the Claim against the identity makes your application security more efficient and saves hitting your database each time.
The method above can be known as a Claims Transformation which often involves reading in data that is transformed into claims after authentication succeeds.
In order to read it later you can do this:
//Get the current claims principal
var identity = (ClaimsPrincipal)Thread.CurrentPrincipal;
//Get the country from the claims
var country = identity.Claims.Where(c => c.Type == ClaimTypes.Country).Select(c => c.Value);
Update
Just to provide some further information to the answer as discussed in the comments below.
With a Claims based approach you also benefit from being able to use a claims authorization manager which can provide a centralized/finely grained access control to resources and actions.
If you've not used claims before it's best to think of actions against resources rather than role based permissions. That way you can drill right down and control access to each resource/action individually rather than having a multitude of roles for each one.
I personally like to use a mixture but store the roles as claims too.
That way I can use the standard authorization tags in mvc with roles, which read the claims and use thinktecture's attributes/ClaimsAuthorization to make the claims authorization manager pickup the more complicated rules.
A good link on implementing claims based authentication in MVC 4 is available here:
http://dotnetcodr.com/2013/02/25/claims-based-authentication-in-mvc4-with-net4-5-c-part-1-claims-transformation/
I have a couple of systems which uses external authentication, google authentication.
I'm just keeping the login information in a session variable and keep track of the user that way (no membership provider).
I would like to have the user identity in the HttpContext.Current.User object.
Should I assign the user manually on an event in Global.asax.cs, or could I have the user automatically identified during the session?
You could write a custom Authorize attribute which will take care of assigning the HttpContext.Current.User property from the session:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var user = httpContext.Session["username"] as string;
if (string.IsNullOrEmpty(user))
{
// we don't have any username inside the session => unauthorized access
return false;
}
// we have a username inside the session => assign the User property
// so that it could be accessed from anywhere
var identity = new GenericIdentity(user);
httpContext.User = new GenericPrincipal(identity, null);
return true;
}
}
Then simply decorate your controllers/actions that require authentication with this custom attribute.
Use a membership provider, it will give you exactly what you want. Even creating your own provider isn't too difficult, just implement the abstract class MembershipProvider and plug into config, or use some of the out-of-the-box providers.
Don't roll your own solution for something critical like security, it will have gaping security holes. Storing authentication info in the session is a really bad idea. It leaves it open to session hijacking, session replay attacks etc.
If you really want to go down the route of custom authentication. Then have a look at the code I posted here. It will show you how you can take control of the authentication cookie, and use this to create your own HttpContext.Current.User instance.
I am experimenting with FormsAuthentication (using ASP.NET MVC2) and it is working fairly well.
However, one case I can't work out how to deal with is validating the user identity on the server to ensure it is still valid from the server's perspective.
eg.
User logs in ... gets a cookie/ticket
Out of band the user is deleted on the server side
User makes a new request to the server. HttpContext.User.Identity.Name is set to the deleted user.
I can detect this fine, but what is the correct way to handle it? Calling FormsAuthentication.SignOut in the OnAuthorization on OnActionExecuting events is too late to affect the current request.
Alternatively I would like to be able to calls FormsAuthentication.InvalidateUser(...) when the user is deleted (or database recreated) to invalidate all tickets for a given (or all) users. But I can't find an API to do this.
In the global.asax, add an handler for AuthenticateRequest. In this method, the forms authentication has already taken place and you're free to modify the current principal before anything else happens.
protected void Application_AuthenticateRequest(object sender, EventArgs e) {
IPrincipal principal = HttpContext.Current.User;
if (!UserStillValid(principal)) {
IPrincipal anonymousPrincipal = new GenericPrincipal(new GenericIdentity(String.Empty), null);
Thread.CurrentPrincipal = anonymousPrincipal;
HttpContext.Current.User = anonymousPrincipal;
}
}
Just implement the UserStillValid method and you're done. It's also a good place to swap the generic principal with a custom one if you need to.