the last few days I have struggled to understand Claims-Based Authorization, and I have a erious problem to apply theoretical knowledge to a real world MVC website.
Tutorials like http://www.codeproject.com/Articles/639458/Claims-Based-Authentication-and-Authorization are describing the process of installation and the very basics, but are sparse on how to manage the Claims.
Suppose a simple MVC Website which is used to save pictures of funny reindeers.
It is using Thinktecture.IdentityModel, but probably every other Identitymodel would do.
Suppose that Authentication is in place and working.
public class ReindeerController : Controller
{
//should be accesible to whole world
public ActionResult AboutReindeers()
{}
//should be accessible to users which are supposed to add Reindeers, e.g. an "Admin" (which is a role, I know)
public ActionResult AddReindeer()
{}
//Only for the Site-owner
public ActionResult DeleteReindeer()
{}
//should be accesible to registred users
public ActionResult Index()
{}
}
So I have a UserClaims Table which stores a User-ID, the type of the claim as URL and the value of the claim. But this is only the technical foundation of claims - I would like to understand if there is a good schema to create claims and to define what claims a user has / needs.
Have a look at this (old) post about claims authorization to get an overview.
Then you find some more details here, there are also many other nice posts at thinktecture.
The WIF API description should help you fill inn the blanks.
I have a UserClaims Table which stores a User-ID, the type of the claim as URL and the value of the claim.
Sounds like a perfectly fine way to store claims. Your approach is correct, and probably what I would do. My advice is not to overthink this problem, you already have a great solution. :)
Related
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 trying to get social authentication working in an asp.net-hosted Nancy web app using the WorldDomination SimpleAuthentication plugin for Nancy. TL;DRs skip to the question bolded at the bottom of the question.
Both are pretty nice, but there is a big documentation gap between the authentication process (well covered) and identifying the authenticated user during requests other than the initial authenticate request (nothing).
Nancy provides for basic and forms authentication via additional packages, and the hooks they provide are pretty straight forward. WorldDomination does not provide much information past the actual authentication process. There seems to be a distinct lack of Happy Path for the normal "who is the user making this request" process that has to happen every time a user hits the server.
I've been spending a fair amount of time to figure this part out, but my research hasn't led me to any obvious solutions. The WD demo apps are bereft of request code other than authentication requests, and the codebase doesn't appear to contain anything dealing with the normal request cycle.
My best guess is that I need to integrate with forms auth, implementing Nancy's forms auth hooks and using what I get back from WD to populate my own types.
This doesn't exactly seem like the happiest of happy paths. In fact, it seems to be more of a "do lots of work you lazy bastard" path.
What, exactly, is the recommended happy path for integrating WorldDomination's social OAuth authentication providers and Nancy? I'm concentrating on the standard "who is this person that requests of me" page lifecycle part here.
Bonus points (from my hordes of sockpuppet accounts I will create for the purpose) for how this happy path handles users logging out as well!
With Simple Authentication, we simply handle the authentication with a provider in a simple way. Every provider has slightly different implementations, different naming, different promises, so we can to consolidate all that into Simple Authentication and make it easier for a developer to implement into their website.
Thats why the Nancy.SimpleAuthentication package exists. Because we know how Nancy works we have simplified the integration into Nancy by creating the modules for you to handle redirection, authentication callback, etc.
The problem is, we simply do not know how you authenticate a user against your website.
We can handle the whole forms auth scenario ourselves, and I actually plan to in the future. (have to implement claims first which I'm 60% way through), but it will still at bare minimum require you to implement the IAuthenticationCallbackProvider
public class Test : IAuthenticationCallbackProvider
{
public dynamic Process(
NancyModule nancyModule,
AuthenticateCallbackData model)
{
//Query for the database user based on the Provider / Id
//Authenticate the user
//Call LoginWithoutRedirect, and redirect how you want...
// or LoginWithRedirect
return nancyModule.Negotiate.WithView("AuthenticateCallback")
.WithModel(model);
}
}
This class is required in order for you to authenticate the user against your database.
The thing we thought about tho is 95% of the time the user putting in the authentication, most likely already has some form of authentication already. Usually Forms Auth.
So assuming you've pulled in SimpleAuthentication, and wired up your IAuthenticationCallbackProvider class. All you really need to do is implement the Forms Auth stuff, which is pretty much 1 class, and a method call.
In the provider you need to call the LoginWithoutRedirect method so that Nancy can create an auth cookie.
Then you need to setup the IUserMapper class to tell Nancy how to get the user from the Database. If you're using RavenDB this would look something like:
public class DatabaseUser : IUserMapper
{
public IDocumentStore DocumentStore { get; set; }
public DatabaseUser(IDocumentStore documentStore)
{
DocumentStore = documentStore;
}
public IUserIdentity GetUserFromIdentifier(
Guid identifier,
NancyContext context)
{
using (var session = DocumentStore.OpenSession())
{
var member = session.Query<Member>()
.SingleOrDefault(x => x.Identifier == identifier);
if (member == null)
return null;
return new UserIdentity
{
UserName = member.DisplayName,
Claims = new []
{
"NewUser",
"CanComment"
}
};
}
}
}
Configured in the bootstrapper like:
protected override void ConfigureRequestContainer(
TinyIoCContainer container,
NancyContext context)
{
base.ConfigureRequestContainer(container, context);
container.Register<IUserMapper, DatabaseUser>();
}
protected override void RequestStartup(
TinyIoCContainer container,
IPipelines pipelines,
NancyContext context)
{
base.RequestStartup(container, pipelines, context);
var formsAuthConfiguration = new FormsAuthenticationConfiguration
{
RedirectUrl = "~/login",
UserMapper = container.Resolve<IUserMapper>(),
};
FormsAuthentication.Enable(pipelines, formsAuthConfiguration);
}
And that's really it...
I personally don't think it's a lot of code that you have to write. Both Nancy and Simple Authentication have done most of the leg work for you :)
I hope we can make SimpleAuthentication even easier in the future by removing the need for the Forms Auth, but for now I think we have a pretty good solution.
Helpful links:
http://www.philliphaydon.com/2012/12/18/forms-authentication-with-nancyfx/
http://www.philliphaydon.com/2013/01/31/oauth-with-nancyfx-and-world-domination-authentication/
The 2nd link for World Domination, although there's a bit of renaming, it's mostly the same. I do plan to do an updated blog post and revamp the wiki when we have polished off Claims.
I hope that helps you.
Edit:
I've made note to create a more end-to-end solution demo project.
While I'm well used to using the standard ASP.Net Membership Provider for new MVC web applications, I've been getting a kick out of using RavenDb lately but I still don't believe I have a grasp on the best practice for implementing user authentication and role authorisation.
The code I have replaced my Register and Logon methods with in the AccountController looks like the following:
[HttpPost]
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
using (IDocumentSession Session = DataDocumentStore.Instance.OpenSession())
{
Session.Store(new AuthenticationUser
{
Name = Email,
Id = String.Format("Raven/Users/{0}", Name),
AllowedDatabases = new[] { "*" }
}.SetPassword(Password));
Session.SaveChanges();
FormsAuthentication.SetAuthCookie(model.UserName, createPersistentCookie: false);
// ...etc. etc.
[HttpPost]
public JsonResult JsonLogOn(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
using (IDocumentSession Session = DataDocumentStore.Instance.OpenSession())
{
book Ok = Session.Load<AuthenticationUser>(String.Format("Raven/Users/{0}", Username)).ValidatePassword(Password);
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
// etc...
I've seen the RavenDb Membership Provider code that a number of people have referenced in similar posts or questions, but there also seems to be a number of people who consider this to be over the top and leveraging an inefficient API for a data store that doesn't need most of what's provided within it.
So what is the best architectural / design strategy for RavenDb authentication (not for OAuth, but Forms Authentication) and am I barking up the right tree?
I think there are a few parts to this problem. First, from the MVC project's perspective, you really want to use something that will work with the AuthorizationAttribute. This actually does not require using a MembershipProvider per se, but rather stuffing an appropriate IPrincipal into HttpContext.Current.User as that is what those attributes look at to authorize things.
From a HTTP perspective, taking advantage of the existing forms authentication infrastructure also makes a ton of sense -- it solves most of the sticky security issues you really should not solve yourself and it is very flexible in terms of working with what you provide.
From there you get to the gist of the question -- how you want to back your authentication system from a data perspective. I think that is a very tactical call -- some apps it might make sense to just use a MembershipProvider style model. But if I had an app that was very user centric where I was storing lots of user data I would probably consider rolling a custom user store based around my requirements. If you are using the Authentication bundle you could glom onto that to some extent as well. But I don't think there is a hard and fast rule at this point -- do what makes sense for your app.
One thing you should not do is use the AuthenticationUser like above -- that is meant for database system users. In SQL Server terms that would be like making every user in your app a SQL user and then authenticating against that. Which is how some old intranet products used to work but the world has moved past that now.
I am creating website (football, soccer) in ASP.NET MVC3 and I want have users (with additional information then user in default membership, these are ordinary visitors) and players which I think it is best thet they would inherit users and have some addional iformation as dress number, ... Players also could post articles, users can just comment articles. What is best way to do this? Should I use default membership provider or should I make my own or use some 3rd party solutions? And can you post some articles and tutorials for changing original provider or article for making own provider for asp.net MVC3? Or is it same as MVC2?
It is very easy to create your own Membership Provider. Just create class derived from MembershipProvider. And implement members which look into DB, for example (or any other data source).
public class YourMembershipClass: MembershipProvider
{
public override bool ValidateUser(string username, string password)
{
return YourDataLayer.ValidateUser(username, password);
}
public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
{
return YourDataLayer.GetSpecificUser(providerUserKey, userIsOnline);
}
// Implement the other methods as well
}
Then add your YourMembershipClass to web.config:
<membership defaultProvider="MlgMembership">
<providers>
<clear />
<add name="CustomMembership" type="YourMembershipClass" enablePasswordRetrieval="false" />
</providers>
</membership>
If you are looking to store profile type information e.g. first name, last name, job title etc. against each user then you should be able to use the Profile system built into ASP.NET Membership. If you are looking to store more identity related information then yes you will have to create some sort of custom membership provider. There is a good video on creating a custom provider on the ASP.NET website: http://www.asp.net/general/videos/how-do-i-create-a-custom-membership-provider
Regarding allowing different types of users to perform different actions you can use the Roles system built into ASP.NET Membership. You can tell your action methods to only allow calls from users in certain roles. For example if you had a PostArticle action method and you only wanted players to be able to access it you would have something like this:
[Authorize(Roles="Player")]
public ActionResult PostArtcile(){
return View();
}
The Authorize attribute tells MVC to only allow authenticated users in the "Player" role to call the action method. You'll still need to restrict the availability of a post article link in your front end but there are several ways to do that.
There is a great series of articles by Scott Mitchell which covers all things membership based: https://web.archive.org/web/20211020202857/http://www.4guysfromrolla.com/articles/120705-1.aspx
Have a look at this soccer Club Site asp.net starter kit.
I Advice you to:
Use Membership provider to just deal
with user registration and
authentication. And let it take care of user security stuffs (rest password, validate user ....)
Then use Roles to separate your
users to their roles ("Players,
normalUsers,..").
And NEVER use Profile provider
cause it cost so many traffic you
don't want and instead of you could
make your custom table in DataBase to
store your additional information.
Then you may use EF or any ORM to get
this information whenever you want.
Don't forget to use authorization attributes [Authorize(Roles="Players")]in your Controllers and Actions deppending on the Roles.
I would advise implementing your own membership provider, it means implementing only the bits you need and forms a foundation for all your user management.
The Membership provider is the same for WebForms and MVC, there are quite a few examples on SO and Google.
In several early previews of ASP.NET MVC, arguments to controller methods would be resolved by inspecting the query string, then the form, then the cookies and server variables collections, as documented in this post from Stephen Walther.
For example, this code used to work:
public class MyController : Controller {
// This should bind to Request.Cookies["userId"].Value
public ActionResult Welcome(int userId) {
WebUser wu = WebUser.Load(userId);
ViewData["greeting"] = "Welcome, " + wu.Name;
return(View());
}
}
but now running against the release candidate, it throws an exception because it can't find a value for userId, even though userId definitely appears in the request cookies.
Was this change covered anywhere in the release notes? If this is a change to the framework, is there now a recommended alternative to binding cookies and server variables in this way?
EDIT: Thanks to those of you who have responded so far. I may have picked a bad example to demonstrate this; our code uses cookies for various forms of "convenient" but non-essential persistence (remembering ordering of search results, that kind of thing), so it's by no means purely an authentication issue. The security implications of relying on user cookies are well documented; I'm more interested in current recommendations for flexible, easily testable techniques for retrieving cookie values. (As I'm sure you can appreciate, the above example may have security implications, but is very, very easy to test!)
I believe it was the security implications that persuaded them to take these out:
The comments in Stephen Walther's post ASP.NET MVC Tip 15, leading to Phil Haack's posting User Input in Sheep's Clothing, especially his comment here:
#Troy - Step one is to dissuade devs from that line of thinking in the first place. ;) Step one prime (in parallel) is for us to remove the possibility of this line of thinking in this case.
The larger point still stands, we can make this change (after discussing it, we probably will), but that doesn't mean that it's suddenly safe to trust action method parameters.
Coupled with the complications of how you would call these methods from the various action builder classes.
I can't seem to find any explicit documentation one way or another about the controllers behaving like this other than Stephen's post, so I guess it was "quietly dropped".
I don't believe the cookies are checked anymore, and I'm not sure if it is intentional or not.
In an app against the RC I wrote recently I used the CookieContainer code from this post and a custom authorize attribute on classes like this:
public class LoginRequiredAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
IAppCookies a = new AppCookies(new CookieContainer());
return a.UserId != null; /* Better checks here */
}
}
My AppCookies.cs just has a method for UserId like this (auto resolves to int/null for you):
public int? UserId
{
get { return _cookieContainer.GetValue<int?>("UserId"); }
set { _cookieContainer.SetValue("UserId", value, DateTime.Now.AddDays(10)); }
}
Then just make sure your web.config is setup to point to your login page like this:
<authentication mode="Forms">
<forms loginUrl="~/Login"/>
</authentication>
This means in my controller to get a UserId I need to do something like this to retrieve my cookie:
[LoginRequiredAttribute]
public class RandomController : Controller
{
BaseDataContext dbContext = new BaseDataContext();
private readonly IAppCookies _cookies = new AppCookies(new CookieContainer());
public ActionResult Index()
{
return View(new RandomViewData(dbContext, _cookies.UserId));
}
}
Besides the obvious security implications, why would you need to pass the cookie through in the route anyway?
Surely you would be better off having an Authorize attribute on the action, indicating that the user should be authenticated before the action is executed.
At the end of the day, I think (hope) Microsoft has closed this as it's quite a large security issue. If this is the case, you should consider rewriting your application to comply with this.