So I'm building a web application in .NET using c#, MVC and sqlexpress. I want users to be able to login and depending on what group they belong to, see some part of an UI.
I have created the tables for groups and users in my database, created models from those tables using ADO.NET wizard for model creation.
I have added a controller that has methods for checking if a user exists, and if his password is correct. My question is how to store the information that the user is authenticated the "©correct" way?
At the moment, i just create a new object in the session variable that is made available by System.Web.Mvc.Controller. I have made a flag object Session["Authenticated"] = true and created another object that holds the information (username, group affiliation etc...) as Session["User"].
I have stumbled upon articles that describe implementing your own membership provider (here and here) but I feel that I would need to break my existing classes for password security and account control in order to implement them inside of the custom membership provider.
Is the custom membership provider implementation necessary or is the data saved in the session good enough?
A better way if you don't want to go down the membership provider route would be to look into forms authentication (http://msdn.microsoft.com/en-us/library/xdt4thhy(v=vs.100).aspx).
Firstly save Roles and Permissions regarding user and write custom Action filter in which check If User have permission for access the controller method if yes then go as its going if not then generate exception and response message.
When User Login then also Get Current User Roles and Permissions and save it on Session in custom filters or on your buttons and tabs check from Session User have permission or not if User have permission then show the tab otherwise hide it.
Here is link for Example
Action Filters
Filters Example Code Project
one of the most easy way to implement authentication and authorization in an ASP.NET MVC project is to use the inbuild Forms Authentication module. When you create a new ASP.NET MVC project, it already incorporates all that is necessary to get you started with the Forms Auth.
All it will do is to create certain tables (5), and you never have to worry or track them every in your code. Its all very simple. You can always map the users created by ASP.NET MVC membership provider to your very own UserMaster table.
And next, all you have to do is to mark your ActionMethod or your Controller or in global.asax the [Authorize] attribute, and you are all set.
Code will be as simple as :
for login:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password,
persistCookie: model.RememberMe))
{
///rest of the logic
}
}
also below are some userful functions which will come in handy:
if(WebSecurity.UserExists(username)){ /// ur logic }
//or
WebSecurity.CreateUserAndAccount(username, password);
//or
System.Web.Security.Roles.AddUserToRole(username, UserRole);
and guess what, you don't have to worry abt setting some flag in some HttpModule or first always executing function to see if the session User exists or not, [Authorize] attribute takes care of that.
Mapping with custom UserMaster
if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password,
persistCookie: model.RememberMe))
{
try
{
try
{
//below is my custom method IsValidUser with my own logic for
// valid user
MembershipCore.IsValidUser(model.UserName);
if (MembershipCore.isFirstTimeLogin(model.UserName))
{
//Show License Screen in a window
return RedirectToAction("License");
}
//my custom method setting custom Session variables
MembershipCore.SetLoggedInUser(model.UserName);
}
}
}
Related
I am trying to utilize Identity for a new application that I am working on. The minimum info that is available about it is making it hard to understand.
Based on what I understand about Identity, I can create roles (i.e Admin, Super User, Users...) and for each action in the controller I can restrict whether a role can access the action or not. This an a decent approach for typical application. But in my application I need a deeper control on the access level. I need to be able to have more control not a role base autherization.
In a typical application I can add this over an action method [Authorize(Roles = "Admin")] in a controller, and only people with Admin will be able authorized to access it.
But, I want to be able to add individual permission and not sure if this is possible using Identity. (Perhaps something to do with Claims?)
Here are my thoughts, I want to be able to do something like this
// In a Razor view I want to be able to do this
// Note: HasPermission is not real method, but is something I am posting
// to explain what I am looking for
if(User.HasPermission("Edit Account")){
// Show the Edit button
}
if(User.HasPermission("Add Account")){
// Show the Add Button
}
Then, in the action methods I want to do be able to do something like this.
[Authorize( HasPermission = "Edit Account")]
public ActionResult Edit()
{
// Handle the edit
}
[Authorize( HasPermission = "Add Account")]
public ActionResult Add()
{
// Handle the addition
}
The application will have many permission (i.e Edit Account, Add Account, Delete Account ....) I will assigned multiple permission to one user. The user with the permission Add Account will be able to add account.
Can something like this be done with Identity out of the box? How can I get started without complicating the problem?
You can extend the Identity workflow by adding Permission concept to the base. The most important thing you should care about is the relation between the current model and new added functionality. I did that in my latest project and publish it as an open source Permission Extension to Identity 2 membership system here.
You can read the Readme file and also feel free to ask your question or extend the library yourself.
As a short example you can use it like this:
First approach:
// GET: /Manage/Index
[AuthorizePermission(Name = "Show_Management", Description = "Show the Management Page.")]
public async Task<ActionResult> Index(ManageMessageId? message)
{
//...
}
Second approach:
// GET: /Manage/Users
public async Task<ActionResult> Users()
{
if (await HttpContext.AuthorizePermission(name: "AllUsers_Management", description: "Edit all of the users information."))
{
return View(db.GetAllUsers());
}
else if (await HttpContext.AuthorizePermission(name: "UnConfirmedUsers_Management", description: "Edit unconfirmed users information."))
{
return View(db.GetUnConfirmedUsers());
}
else
{
return View(new List<User>());
}
}
Claims tell you who the user is, not what the user can do. For example, their may be a claim for the user's first name, their email address and so on. There may also be claims for the user's roles, or groups they belong to (there is a subtle difference, but let's ignore that for now). For example, there may be role claims stating the user is an accountant and a manager.
As you noticed, these claims don't tell you what the user can do. It may be the case that an accountant can edit an account, but the claims won't tell you that. Somewhere somehow, you'll have to make a translation from roles (and other claims) to permissions (as you call them).
Unfortunately, there is no standard component in the .NET Framework to do that. The only thing that ever came close was AzMan, but that is now deprecated.
That means you have two options:
You can role your own. It's not too difficult. Basically, you need a data structure with a many to many relationship between roles and tasks, and a many to many relationship between tasks and individual, fine-grained permissions. The Azman model can be a good source of inspiration.
The alternative, which many people take, and for simple applications is probably good enough, is to hardcode the roles in the application. That's where [Authorize(Roles = "Accountant")] comes in. Obviously, this model requires recompilation whenever the organization of the business changes, but that may be OK if it doesn't happen too often.
Unfortunately, there is no better answer.
Right now I have the following website structure:
Front end: website\user1, website\user2
Back end: website\account\user1, website\account\user2
Where website\account controller has generic [Authorize] attribute applied. Which is not enough since any authorized user can access other user's backed functionality simply by going to website\account\ url (if he knows his name).
What is the best way to resolve this issue?
I have two approaches so far:
Create custom Authorize attribute, inspect controller context, extract user information from there and compare it against current authorized user in ASP.NET:
var currerntUserId = (long)System.Web.Security.Membership.GetUser().ProviderUserKey;
return ExtractCurrentUserId(filterContext) == currerntUserId;
Remove part from back-end URLs and have all users access \account. Current user information will be provided by ASP.NET framework.
How about this?
Route /website/my-account
[Authorize]
public class MyAccountController : Controller
{
public ActionResult Index()
{
var userData = System.Web.Security.Membership.GetUser();
// note you could also get this from db using this.User.Identity.Name
return View(userData);
}
}
It is much easier to control the authorization this way because we are not passing the userid to the action method via a route parameter. The only way someone can get to the backend for a particular user account is by being logged in as that user.
Reply to comments:
To answer your questions in comments about what is easier / harder / better / what my preference is, I am going to go ahead and make my final answer "It depends."
It depends on the sensitivity of the data, what things admins can do that users aren't allowed to (or vice versa), how many controller actions needed to be secured, how similar the views are for public / account / admin perspectives on the data, etc. Pretty much everything stated in your question and all the answers here are valid approaches. You can certainly do it with an ActionFilter and keep the user URL's, or you could do it directly in the action method (if there aren't a lot of them), change your URL schema, implement impersonation (or not), etc.
When you are retrieving a user's data from your datastore (most likely a database), you should only retrieve data for the username of the authenticated user. In your controller, this will give you the username of the currently authenticated user:
User.Identity.Name
So you could do something like:
return ExtractCurrentUserId(filterContext) == User.Identity.Name;
If you use Role based authentication with SimpleMembership you can do something like this and give users roles that should be able to access certain controller actions:
public class MyAccountController : Controller
{
[Authorize(Roles = "Admin")]
public ActionResult User1()
{
// do user1 work
}
[Authorize]
public ActionResult User2()
{
// do user2 work
}
}
I have 3 different types of users (with different roles) interacting on my web application, they all perform some task - some can be exactly the same e.g. create a quote others can be unique to that specific user e.g. sign off quote.
For more clarity 3 types of users: Client, Supplier, Customer.
Client or Customer can create a quote, however only the Customer can sign off a quote.
How do I ensure my application allows clients to access client speficic controllers and suppliers to access supplier specific controllers or areas. Via Custom Attributes? Do I store the type of user inside a cookie? Is this safe? or Session state? As soon as someone logs onto the system I send back a LoggedOnDTO object on which I store Username, UserID, and type of user....
NOTE: I went away from asp.net build in way of creating users, I have my own custom tables with my custom mechanism for logging into the system. I have a registered Model Bindiner that looks for the prefix and I send in a strongly typed object to each action...
Sample code:
[HttpGet]
public ActionResult AddComment(int quoteid, ClientUserDTO loggedonclientuser)
{
}
[HttpGet]
public ActionResult AddCommentSupplier(int quoteid, Supplier loggedonsuppluser)
{
}
EDIT: This method for some reason seems so much simpler... Is there something wrong with it? Any possible security issues? Threading?
My session controller is:
if (_authService.isValidUser(model))
{
var data = _authService.GetAuthenticationCookieDetails(model);
AuthenticateCookie.AddDetailsToCookie(data);
return Redirect(Url.Action("Index", "Activity"));
}
When I create my cookie... I can simple store "ClientUser", "Supplier" or whatever role they are inside the cookie.
Then I can create an Attribute and read in the cookie data to see if they are a valid user e.g.
public class ClientAuthorizationAttribute : AuthorizeAttribute
{
public bool AlwaysAllowLocalRequests = false;
protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext)
{
if (AlwaysAllowLocalRequests && httpContext.Request.IsLocal)
{
bool authorized = false;
var result = UserDetails.GetTypeFromTicket(httpContext.User.Identity as FormsIdentity);
if (result.Equals("client", StringComparison.OrdinalIgnoreCase))
{
authorized = true;
}
//throw no access exception?
return authorized;
}
return base.AuthorizeCore(httpContext);
}
}
Register the attribute under my base controller and I have a simple working solution???
Write a custom MembershipProvider and a Custom RoleProvider then you can decorate your controler class or specific methods with the attribute
<Authorize(Roles:="ROLENAME")>
You can learn how to make that your asp mvc use the custom membershiprovider in this question It's really easy.
Edited:
The way you did it looks right, but I think you take the long way. Implementing your own MembershipProvider and your own Roleprovider will take you no more than 20 minutes... and you will have the benefits of being working with a well tested and documented system and still having the benefits of use your own database tables for the login. In a simple login system, you only have to write two functions in the roleprovider (GetRolesForUser and IsUserInRole) and only one function in the membershipprovider (ValidateUser) and you will get your system working.
If you want, I can put somewhere (maybe pastebin) a well commented versión of a membershipProvider as well of a roleprovider that i'm using in a simple app (they're made in vb.net but i'm sure it will not be a problem)
You can also write generic code in the base controller instead of decorating each action with Authorize attribute. Please refer below url.
Good practice to do common authorization in a custom controller factory?
custom-controller-factory/5361846#5361846
I currently have a project that I seem to have ran into an issue regarding Roles and thought I would get some opinions on how to best handle the problem.
The system will require editable, flexible roles that control not only the access of specific areas, but also the use of system functions (Adding Users, Editing Users, Viewing Reports etc.)
The system currently allows users to have multiple roles, each of those roles has explicitly defined areas of access/actions, for example:
Role A can access areas 1,2,3 and can Add Users.
Role B can access areas 1,5,7 and can Modify Users.
Role C can access areas 4,6 and only View Users.
so a User could be in Roles A and C, and thus access : 1,2,3,4 and 6, and could Add and View Users.
My first solution was to create a dictionary that would store all of the possible areas of access/access options into a Dictionary like so:
Dictionary<string,bool>
then when it is instantiated it pulls all of the properties from the database and then iterates through the roles to determine if they are accessible.
All of that currently works just fine - however the project is quite Javascript/jQuery intensive so many of these options are called by client-side functions. I am trying to avoid having to wrap all of these client side functions with:
<%if(AccessDictionary[key])
//Enable or Disable Action
<%}%>
So basically, I am wondering about the following things:
After a user logs in, what is the best way to store this Dictionary? Statically? In the Session?
What would be the best method of storage such that the Dictionary will be easily accessed in the View? (As I currently see no way around wrapping my client-side functions)
Any advice or ideas would be greatly appreciated!
I would store this information in the user data part of the authentication cookie. So when a user logs in:
public ActionResult Login(string username, string password)
{
// TODO: validate username/password couple and
// if they are valid get the roles for the user
var roles = "RoleA|RoleC";
var ticket = new FormsAuthenticationTicket(
1,
username,
DateTime.Now,
DateTime.Now.AddMilliseconds(FormsAuthentication.Timeout.TotalMilliseconds),
false,
roles
);
var encryptedTicket = FormsAuthentication.Encrypt(ticket);
var authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket)
{
// IIRC this property is only available in .NET 4.0,
// so you might need a constant here to match the domain property
// in the <forms> tag of the web.config
Domain = FormsAuthentication.CookieDomain,
HttpOnly = true,
Secure = FormsAuthentication.RequireSSL,
};
Response.AppendCookie(authCookie);
return RedirectToAction("SomeSecureAction");
}
Then I would write a custom authroize attribute which will take care of reading and parsing the authentication ticket and store a generic user in the HttpContext.User property with its corresponding roles:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext.User.Identity.IsAuthenticated)
{
var authCookie = httpContext.Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
var ticket = FormsAuthentication.Decrypt(authCookie.Value);
var roles = ticket.UserData.Split('|');
var identity = new GenericIdentity(ticket.Name);
httpContext.User = new GenericPrincipal(identity, roles);
}
}
return base.AuthorizeCore(httpContext);
}
}
Next you could decorate your controllers/actions with this attribute to handle authorization:
// Only users that have RoleA or RoleB can access this action
// Note that this works only with OR => that's how the base
// authorize attribute is implemented. If you need to handle AND
// you will need to completely short-circuit the base method call
// in your custom authroize attribute and simply handle this
// case manually
[MyAuthorize(Roles = "RoleA,RoleB")]
public ActionResult Foo()
{
...
}
In order to check whether a user is in a given role simply:
bool isInRole = User.IsInRole("RoleC");
Armed with this information you can now start thinking of how to organize your view models. In those view models I would include boolean properties such as CanEdit, CanViewReport, ... which will be populated by the controller.
Now if you need this mapping in each action and views things might get repetitive and boring. This is where global custom action filters come into play (they don't really exist in ASP.NET MVC 2, only in ASP.NET MVC 3 so you might need a base controller decorated with this action filter which simulates more or less the same functionality). You simply define such global action filter which executes after each action and injects some common view model to the ViewData (holy ...., can't believe I am pronouncing those words) and thus make it available to all views in a transverse of the other actions manner.
And finally in the view you would check those boolean value properties in order to include or not different areas of the site. As far as the javascript code is concerned if it is unobtrusively AJAXifying areas of the site then if those areas are not present in the DOM then this code won't run. And if you needed more fine grained control you could always use HTML5 data-* attributes on your DOM elements to give hints to your external javascript functions on the authorizations of the user.
I am using ASP.NET MVC 3. I am using what essentially came with for free in the Visual Studio project template for an MVC project with the "Internet Application" option. Basically this brings in Forms authentication and provides some basic elements to manage user login and stuff.
I am also using the web profiles stuff with this to store some custom fields. Everything was going great. I use SuperFunProfile as a wrapper around the Profile instance to make it easier to get at profile properties.
Until I wanted to set a property of a Profile right away after signing the user up.
The problem I can't solve is that this.Request.RequestContext.HttpContext.Profile contains the profile for the anonymous user. How can I get a new profile for the user now that he should be signed up and signed in?
public ActionResult SignUp(SignUpModel model)
{
if (ModelState.IsValid)
{
// Attempt to register the user
MembershipCreateStatus createStatus = this.MembershipService.CreateUser(model.UserName, model.Password, model.Email);
if (createStatus == MembershipCreateStatus.Success)
{
this.FormsService.SignIn(model.UserName, false /* createPersistentCookie */);
var profile = new SuperFunProfile(this.Request.RequestContext.HttpContext.Profile);
profile.DisplayName = model.UserName;
profile.Save();
return RedirectToAction("Index", "Home");
}
else
{
ModelState.AddModelError(string.Empty, AccountValidation.ErrorCodeToString(createStatus));
}
}
I poked around Membership and Web.Profile, but I am not seeing anything that looks like it will get me closer to my goal.
Maybe I should just create a ProfileModel that I store myself into the DB rather than using Web.Profile? I could key that on MembershipUser.ProviderUserKey which would make it easier to create a ProfileModel at sign up, I suppose.
I think you can use MigrateAnonymous event.
When users log in (that is, when they
stop being anonymous users), the
MigrateAnonymous event is raised. You
can handle this event to migrate
information from the user's anonymous
identity to the new authenticated
identity, if necessary. The following
code example shows how to migrate
information when a user is
authenticated.
In your global.asax use something like
public void Profile_OnMigrateAnonymous(object sender, ProfileMigrateEventArgs args)
{
ProfileCommon anonymousProfile = Profile.GetProfile(args.AnonymousID);
Profile.ZipCode = anonymousProfile.ZipCode; //Your custom property
Profile.CityAndState = anonymousProfile.CityAndState;//Your custom property
Profile.StockSymbols = anonymousProfile.StockSymbols;//Your custom property
////////
// Delete the anonymous profile. If the anonymous ID is not
// needed in the rest of the site, remove the anonymous cookie.
ProfileManager.DeleteProfile(args.AnonymousID);
AnonymousIdentificationModule.ClearAnonymousIdentifier();
// Delete the user row that was created for the anonymous user.
Membership.DeleteUser(args.AnonymousID, true);
}