I am developing an MVC 4 Application to be hosted in Azure and want to use their ACS service for authentication. Once the user is authenticated I will use the resulting claim details to correlate to my local records. Subsequent to that, I would like to extend the claimset to include additional claims that represent local authorizations which my application would use for authorization decisions. I assume I need to replace the Principle but I'm not sure where/when to do this in MVC and want to avoid breaking any of the authentication plumbing which would normally be used throughout the life of the session. Can anyone shed some light on this?
The extensibility point in WIF for enriching the claimset is the ClaimsAuthenticationManager
From the docs:
The claims authentication manager provides an extensibility point in
the RP processing pipeline that you can use to filter, modify, or
inject new claims into the set of claims presented by an
IClaimsPrincipal before the RP application is called.
You can also add rules in ACS to enrich the token with the claims you need.
In addition to what #Eugenio Pace has said, it's worth noting that you can just add and remove claims to and from the IClaimsPrincipal:
public static void UpdateClaims(IClaimsIdentity identity)
{
identity.Claims.Remove(identity.Claims.SingleOrDefault(x => x.ClaimType == ClaimTypes.Name));
identity.Claims.Remove(identity.Claims.SingleOrDefault(x => x.ClaimType == ClaimTypes.Email));
identity.Claims.Add(new Claim(ClaimTypes.Name, "Steve Smith"));
identity.Claims.Add(new Claim(ClaimTypes.Email, "steve#smith.com"));
}
UpdateClaims(User.Identity as IClaimsIdentity);
Claims added can either be one of the types enumerated in ClaimTypes, or a custom string of your own devising. You can add multiple claims of type ClaimTypes.Role - I'm not sure about the other types.
From the ClaimsCollection docs:
Represents a collection of claims associated with a single subject.
Adding a Claim to a ClaimCollection implicitly associates that Claim
with the subject associated with the collection by calling the
SetSubject method.
Removing a Claim from a ClaimCollection implicitly removes this
association by also calling the SetSubject method.
http://msdn.microsoft.com/en-us/library/microsoft.identitymodel.claims.claimcollection.aspx
Update
For .Net 4.5, the identity class and the method for updating claims have changed, as well as the namespace:
using System.IdentityModel;
using System.Security.Claims;
public static void UpdateClaims(Member member, ClaimsIdentity identity)
{
identity.RemoveClaim(identity.Claims.SingleOrDefault(x => x.Type == ClaimTypes.Name));
identity.RemoveClaim(identity.Claims.SingleOrDefault(x => x.Type == ClaimTypes.Email));
identity.AddClaim(new Claim(ClaimTypes.Name, "Steve Smith"));
identity.AddClaim(new Claim(ClaimTypes.Email, "steve#smith.com"));
}
UpdateClaims(User.Identity as ClaimsIdentity);
http://msdn.microsoft.com/en-us/library/system.security.claims.claimsidentity.aspx
Related
In my aspnet core MVC app if I retrieve a user using an injected UserManager then the following returns true:
await _userManager.IsInRoleAsync( user, "Administrator" );
However, whenever I check User (ClaimsPrincipal) in a view like so, the claim is missing (returns false):
User.IsInRole( "Administrator" );
This seems inconsistent to me.
Do I have to manually set Role claims myself somewhere? I imagined that would come for free and that a custom UserClaimsPrincipalFactory would be for app-specific Claims as opposed to Roles (which come out-of-the-box with MVC).
What am I missing?
It's a known issue in the version of 2.1 and has been fixed in 2.2 preview-1 .
In asp.net core 2.1, AddDefaultIdentity is used and the injection becomes to
services.AddDefaultIdentity<IdentityUser>()
.AddRoles<IdentityRole>()
,will not make Roles enabled by default and it always return false for User.IsInRole.
To walk around it , instead of using the new AddDefaultIdentity<TUser>() to configure Identity , simply use the old-style api :
services.AddIdentity<IdentityUser, IdentityRole>()
.AddRoleManager<RoleManager<IdentityRole>>()
.AddDefaultUI()
.AddDefaultTokenProviders()
.AddEntityFrameworkStores<ApplicationDbContext>();
Another way is that you could replace the UserClaimsPrincipal factory with the one that's role aware.Add below code in ConfigureService and refer to UserRoles in DefaultIdentity
services.AddScoped<IUserClaimsPrincipalFactory<IdentityUser>, UserClaimsPrincipalFactory<IdentityUser, IdentityRole>>();
I'm building MVC 5 webapp with Windows Authentication.
Since I have external authorization store, so I want to add some custom claims authentication. I handle the PostAuthenticationRequest event
void Application_PostAuthenticateRequest()
{
if (Request.IsAuthenticated)
{
var id = ClaimsPrincipal.Current.Identities.First();
...
//query authorization store
id.AddClaim(new Claim(ClaimTypes.Role, "SomeRole"));
}
}
Then in controller, I check the IsInRole("SomeRole") , but I found it always return false even the identity have the SomeRole role claim
Then I found out that in Windows Authentication, the IsInRole() is using groupsid claims as role
Instead of add my custom claim to groupsid claim, how can I set the IsInRole function use the standard Role claims?
Look like RoleClaimType is the way to go, but it is readonly I cannot set it.
I don't think you can change the behavior of the WindowsPrincipal.IsInRole method.
What you can do is create a new ClaimsPrincipal from a new ClaimsIdentity, using the claims from the WindowsPrincipal. One of the ClaimsIdentity constructors lets you specify the nameType and roleType.
Having set the new ClaimsPrincipal, your IsInRole calls should work on the specified roleType.
I am completely new to the use of claims in ASP.NETIdentity and want to get an idea of best practices in the use of Roles and/or Claims.
After all this reading, I still have questions like...
Q: Do we no longer use Roles?
Q: If so, why are Roles still offered?
Q: Should we only use Claims?
Q: Should we use Roles & Claims together?
My initial thought is that we "should" use them together. I see Claims as sub-categories to the Roles they support.
FOR EXAMPLE:
Role: Accounting
Claims: CanUpdateLedger, CanOnlyReadLedger, CanDeleteFromLedger
Q: Are they intended to be mutually exclusive?
Q: Or is it better to go Claims ONLY and "fully-qualify" you claims?
Q: So what are the best practices here?
EXAMPLE: Using Roles & Claims Together
Of course, you would have to write your own Attribute logic for this...
[Authorize(Roles="Accounting")]
[ClaimAuthorize(Permission="CanUpdateLedger")]
public ActionResult CreateAsset(Asset entity)
{
// Do stuff here
return View();
}
EXAMPLE: Fully-Qualifying Your Claims
[ClaimAuthorize(Permission="Accounting.Ledger.CanUpdate")]
public ActionResult CreateAsset(Asset entity)
{
// Do stuff here
return View();
}
A role is a symbolic category that collects together users who share the same levels of security privileges. Role-based authorization requires first identifying the user, then ascertaining the roles to which the user is assigned, and finally comparing those roles to the roles that are authorized to access a resource.
In contrast, a claim is not group based, rather it is identity based.
from Microsoft documentation:
When an identity is created it may be assigned one or more claims issued by a trusted party. A claim is a name value pair that represents what the subject is, not what the subject can do.
A security check can later determine the right to access a resource based on the value of one or more claims.
You can use both in concert, or use one type in some situations and the other in other situations. It mostly depends on the inter-operation with other systems and your management strategy. For example, it might be easier for a manager to manage a list of users assigned to a role than it is to manage who has a specific Claim assigned. Claims can be very useful in a RESTful scenario where you can assign a claim to a client, and the client can then present the claim for authorization rather than passing the Username and Password for every request.
As #Claies perfectly explained, claims could be a more descriptive and is a deep kind of role. I think about them as your role's ids. I have a gym Id, so I belong to the members role. I am also in the kickboxing lessons, so I have a kickboxing Id claim for them. My application would need the declaration of a new role to fit my membership rights. Instead, I have ids for each group class that I belong to, instead of lots of new membership types. That is why claims fit better for me.
There is a a great explanation video of Barry Dorrans, talking about the advantage of using claims over roles. He also states that roles, are still in .NET for backward compatibility. The video is very informative about the way claims, roles, policies, authorization and authentication works.
Or check a related session shared by Lafi
Having used various authentication and authorisation techniques over decades, my current MVC application uses the following methodology.
Claims are used for all authorisation. Users are assigned one role (multiple roles are possible but I do not need this) - more below.
As is common practice, A ClaimsAuthorize attribute class is used. Since most controller actions are CRUD, I have a routine in the code-first database generation that iterates all controller actions and creates claim types for each controller action attribute of Read/Edit/Create/Delete. E.g. from,
[ClaimsAuthorize("SomeController", "Edit")]
[HttpPost]
For use at in an MVC View, a base controller class presents view bag items
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
// get user claims
var user = filterContext.HttpContext.User as System.Security.Claims.ClaimsPrincipal;
if (user != null)
{
// Get all user claims on this controller. In this controler base class, [this] still gets the descendant instance type, hence name
List<Claim> claims = user.Claims.Where(c => c.Type == this.GetType().Name).ToList();
// set Viewbag with default authorisations on this controller
ViewBag.ClaimRead = claims.Any(c => c.Value == "Read");
ViewBag.ClaimEdit = claims.Any(c => c.Value == "Edit");
ViewBag.ClaimCreate = claims.Any(c => c.Value == "Create");
ViewBag.ClaimDelete = claims.Any(c => c.Value == "Delete");
}
base.OnActionExecuting(filterContext);
}
For website menus and other non-controller actions, I have other claims. E.g. whether a user can view a particular monetary field.
bool UserHasSpecificClaim(string claimType, string claimValue)
{
// get user claims
var user = this.HttpContext.User as System.Security.Claims.ClaimsPrincipal;
if (user != null)
{
// Get the specific claim if any
return user.Claims.Any(c => c.Type == claimType && c.Value == claimValue);
}
return false;
}
public bool UserHasTradePricesReadClaim
{
get
{
return UserHasSpecificClaim("TradePrices", "Read");
}
}
So where do Roles fit in?
I have a table that links a Role to a (default) set of claims. When setting user authorisation, the default is to give the user the claims of their role. Each user can have more or less claims than the default. To make editing simple, the claims list is show by controller and actions (in a row), with other claims then listed. Buttons are used with a bit of Javascript to select a set of actions to minimise the "clicking" required to select claims. On Save, the users claims are deleted and all of the selected claims are added. The web application loads claims only once, so any changes must prompt a reload within this static data.
Managers can therefore select which claims are in each role and which claims a user has after setting them to a role and those default claims. The system has only a small number of users so managing this data is straightforward
To understand the difference between Roles and Claims you must face the limitation of roles and feel how claims come over these issues, so let me give you 2 scenarios to recognize the power of claims where role can't resolve these issues :
1- Your site has two modules (pages, service ..etc) the first module for children (under 18 years old) the other for adults (over 18 years old)
your user identity has a birthday claim
You need to create a policy on this claim so the authorization for each module will be given on this value and if the age of the user is over 18 years then he can go to the adult module and not before this age.
Role is Boolean data type you can have or not have the role, it doesn't have multi values.
2- Your site has role user and you want to prevent access of users to make some maintenance without changing the code.
In claims, you can create an UnderConstrain policy that if true the user can't view the page give property authorize for role user.
At the time of writing this answer we were at '.NET 5.0' with '.NET 6.0' just around the corner. And this is my understanding of what I've seen:
Q: Do we no longer use Roles?
Yep, you're not supposed to use Roles any longer (at least not the way you did it in the previous frameworks.
Q: If so, why are Roles still offered?
To make upgrading projects easier/faster?
Q: Should we only use Claims?
yes. But be sure to check out the video posted here in the answer by #Jonathan Ramos.
Q: Should we use Roles & Claims together?
No, but you can put a role into a claim ofcourse, but be sure to upgrade your project to use Claims only.
And you should not have to write you're own attributes, you should use policy for that, as it's the way of the newer framework. If you need you're own attributes you're "doing it wrong", just create your own Requirement(handler) that's what the whole 'new' policy is all about.
In the current framework the attribute ClaimAuthorize is not even available anymore.
I'm using ASP.Net Identity with ADFS 2.0 (I think).
Users log in at a separate server, come back to me, I check the ClaimsPrincipal and pull out the userID claim, then use that ID to retrieve the user.
So I'm only using ADFS/claims-based auth to get the user object; after that, I have things like usergroups and roles, but they are custom objects and I manage them in the application rather than using ADFS to manage them.
What I want to know is: How hard is it to use my custom database roles with the out-of-the-box role stuff? Specifically, I want to be able to use the Role("RoleName") attribute on controllers, and wrap some UI elements in User.IsInRole("RoleName") on the views to control user access.
What do I have to do to wire this up?
I'm not aware of any Role attribute. Do you mean Authorize("RoleName") attribute?
Adding a role claim that is compatible with IPrincipal.IsInRole and AuthorizeAttribute as a consequence is very easy. Just add a claim with the type ClaimTypes.Role
//when creating a new identity
var identity = new ClaimsIdentity(new Claim[] {
new Claim(ClaimTypes.Role, "MyRole1"),
new Claim(ClaimTypes.Role, "MyRole2")
});
//add a claim to an identity
identity.AddClaim(new Claim(ClaimTypes.Role, "MyRole3"));
Then in your controller, add the AuthorizeAttribute
Authorize("MyRole1")
I have the following ntier app: MVC > Services > Repository > Domain. I am using Forms authentication. Is it safe to use Thread.CurrentPrincipal outside of my MVC layer to get the currently logged in user of my application or should I be using HttpContext.Current.User?
The reason I ask is there seems to be some issues around Thread.CurrentPrincipal, but I am cautious to add a reference to System.Web outside of my MVC layer in case I need to provide a non web font end in the future.
Update
I have been following the advice recieved so far to pass the username into the Service as part of the params to the method being called and this has lead to a refinement of my original question. I need to be able to check if the user is in a particular role in a number of my Service and Domain methods. There seems to be a couple of solutions to this, just wondering which is the best way to proceed:
Pass the whole HttpContext.Current.User as a param instead of just the username.
Call Thread.CurrentPrincipal outside of my web tier and use that. But how do I ensure it is equal to HttpContext.Current.User?
Stick to passing in the username as suggested so far and then use Roles.IsUserInRole. The problem with this approach is that it requires a ref to System.Web which I feel is not correct outside of my MVC layer.
How would you suggest I proceed?
I wouldn't do either, HttpContext.Current.User is specific to your web layer.
Why not inject the username into your service layer?
Map the relevant User details to a new Class to represent the LoggedInUser and pass that as an argument to your Business layer method
public class LoggedInUser
{
public string UserName { set;get;}
//other relevant proerties
}
Now set the values of this and pass to your BL method
var usr=new LoggedInUser();
usr.UserName="test value "; //Read from the FormsAuthentication stuff and Set
var result=YourBusinessLayerClass.SomeOperation(usr);
You should abstract your user information so that it doesn't depend on Thread.CurrentPrincipal or HttpContext.Current.User.
You could add a constructor or method parameter that accepts a user name, for example.
Here's an overly simplified example of a constructor parameter:
class YourBusinessClass
{
string _userName;
public YourBusinessClass(string userName)
{
_userName = userName;
}
public void SomeBusinessMethodThatNeedsUserName()
{
if (_userName == "sally")
{
// do something for sally
}
}
}
I prefer option number 2( use Thread.CurrentPrincipal outside of web tier ). since this will not polute your service tier & data tier methods. with bonuses: you can store your roles + additional info in the custom principal;
To make sure Thread.CurrentPrincipal in your service and data tier is the same as your web tier; you can set your HttpContext.Current.User (Context.User) in Global.asax(Application_AuthenticateRequest). Other alternative location where you can set this are added at the bottom.
sample code:
//sample synchronizing HttpContext.Current.User with Thread.CurrentPrincipal
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
//make sure principal is not set for anonymous user/unauthenticated request
if (authCookie != null && Request.IsAuthenticated)
{
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
//your additional info stored in cookies: multiple roles, privileges, etc
string userData = authTicket.UserData;
CustomPrincipal userPrincipal = PrincipalHelper.CreatePrincipal(authTicket.Name, authTicket.UserData, Request.IsAuthenticated);
Context.User = userPrincipal;
}
}
of course first you must implement your login form to create authorization cookies containing your custom principal.
Application_AuthenticateRequest will be executed for any request to server(css files, javascript files, images files etc). To limit this functionality only to controller action, you can try setting the custom principal in ActionFilter(I haven't tried this). What I have tried is setting this functionality inside an Interceptor for Controllers(I use Castle Windsor for my Dependency Injection and Aspect Oriented Programming).
I believe you are running into this problem because you need to limit your domains responsibility further. It should not be the responsibility of your service or your document to handle authorization. That responsibility should be handled by your MVC layer, as the current user is logged in to your web app, not your domain.
If, instead of trying to look up the current user from your service, or document, you perform the check in your MVC app, you get something like this:
if(Roles.IsUserInRole("DocumentEditorRole")){
//UpdateDocument does NOT authorize the user. It does only 1 thing, update the document.
myDocumentService.UpdateDocument(currentUsername, documentToEdit);
} else {
lblPermissionDenied.InnerText = #"You do not have permission
to edit this document.";
}
which is clean, easy to read, and allows you to keep your services and domain classes free from authorization concerns. You can still map Roles.IsUserInRole("DocumentEditorRole")to your viewmodel, so the only this you are losing, is the CurrentUserCanEdit method on your Document class. But if you think of your domain model as representing real world objects, that method doesn't belong on Document anyway. You might think of it as a method on a domain User object (user.CanEditDocument(doc)), but all in all, I think you will be happier if you keep your authorization out of your domain layer.