MVC5 Claims version of the Authorize attribute - asp.net-mvc

I'm trying out some of the new stuff in VS2013 RC with MVC5 and the new OWIN authentication middleware.
So, I'm used to using the [Authorize] attribute to limit actions by role but I'm trying to use claims/activity based authorization, and I can't find an equivalent attribute for it.
Is there an obvious one I'm missing or do I need to roll my own? I kinda expected there to be one out of the box.
What I'm looking for specifically is something along the lines of [Authorize("ClaimType","ClaimValue")] I suppose.
Thanks in advance.

I ended up just writing a simple attribute to handle it. I couldn't find anything in the framework right out of the box without a bunch of extra config. Listed below.
public class ClaimsAuthorizeAttribute : AuthorizeAttribute
{
private string claimType;
private string claimValue;
public ClaimsAuthorizeAttribute(string type, string value)
{
this.claimType = type;
this.claimValue = value;
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
var user = filterContext.HttpContext.User as ClaimsPrincipal;
if (user != null && user.HasClaim(claimType, claimValue))
{
base.OnAuthorization(filterContext);
}
else
{
base.HandleUnauthorizedRequest(filterContext);
}
}
}
Of course, you could remove the type and value params if you were happy to use the controller-action-verb triplet for claims somehow.

You wouldn't check for claims specifically, but rather for action/resource pairs. Factor out the actual claims / data checks into an authorization manager. Separation of concerns.
MVC and ClaimsPrincipalPermission is not a good match. It throws a SecurityException and is not unit testing friendly.
My version is here:
http://leastprivilege.com/2012/10/26/using-claims-based-authorization-in-mvc-and-web-api/

I found that you can still use the Authorization attribute with roles and users, with claims.
For this to work, your ClaimsIdentity have to include 2 specific claim types:
ClaimTypes.Name
and
ClaimTypes.Role
Then in your class derived from OAuthAuthorizationServerProvider, in the GrantXX methods you use, when you create your ClaimsIdentity, add these 2 claims.
Example:
var oAuthIdentity = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Name, context.ClientId),
new Claim(ClaimTypes.Role, "Admin"),
}, OAuthDefaults.AuthenticationType);
Then on any action you can use [Authorize(Roles ="Admin")] to restrict access.

In ASP.NET Core 3, you can configure security policies like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddAuthorization(options =>
{
options.AddPolicy("EmployeeOnly", policy => policy.RequireClaim("EmployeeNumber"));
});
}
then use AuthorizeAttribute to require the user meet the requirements of a specific policy (in other words, meet the claim backing that policy).
[Authorize(Policy = "EmployeeOnly")]
public IActionResult VacationBalance()
{
return View();
}
Source.

[ClaimsPrincipalPermission(SecurityAction.Demand, Operation="Delete", Resource="Customer")]
public ActionResult Delete(int id)
{
_customer.Delete(id);
return RedirectToAction("CustomerList");
}
ClaimsPrincipalPermissionAttribute Class

Related

How to implement role authorization with custom database?

