Tying MembershipUser and Web.Profile together for newly created users - c#

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);
}

Related

Should I store a small picture avatar inside ClaimsIdentity in ASP.NET core? if not, what is the best way?

I am new to ASP.NET core and I am developing a website (.NET core 2.1) with user login.
I would like to be able to show an user avatar image in all pages that uses _Layout.cshtml.
So inside that file, for the user name I am already using: #User.Identity.Name.
Now I would like to use something like this: #User.Claims.First("avatar")
I am using Cookies based authentication:
In my "Startup.cs":
public void ConfigureServices(IServiceCollection services) {
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
// support for cookie based authentication
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options => {
options.Cookie.HttpOnly = true;
options.LoginPath = "/Account/Login";
});
}
In my "AccountController":
[HttpPost]
public async Task<IActionResult> Login(LoginModel m) {
var user = Authenticate(m.Email, m.Password); // returns null when authentication fails
if (user!=null) {
var userIdentity = new ClaimsIdentity(
new List<Claim> {
new Claim(ClaimTypes.Name, user.Company.Name),
new Claim(ClaimTypes.Email, user.Email)
}, "login");
ClaimsPrincipal principal = new ClaimsPrincipal(userIdentity);
await HttpContext.SignInAsync(principal);
return Redirect("/"); // redirect after authentication
}
return View("Login"); // default view if cannot authenticate
}
The avatar picture itself will be store in a firebase database, so I need to avoid database query for each page request of my website.
Since "claims" are used for authorization rules, I don't feel like I should use that to store a "small" byte array that I can use to render a JPG picture. Since I am showing it in every page during the user session, I want to load the picture to the server RAM during the Login and make it stay there for as long as the user remains logged-in.
I've found many examples where people will extend the class ClaimsPrincipal to add new properties, but that requires some other code change (seems to be related with IdentityModel using the entity framework) and a factory for ClaimsPrincipal objects. (If I have to take that direction, I would like a small example starting from the code I provided)
Remembering that I am new to ASP.NET core MVC...
If someone could point me towards a good direction I would appreciate.
You can add custom claims like that:
new Claim("Avatar", Convert.ToBase64String(byteArray));
Since they only accept strings, you should convert your byte array into a base64 string.
However, I believe that the best way to do that is to have some sort of Storage, and then save only the URL of the picture in the database (and in the claims).
I don't understand why the avatar only is shown when the user is online. Does only the user itself see his avatar?
But I am also currently working on the identity stuff for my project and maybe this solution works for you too.
I have a little controller logic for images underlying. All images are in a database and get served by a special MediaController. I also cache them on a folder, but that's a different story. This MediaController serves all images, identified by its id. The route is /media/images/{imageId:int}
This would work also with a filename instead of an id. Why handle a profile pic different from all other images?
My IdentityUser Class looks like this:
using System;
using Microsoft.AspNetCore.Identity;
namespace MyProject.Identity
{
public class MyProjectIdentity : IdentityUser<string>
{
//other properties...
public int ProfilePicId { get; set; }
//more other properties...
}
}
On every view, where I show the Avatar I just do:
<img src="~/media/images/#User.ProfilePicId" alt="Profile Pic" />
Its best to store avatar in DB, that way it is faster to load.
Refer this - https://learn.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-3.1#storage-scenarios

How to autherize user in asp.net Identity using permissions instead of roles?

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.

Best practice to allow/deny user to call Controller actions depending on role rights

I have a role system where the Admin can create new roles and grant Read, Write, Edit and Delete rights based on the page menu (each page menu has it's own controller) then assign that role to the user.
Users can also have multiple roles but i got that covered. The only problem I am facing is this: since Role assigning can be done dynamically from within the application i can't use the [Authorize(Roles = Role.Admin)] attribute over controller actions.
Instead i was thinking of creating [Read], [Write], [Edit] and [Delete] attributes that i would put over the actions to let me know what that action is for and then allow or deny users to call that action.
Would this be the correct approach? What would i need to have in those custom attributes and how would i allow/deny user to call them? Anyone have any similar experience?
Let's say i have Home and Admin controller and Role1 and Role2 with following rights:
Would something like the above approach works? How do I define custom access for controller actions?
I want to avoid having all the actions in the database and simplify the administration on the site.
In this way administration can be done on R/W/E/D flags on controller level instead of Role1 can all Home/Index, Home/GetRecords etc... action level.
I have same requirement for my project. In my project I have to allow user to access some pages and some pages to admin. Mostly admin can access everything, but I have to block user to access some pages/Action. I did below thing.
a. Create one class to Authenticate user/admin.
b. Create one class for User and inherited that class from "ActionFilterAttribute", and override the OnActionExecuting method.
c. Create one class for Admin and did samething as mentioned in "b", but for admin.
d. put the Authentication class name above the class name or method depending on access.
see sample code(step b). I removed some code, but you can get the idea.
public class AuthenticateUser : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext context)
{
//Do session check.
if (HttpContext.Current.Session["Id"] == null || String.IsNullOrEmpty(HttpContext.Current.Session["Id"].ToString()))
{
HttpContext.Current.Response.StatusCode = HttpStatusCode.Forbidden.GetHashCode();
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.Result = new JsonResult { Data = new { Status = HttpStatusCode.Forbidden, LogonRequired = true, result = "Session Expired" }, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
else
{
//redirect to login page
UrlHelper oUrl = new UrlHelper(HttpContext.Current.Request.RequestContext);
string s = oUrl.Action("", "UserInfo/");
HttpContext.Current.Response.Redirect(s);
}
}
//If usertype is User then allow it. If user type is admin then return redirect.
//Redirect code if admin
UrlHelper oUrl = new UrlHelper(HttpContext.Current.Request.RequestContext);
string s = oUrl.Action("", "UserInfo/");
HttpContext.Current.Response.Redirect(s);
}
}
I would define it differently, just give each user a set of roles.
Define each user with more than one role, if someone has access to write, you set a role for him to write, if he can also read you can add another role - read.
Now when you define your actions you can use this format[Authorize(Roles = Role.Read)]. Set each action by the access right it requires and you are done.
Use claims. each public controller action claim that you need to define.
Then have your roles defined by the list of claims/operations they can do.
Here's a great explanation here Role-based access control (RBAC) vs. Claims-based access control (CBAC) in ASP.NET MVC

saving session data once authenticated .net

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);
}
}
}

Handling Multiple Roles in MVC - Action-based Accessibility

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.

Categories