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.
Related
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()
{
}
}
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);
}
}
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
I am making an ASP.Net MVC3 application. I use for now the built in Authentication code that comes with a Visual Studio 2010 project. The problem is dat I need to retrieve the logged in user's database ID as soon as he has logged in. I do that now by adding code to the Login Action of the Account controller that retrieves the ID from the database by looking it up by username. This works for new logins, but not for "remembered" ones. On restarting the application the last user is automatically logged in again, but the Login code is not fired, so I do not get the database ID.
How can I solve this?
EDIT:
I tried to implement Daniel's solutions which looks promising and I came up with this code. It nevers gets called though! Where have I gone wrong?
Global.asax.cs:
protected void Application_Start()
{
Database.SetInitializer<StandInContext>(new StandInInitializer());
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
this.AuthenticateRequest +=
new EventHandler(MvcApplication_AuthenticateRequest);
}
void MvcApplication_AuthenticateRequest(object sender, EventArgs e)
{
if(Request.IsAuthenticated)
{
using (var db = new StandInContext())
{
var authenticatedUser = db.AuthenticatedUsers.SingleOrDefault(
user => user.Username == User.Identity.Name);
if (authenticatedUser == null)
return;
var person = db.Persons.Find(authenticatedUser.PersonID);
if (person == null)
return;
Context.User = new CustomPrincipal(
User.Identity, new string[] { "user" })
{
Fullname = person.FullName,
PersonID = person.PersonID,
};
}
}
}
You can use the AuthenticateRequest event in your Global.asax.cs:
protected void Application_AuthenticateRequest()
{
if (Request.IsAuthenticated)
{
// retrieve user from repository
var user = _membershipService.GetUserByName(User.Identity.Name);
// do other stuff
}
}
Update:
Now that I see what you're trying to do a little clearer, I would recommend against using sessions in this particular case. One reason is that Session requires a reference to System.Web, which you don't have access to from some places, like a business logic layer in a separate class library. IPrincipal, on the other hand, exists for this very reason.
If you need to store more user information than what IPrincioal provides, you simply implement it and add your own properties to it. Easier yet, you can just derive from GenericPrincipal, which implements IPrincipal and adds some basic role checking functionality:
CustomPrincipal.cs
public class CustomPrincipal : GenericPrincipal
{
public CustomPrincipal(IIdentity identity, string[] roles)
: base(identity, roles) { }
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
...
}
So then you replace the default principal with your own in AuthenticateRequest, as before:
Global.asax.cs
protected void Application_AuthenticateRequest()
{
if (Request.IsAuthenticated)
Context.User = _securityService.GetCustomPrincipal(User.Identity.Name);
}
And that is it. The greatest advantage you get is that you automatically get access to your user data from literally everywhere, without having to stick a userId parameter into all your methods. All you need to do is cast the current principal back to CustomPrincipal, and access your data like so:
From your razor views:
<p>Hello, #((CustomPrincipal)User).FirstName!</p>
From your controllers:
var firstName = ((CustomPrincipal)User).FirstName;
From a business logic layer in another assembly:
var firstName = ((CustomPrincipal)Thread.CurrentPrincipal).FirstName;
To keep things DRY, you could pack this into an extension method and hang it off IPrincipal, like so:
public static class PrincipalExtensions
{
public static string GetFirstName(this IPrincipal principal)
{
var customPrincipal = principal as CustomPrincipal;
return customPrincipal != null ? customPrincipal.FirstName : "";
}
}
And then you would just do #User.GetFirstName(), var userName = User.GetFirstName(), Thread.CurrentPrincipal.GetFirstName(), etc.
Hope this helps.
I wasn´t thinking clear. I was trying to store the userinfo in the Session object, while it available through the User object. Sorry to have wasted your time.
I am building a simple CMS in which roles are set dynamically in the admin panel. The existing way of authorizing a controller method, adding [Authorize(Roles="admin")] for example, is therefore no longer sufficient. The role-action relationship must be stored in the database, so that end users can easily give/take permissions to/from others in the admin panel. How can I implement this?
If you want to take control of the authorization process, you should subclass AuthorizeAttribute and override the AuthorizeCore method. Then simply decorate your controllers with your CmsAuthorizeAttribute instead of the default.
public class CmsAuthorizeAttribute : AuthorizeAttribute
{
public override virtual bool AuthorizeCore(HttpContextBase httpContext)
{
IPrincipal user = httpContext.User;
IIdentity identity = user.Identity;
if (!identity.IsAuthenticated) {
return false;
}
bool isAuthorized = true;
// TODO: perform custom authorization against the CMS
return isAuthorized;
}
}
The downside to this is that you won't have access to ctor-injected IoC, so you'll have to request any dependencies from the container directly.
That is exactly what the ASP.NET membership / profile stuff does for you. And it works with the Authorize attribute.
If you want to roll your own you could create a custom action filter that mimics the behavior of the standard Authorize action filter does. Pseudo code below.
public MyAuthorizeAttribute : ActionFilterAttribute
{
public string MyRole { get; set; }
public void OnActionExecuting(ControllerContext context)
{
if (!(bool)Session["userIsAuthenticated"])
{
throw new AuthenticationException("Must log in.");
}
if (!Session["userRoles"].Contains(MyRole))
{
throw new AuthenticationException("Must have role " + MyRole);
}
}
}
The role - action relationship must be
stored in the database
You will have to check your security within the controller method, unless you want to subclass AuthorizeAttribute so that it looks up the roles from the database for you.