I have an application which requires role authorization using custom database. The database is set up with a tblUsers table that has a reference to a tblRoles table. The users are also already assigned to their roles.
I also want to use the [Authorize(Role = "RoleName")] attribute on each action to check if an authenticated user is assigned to "RoleName" in the database. I'm having a lot of trouble figuring out where I need to make a modification to the [Authorize] attribute so it behaves that way. I just want to see if a username has a role, I won't have a page to manage roles in the database.
I have tried implementing custom storage providers for ASP.NET Core Identity, but it's starting to look like this is not what I need because I'm not gonna be managing roles within the application, and I can't tell how it affects the behavior of [Authorize] attribute.
Also, it's likely that I have a false assumption in my understanding on how the [Authorize] attribute even works. If you notice it, I would appreciate if you could point it out.
I had a similar problem when my client asked for granular permissions for each role. I couldn't find a way to modify the Authorize attribute but was able to implement the solution with a custom attribute. But it depends on one thing i.e can you get the userId of the calling user? I used cookie authentication so I just include the userId in my claims when someone logs in so when a request comes I can always get it from there. I think the built-in Session logic in asp.net might get the job done too, I can't say for sure though. Anyways the logic for custom authorization goes like this:
Load users and roles from database to cache on startup. If you haven't set up a cache in your program (and don't want to) you can simply make your own for this purpose by making a UserRoleCache class with 2 static lists in it. Also there are several ways of loading data from db on startup but I found it easy to do that directly in Program.cs as you'll see below.
Define your custom attribute to check if the calling user has the required role by iterating over lists in cache and return 403 if not.
Modify your Program class like:
public class Program
{
public static async Task Main(string[] args)
{
IWebHost webHost = CreateWebHostBuilder(args).Build();
using (var scope = webHost.Services.CreateScope())
{
//Get the DbContext instance. Replace MyDbContext with the
//actual name of the context in your program
var context = scope.ServiceProvider.GetRequiredService<MyDbContext>();
List<User> users = await context.User.ToListAsync();
List<Role> roles = await context.Role.ToListAsync();
//You may make getters and setters, this is just to give you an idea
UserRoleCache.users = users;
UserRoleCache.roles = roles;
}
webHost.Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}
Then comes the logic for checking if user has a role. Notice I've used an array of roles because sometimes you'll want to allow access to multiple roles.
public class RoleRequirementFilter : IAuthorizationFilter
{
private readonly string[] _roles;
public PermissionRequirementFilter(string[] roles)
{
_roles = roles;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
bool hasRole = false;
//Assuming there's a way you can get the userId
var userId = GetUserId();
User user = UserRoleCache.users.FirstOrDefault(x => x.Id == userId);
//Where roleType is the name of the role like Admin, Manager etc
List<Role> roles = UserRoleCache.roles.FindAll(x => _roles.Contains(x.RoleType))
foreach(var role in roles)
{
if(user.RoleId == role.Id)
{
hasRole = true;
break;
}
}
if (!hasRole)
context.Result = new StatusCodeResult(403);
}
}
Finally make the Role attribute
public class RoleAttribute : TypeFilterAttribute
{
public RoleAttribute(params string[] roles) : base(typeof(RoleRequirementFilter))
{
Arguments = new object[] { roles };
}
}
Now you can use the Role attribute in your controllers:
public class SampleController : ControllerBase
{
[HttpGet]
[Role("Admin", "Manager")]
public async Task<ActionResult> Get()
{
}
[HttpPost]
[Role("Admin")]
public async Task<ActionResult> Post()
{
}
}

Authentification and Authorization in ASP.NET MVC 5

I am very confused with Authentication and Authorization in ASP.NET MVC 5.
I am working on an existing website and I need to add security in it. By security I mean Authentication (Logins) and Authorization (Roles). I have access to a Webservice, but not directly to the database though I can access the Entities (Users, Roles etc.).
Membership Provider seems to be a bit old, so I took a look at Identity but it looks complicated to implement to an existing project, especially when I don't have direct access to the database.
What would be a good solution ? What are the best practices ?
Could you suggest me any good resource so I can suits my needs ?
Thank you.
In case someone feel as lost as I was, here is a potential solution using Claims. Ath the end, you will know how to handle Authentication, Authorization and Roles.
Hope this can help.
Startup config
In the root folder off my project I have created a file, startup.cs. She contains a partial class that we will use to configure the application to use a cookie that store the signed user.
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
}
Then, in the App_Start I have a file, Startup.Auth.cs
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login")
});
}
}
Controller
First, I have created an AcountController.cs with attribute of type IAuthenticationManager. This attribute gets the authentication middleware functionality available on the current request.
public class CompteController : Controller
{
private IAuthenticationManager AuthenticationManager
{
get
{
return HttpContext.GetOwinContext().Authentication;
}
}
}
Then, I have a classic view called Login with GET and POST. In the post I check in my Webservice if the user can Log In. If he can, I call a the magic function to authenticate. In this code, the class User is the custom User I get in the Webservice. He don't implement IUser.
private void AuthentifyUser(User user, bool isPersistent)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
CustomIdentity identity = new CustomIdentity(user);
CustomPrincipal principal = new CustomPrincipal(identity);
Thread.CurrentPrincipal = principal;
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
}
Last important method in my Controller allow users to Log Out.
public ActionResult Deconnexion()
{
AuthenticationManager.SignOut();
return RedirectToAction("Login", "Account");
}
Claims
CustomIdentity and CustomPrincipal are two custom class that I use for the Claims system. They indirectly implement IIdentity and IPrincipal. I put them in a separate new folder.
-Remember, A principal object represents the security context of the user on whose behalf the code is running, including that user's identity (IIdentity) and any roles to which they belong.
-An identity object represents the user on whose behalf the code is running.
public class HosteamIdentity : ClaimsIdentity
{
public HosteamIdentity(User user)
: base(DefaultAuthenticationTypes.ApplicationCookie)
{
AddClaim(new Claim("IdUser", user.Id.ToString()));
AddClaim(new Claim(ClaimTypes.Name, user.Name));
AddClaim(new Claim(ClaimTypes.Role, user.Role));
}
public int IdUser
{
get
{
return Convert.ToInt32(FindFirst("IdUser").Value);
}
}
//Other Getters to facilitate acces to the Claims.
}
The Principal gives us access to the Identity.
public class HosteamPrincipal : ClaimsPrincipal
{
private readonly HosteamIdentity _identity;
public new HosteamIdentity Identity
{
get { return _identity; }
}
public HosteamPrincipal(HosteamIdentity identity)
{
_identity = identity;
}
public override bool IsInRole(string role)
{
return _identity.Role == role;
}
}
Access the CustomPrincipal
Now, I will lgo to the gGlobal.asax, here we will override the Application_PostAuthenticateRequest event. This event is fired when a security module has established the identity of the user.
We will use Thread.CurrentPrincipal, this static object Gets or sets the thread's current principal (for role-based security), so it is perfectly adapted to our case !
You may have to adapt the code here. I personally have to request my Webservice, this may not be your case.
Just talking briefly about our constructors. The fist is empty, we will use it when we don't care about Roles
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
Thread.CurrentPrincipal = new HosteamPrincipal(
new HosteamIdentity(
WebService.GetUser(
HttpContext.Current.User.Identity.Name)));
}
}
In most case, retrieving the user by is Name is not a good practice. Please, adapt the above code to your solution.
Authorize Attribute Filter
Now, it will be great if we could easily tell which Controller or Action can be accessed by an authenticated user. To do so, we will use Filters.
Filters are custom classes that provide both a declarative and programmatic means to add pre-action and post-action behavior to controller action methods. We use them as annotation, for example [Authorize] is a Filter.
As there is to many things to explain, I will let you read the comments, they are very explicit.
Just talking briefly about our Constructors.
-The first one is empty, we will use it when we don't care about Roles. We access it by writing the annotation [CustomAuthorize] abose a Controller or an Action.
-The second one, takes an array of Roles, we will use it by writing the annotation [CustomAuthorize("Role1", "Role2", etc.)] abose a Controller or an Action. He will define which Roles access the Controller or action
public class CustomAuthorize : AuthorizeAttribute
{
private new string[] Roles { get; set; }
public CustomAuthorize() { }
public CustomAuthorize(params string[] roles)
{
this.Roles = roles[0].Split(',');
}
/// <summary>
/// Check for Authorizations (Authenticated, Roles etc.)
/// </summary>
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext.Request.IsAuthenticated)
if (Roles != null)
{
foreach (string role in Roles)
if (((HosteamPrincipal)Thread.CurrentPrincipal).IsInRole(role))
return true;
return false;
}
else
return true;
return false;
}
/// <summary>
/// Defines actions to do when Authorizations are given or declined
/// </summary>
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (!AuthorizeCore(filterContext.HttpContext))
HandleUnauthorizedRequest(filterContext);
}
/// <summary>
/// Manage when an Authorization is declined
/// </summary>
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAuthenticated)
filterContext.Result = new HttpStatusCodeResult(HttpStatusCode.Forbidden);
else
base.HandleUnauthorizedRequest(filterContext);
}
}

Impersonate for one request (Asp.net MVC)

In my ASP.net MVC project I've got (among other roles) moderators and users. I want to give the moderators the option to "see current page as user".
My approach is to create a ActionFilterAttribute and overload OnActionExecuting & OnResultExecuted as the page is then rendered for the given user.
The first idea there was to juggle with the Roles:
OnActionExecuting {
... //various checks, if role exist, if user want to switch
var tempRoles = Roles.getRolesForUser(user);
filterContext.HttpContext.Items["tempRole"] = tempRoles;
Roles.RemoveUserFromRoles(user, tempRoles)
Roles.AddUserToRole(user, targetRole);
}
and then
OnResultExecuted {
//if switched view
{
Roles.RemoveUserFromRole(user,targetRole)
Roles.AddUserToRoles(filterContext.HttpContext.Items["tempRole"])
}
This works, but in a worst case scenario the roles are gone, so i prefer to not touch them...
My second idea was to create a dummy user add him to the userroles sign the moderator into this account with FormsAuthentication.SetAuthCookie(dummyUser, true) and revert everything in the OnResultExecuted, so in a worst case scenario the user is in the dummyRole (where he can logout) and the dummyUser is in the Database.
After debugging and researching I realised that SetAuthCookie requires a Redirect to come into effect - so it doesn't work this way.
The questions:
Is there a way to force SetAuthCookie to come into affect without a redirect
Any other suggestion/approaches how to accomplish this "see page as other user"?
If my first idea is the only solution, how do i make it foolproof?
Ahoi Christian,
you could decorate the class SqlRoleProvider and add it to the role manager.
See Sample Role-Provider Implementation:
http://msdn.microsoft.com/en-us/library/tksy7hd7%28v=vs.100%29.aspx
The decorated SqlRoleProvider could overwrite the IsUserInRole method and thereby implement impersonation functionality.
edit: I have added the code below:
public class MyRoleProvider : SqlRoleProvider
{
private static ConcurrentDictionary<string, string> impersonationList;
public MyRoleProvider() : base()
{
impersonationList = new ConcurrentDictionary<string, string>();
}
public static void startImpersonate(string username, string rolename)
{
impersonationList.TryAdd(username,rolename);
}
public override string[] GetRolesForUser(string username) {
if (impersonationList.ContainsKey(username))
return new string[] { impersonationList[username] };
else
return base.GetRolesForUser(username);
}
public static void stopImpersonate(string username)
{
string rolename;
impersonationList.TryRemove(username, out rolename);
}
}

Can you wrap the RolePrincipal in a custom IPrincipal object?

I am using custom Membership and Role providers inside the ASP.NET framework with Forms Authentication. These are working great. The Role provider is using a cookie to persist the roles, saving a trip to the database on each web request. I am also using the UserData string inside the FormsAuthenticationTicket to store the UserId.
I need to refactor my DAL out of the web project to its own project. The DAL has a dependency on retrieving the Current user’s ID as well as checking the roles for rights.
How should my Authentication system change so I can use the Thread.CurrentPrincipal without referencing System.Web in the DAL project?
Currently the Provider Framework creates a RolePrincipal and FormsIdentity object and attaches it the the Thread.CurrentPrincipal.
I thought about creating a custom IPrincipal wrapper around the RolePrincipal during the Application_PostAuthenticateRequest event. In this event I can get the UserID from the FormsAuthenticalTicket and pass it to this new wrapperPrincipal along with the RolePrincipal.
Is this a valid approach? Will I end up causing some issues farther down in the project by messing with the Provider structure?
Thank you,
Keith
protected void Application_PostAuthenticateRequest()
{
if (Request.IsAuthenticated)
{
FormsIdentity identity = (FormsIdentity)User.Identity;
if (identity != null)
{
FormsAuthenticationTicket ticket = identity.Ticket;
int id = 1;
if (identity != null)
{
int.TryParse(identity.Ticket.UserData, out id);
}
var wrapperPrincipal = new WrapperPrincipal(User, id);
System.Threading.Thread.CurrentPrincipal = WrapperPrincipal;
}
}
}
[Serializable]
public class WrapperPrincipal : IPrincipal
{
private IPrincipal principal;
public WrapperPrincipal(IPrincipal principal, int userId)
{
this.principal = principal;
this.Id = userId;
}
public int Id { get; set; }
public IIdentity Identity
{
get { return principal.Identity; }
}
public bool IsInRole(string role)
{
return principal.IsInRole(role);
}
}
i came across this question recently when i was trying to implement custom principal too. I did wrapper too and i think it is valid. It works perfectly with LukeP's approach in ASP.NET MVC - Set custom IIdentity or IPrincipal.
You cant mess anything because it is just wrapper, u just delegete origin principal members, you dont even touch it. I would call it clever solution.

Configure authorized roles dynamically via a config file in MVC Application

I current have the following attribute decorating one of the action method.
[Authorize(Roles = "Admin")]
public ActionResult DoAdminTask()
{
//Do something
return View();
}
Currently, only users in the Admin role can invoke this method, but this will change. Is there anyway I can store a list of authorised roles in a config file, rather than hard coding it into the source?
EDIT: Roles will change over time, and more than 1 role will need access.
i.e. Users in either role A OR role B can access.
No way to do this with the standard authorize attribute, but you could extend the authorize attribute with your own custom authorize attribute and have it use a configuration file to determine the mapping between controller/action and the set of roles.
but you can use something like
public static class AppRoles
{
public const string Users = "UsersRoleName";
public const string Admin = "AdminRoleName";
}
and then Controller can have authorize attribute as
[Authorize(Roles = AppRoles.Admin)]
I felt this question deserved an answer with a code sample... Taking #tvanfosson's suggestion of extending the AuthorizeAttribute class, here's what I came up with (criticism is more than welcome).
AuthorizeFromConfiguration.cs:
public class AuthorizeFromConfiguration: AuthorizeAttribute
{
public new string Roles
{
get {
return base.Roles;
}
set {
var config = new ConfigurationBuilder()
.SetBasePath(Environment.CurrentDirectory)
.AddJsonFile("authorization.json")
.Build();
base.Roles = config[value];
}
}
}
authorization.json:
{
"Parts": {
"Create": "contoso.com\\MyWebApp_CreateNewPart",
"Edit": "contoso.com\\MyWebApp_EditPart"
}
}
Example Usage:
[AuthorizeFromConfiguration(Roles = "Parts:Create")]
public class CreateModel : PageModel
{
//...
}
Note: In my testing, the web-site had to be restarted before any changes to authorization.json file took effect, even when I tried changing the logic so that the JSON file was read on the get accessor instead of the set.

Resources