I have implemented the Identity 2.0 membership and starting to regret it now. But, I'm so deep in this project there is no turning around. My issue is probably simple to most so hopefully I can get some assistance.
I have a grid control in my MVC5 application.
VIEW
#using System.Web.Helpers;
#model List<PTSPortal.Models.PTSUsersViewModel>
#{
var grid = new WebGrid(source: Model, defaultSort: "PropertyName", rowsPerPage: 10);
}
<div id="ContainerBox">
#grid.GetHtml(columns: grid.Columns(
grid.Column("_email", "Email", canSort: true, style: "text-align-center"),
grid.Column("_employeeID", "EmployeeID", canSort: true, style: "text-align-center"),
grid.Column("_phoneNumber", "Phone", canSort: true, style: "text-align-center")
//-----I want to display the user's role here!------
))
</div>
VIEWMODEL
public class PTSUsersViewModel
{
public string _ID { get; set; }
public string _email { get; set; }
public int? _employeeID { get; set; }
public string _phoneNumber { get; set; }
public string _role { get; set; }
}
My goal is to display each registered user's role using the grid.Column just like email, employeeID, and phoneNumber.
CONTROLLER
public ActionResult PTSUsers()
{
List<PTSUsersViewModel> viewModel = FetchInfo().ToList();
return View(viewModel);
}
private static IEnumerable<PTSUsersViewModel> FetchInfo()
{
PTSPortalEntities context = new PTSPortalEntities();
using (ApplicationDbContext _context = new ApplicationDbContext())
{
var roleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(_context));
var UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(_context));
}
return (from a in context.AspNetUsers
orderby a.Email ascending
select new PTSUsersViewModel
{
_ID = a.Id,
_email = a.Email,
_employeeID = a.EmployeeID,
_phoneNumber = a.PhoneNumber,
//_role = ........
}).ToList<PTSUsersViewModel>();
}
Within my using statement, I have var roleManager and var userManager but they are not doing anything. I was trying to retrieve the user's role, but that's when I stopped and figured I would reach out to SOF for some tips or better approach.
Now, on a side note. There are some service methods already created in the project which work great within other controller methods. Maybe these can be used or modified in my issue above:
public class AppServices
{
// Roles used by this application
public const string AdminRole = "Admin";
public const string TrainerRole = "Trainer";
private static void AddRoles(ref bool DataWasAdded)
{
var roleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(new ApplicationDbContext()));
if (roleManager.RoleExists(AdminRole) == false)
{
Guid guid = Guid.NewGuid();
roleManager.Create(new IdentityRole() { Id = guid.ToString(), Name = AdminRole });
DataWasAdded = true;
}
if (roleManager.RoleExists(TrainerRole) == false)
{
Guid guid = Guid.NewGuid();
roleManager.Create(new IdentityRole() { Id = guid.ToString(), Name = TrainerRole });
DataWasAdded = true;
}
}
/// <summary>
/// Checks if a current user is in a specific role.
/// </summary>
/// <param name="role"></param>
/// <returns></returns>
public static bool IsCurrentUserInRole(string role)
{
if (role != null)
{
using (ApplicationDbContext _context = new ApplicationDbContext())
{
var roleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(_context));
var UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(_context));
if (UserManager.IsInRole(GetCurrentUserID(), role))
{
return true;
}
}
}
return false;
}
/// <summary>
/// Adds a user to a role
/// </summary>
/// <param name="userId"></param>
/// <param name="RoleToPlaceThemIn"></param>
public static void AddUserToRole(string userId, string RoleToPlaceThemIn)
{
// Does it need to be added to the role?
if (RoleToPlaceThemIn != null)
{
using (ApplicationDbContext _context = new ApplicationDbContext())
{
var roleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(_context));
var UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(_context));
if (UserManager.IsInRole(userId, RoleToPlaceThemIn) == false)
{
UserManager.AddToRole(userId, RoleToPlaceThemIn);
}
}
}
}
Any advice would be appreciated.
Use await UserManager.GetRolesAsync(user) to return list of strings with roles assigned. Because user can have many roles there is no such thing as "user role", there are roles.
So if you'd like to show roles in the table, you'll need to join them into CSV. Something like this:
var roles = await UserManager.GetRoles.Async();
var allUserRoles = String.Join(", ", roles);
_PTSUsersViewModel._roles = allUserRoles;
Related
I'm changing the way i seed data on my app using entiy framework core. Previous to 2.1 version you cannot set the identity key value so I started migrating my code but now i have a problem with seeding Users and Roles from the Identity. How can i access userManager or RoleManager on my DbContext file or modelbinder extension?
Here is how i seed users and roles using a class and inject it in program.cs
public class DbInitializer
{
public static async Task Seed(ApplicationDbContext context, UserManager<User> userManager, RoleManager<Role> roleManager)
{
#region Identity
string[] roles = new string[] { "Developer", "Super Administrator", "Administrator", "User" };
foreach (string role in roles)
{
if (!context.Roles.Any(r => r.Name == role))
{
await roleManager.CreateAsync(new Role(role));
}
}
var user = new User
{
Name = "test",
Email = "test#test.com",
NormalizedEmail = "TEST#TEST.COM",
UserName = "test",
NormalizedUserName = "TEST",
EmailConfirmed = true,
PhoneNumberConfirmed = false,
SecurityStamp = Guid.NewGuid().ToString("D")
};
if (!context.Users.Any(u => u.UserName == user.UserName))
{
var password = new PasswordHasher<User>();
var hashed = password.HashPassword(user, "development");
user.PasswordHash = hashed;
var result = await userManager.CreateAsync(user);
}
await AssignRoles(userManager, user.UserName, roles);
#endregion
}
public static async Task<IdentityResult> AssignRoles(UserManager<User> userManager, string userName, string[] roles)
{
User user = await userManager.FindByNameAsync(userName);
var result = await userManager.AddToRolesAsync(user, roles);
return result;
}
}
program file
var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
var isDevelopment = environment == Environments.Development;
if (isDevelopment)
{
using var scope = host.Services.CreateScope();
var services = scope.ServiceProvider;
try
{
var context = services.GetRequiredService<ApplicationDbContext>();
var roleManager = services.GetRequiredService<RoleManager<Role>>();
var userManager = services.GetRequiredService<UserManager<User>>();
await DbInitializer.Seed(context, userManager, roleManager);//<---Do your seeding here
}
catch (Exception ex)
{
Log.Error(ex, "An error occurred while seeding the database.");
}
}
now i want to start using this instead after version 2.1
public static class ModelBuilderExtensions
{
public static void Seed(this ModelBuilder modelBuilder)
{
modelBuilder.Entity<Author>().HasData(
new Author
{
AuthorId = 1,
FirstName = "William",
LastName = "Shakespeare"
}
);
modelBuilder.Entity<Book>().HasData(
new Book { BookId = 1, AuthorId = 1, Title = "Hamlet" },
new Book { BookId = 2, AuthorId = 1, Title = "King Lear" },
new Book { BookId = 3, AuthorId = 1, Title = "Othello" }
);
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Seed();
}
If you inherited from IdentityDbContext then you can get the users table by
modelBuilder.Entity<Users>().HasData(
new User
{
}
I have created an Asp.Net Web Api project and used Individual user accounts.
When I am adding users to the table the default system automatically checks if the email address supplied already exists in the table, if so a bad request is thrown otherwise the user can be submitted.
How can I also check if the Phone Number is unique and hasn't already been submitted to the table?
// POST api/Account/Register
[AllowAnonymous]
[Route("Register")]
public async Task<IHttpActionResult> Register(RegisterBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
**using (ApplicationDbContext db = new ApplicationDbContext())
{
var foundPhoneNumber = await db.Users.FirstOrDefaultAsync(x => x.PhoneNumber.Equals(model.PhoneNumber));
if (foundPhoneNumber != null)
{
return BadRequest("Phone number already exists");
}
}**
var user = new ApplicationUser()
{
UserName = model.Email,
Email = model.Email,
PhoneNumber = model.PhoneNumber,
FirstName = model.FirstName,
LastName = model.LastName,
MemberNumber = model.MemberNumber,
CarReg = model.CarReg
};
IdentityResult result = await UserManager.CreateAsync(user, model.Password);
if (!result.Succeeded)
{
return GetErrorResult(result);
}
return Ok();
}
I have queried the database to check if there is a Phone Number with the same number. This works, but is there a better way to do this?
Modify your ApplicationUser call and add the following attributes.
public class ApplicationUser : IdentityUser
{
[MaxLength(17)]
[IsUnique]
public string PhoneNumber { get; set; }
}
You can override ValidateEntity method in ApplicationUserDbContext class, it will trigger on SaveChanges method.
protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
{
if (entityEntry != null && entityEntry.State == EntityState.Added)
{
var errors = new List<DbValidationError>();
////User Validation
if (entityEntry.Entity is ApplicationUser user)
{
if (this.Users.Any(u => string.Equals(u.PhoneNumber, user.PhoneNumber)))
{
errors.Add(new DbValidationError("User",
string.Format($"Phonenumber {user.PhoneNumber} is already taken")));
}
}
if (errors.Any())
{
return new DbEntityValidationResult(entityEntry, errors);
}
}
return new DbEntityValidationResult(entityEntry, new List<DbValidationError>());
}
Validation can be added via a custom ValidationAttribute that you add to the PhoneNumber property on you model. Here is a simple example:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class NotABananaAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
var inputValue = value as string;
var isValid = true;
if (!string.IsNullOrEmpty(inputValue))
{
isValid = inputValue.ToUpperInvariant() != "BANANA";
}
return isValid;
}
}
And its used liked this...
public class Model
{
[NotABanana(ErrorMessage = "Bananas are not allowed.")]
public string FavoriteFruit { get; set; }
}
Example sourced from:
https://riptutorial.com/csharp/example/18486/creating-a-custom-validation-attribute
I have a .netcore mvc app where I'm having Roles in CustomRoles class :
public class CustomRoles
{
///<Summary>Manager role</Summary>
public const string Manager = "Manager";
///<Summary>Publisher role </Summary>
public const string Publisher = "Publisher";
}
In my controller, I'm setting Authorize on the endpoint :
/// <summary>
/// ASYNC Version: Get a List of Journals based on a Week Id
/// </summary>
/// <param name="weekId">Database unique Week ID</param>
/// <response code="204">No Content</response>
/// <response code="400">Bad Request</response>
/// <returns></returns>
[Authorize(Roles = CustomRoles.Manager +","+ CustomRoles.Publisher)]
[HttpGet("GetPartnerJounalsByIdAsync/")]
[ProducesResponseType(200, Type = typeof(JounalsPartnerGetResponse))]
[ProducesResponseType(204)]
[ProducesResponseType(400)]
public async Task<ActionResult> GetPartnerJournalsByIdAsync([Required]int weekId)
{
//get from db
}
I want to Populate Roles from the Database instead of hard-coding them in the class, so I can achieve this :
[Authorize (Roles = //Roles from the database)]
Here is the solution which I created,But I'm stuck. I implemented a Role class :
public class Role
{
/////<Summary>Manager role</Summary>
public string RoleName { get; set; }
}
and a method to get Roles from Database :
/// <summary>
/// Get All Roles
/// </summary>
/// <returns></returns>
public async Task<List<Role>> GetRoles()
{
GetResponse response = new GetResponse();
List<Role> CRoles = new List<CustomRoles>();
response = await database.ObjectGetTypedListAsync<Role>("", "Role");
CRoles = (List<Role>)response.StronglyTypedObject;
return CRoles;
}
Any ideas how I can accomplish this ?
For checking role based on data in database, you could implement your own IAuthorizationFilter like
[AttributeUsage(AttributeTargets.Class)]
public class MyAuthorize : Attribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
var dbContext = context.HttpContext.RequestServices.GetRequiredService<ApplicationDbContext>();
var roles = dbContext.Roles.ToList();
foreach (var role in roles)
{
if (!context.HttpContext.User.IsInRole(role.Name))
{
context.Result = new ForbidResult();
}
}
}
}
And use it like:
[MyAuthorize]
public IActionResult About()
{
ViewData["Message"] = "Your application description page.";
return View();
}
I used to use something like Edward's answer. Now, I just use the Login method in the AccountController:
public class AppUser
{
public string DisplayName { get; set; }
[Required]
public string Username { get; set; }
[Required]
[DataType(DataType.Password)]
public string Password { get; set; }
}
[HttpPost, ValidateAntiForgeryToken]
[AllowAnonymous]
public async Task<IActionResult> Login(string returnUrl, AppUser model)
{
// if input parameters are valid…
if (ModelState.IsValid)
{
// attempt to authenticate
var user = _authService.Login(model.Username, model.Password);
if (user != null)
{
// if login was successful, create Claims for Username and DisplayName
var userClaims = new List<Claim>
{
new Claim("displayName", user.DisplayName),
new Claim(ClaimTypes.Name, user.Username)
};
// as well as all the permitted Roles
var permissions = (from p in _context.Permissions
where p.Username == user.Username
select p.Name);
foreach (var role in permissions){
userClaims.Add(new Claim(ClaimTypes.Role, role));
}
ClaimsIdentity identity = new ClaimsIdentity(userClaims, _authService.GetType().Name);
identity.Label = user.DisplayName;
// save the ClaimsIdentity in the authentication cookie
await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(identity),
// the cookie will persist for 7 days
new AuthenticationProperties
{
IsPersistent = true,
ExpiresUtc = DateTimeOffset.UtcNow.AddDays(7)
}
);
}
}
return View(model);
}
Obviously, you have your own Authentication. AppUser is the model used in the Login View. Cookie authentication is free :-)
Now your controllers just use [Authorize(Roles = "permission")] on your Controller methods, where permission is p.name from the query above.
I have three tables
dbo.PermissionFunc, dbo.Roles, dbo.Permissions for my asp.net MVC web application.
dbo.PermissionFunc contains all the function name in my project.
dbo.Roles contains the user roles such as admin, user, subuser etc
dbo.Permissions contains the RolesId from dbo.Roles and PermissionFuncId from dbo.PermissionFunc.
I want to give authorization based on the value assigned in dbo.Permission.
Update in question:
Query using to determine if the current user has permission or not
string mail = FormsAuthentication.Decrypt(Request.Cookies[FormsAuthentication.FormsCookieName].Value).Name;
var usr = _user.GetUserByMail(mail);
var permFunc = _permissionfunc.FindByName("ActionResultName");
var permission = _permission.checkIfPermitted(Convert.ToInt64(usr.Usr_Role_ID), permFunc.PermFunc_ID);//usr.Usr_Role_ID is RoleId and permFunc.PermFunc_ID is the PermissionFunctionId
if(permission != null)
{
//Permission granted
}
else
{
//Permission Rejected
}
Thanks in advance
You can create custom AuthorizationAttribute with the logic of checking your roles and permissions in it and use it for operations that requires that.
You can use mvc.filters with your implementation of IAuthorizationFilter, to filter every request. Register it in your FilterConfig
filters.Add(new MyAuthorizationAttribute());
Updated to use CustomAuthorize attribute on MVC action
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class CustomAuthorize : AuthorizeAttribute
{
private string _action { get; set; }
public CustomAuthorize() { }
public CustomAuthorize(string action) { _action = action; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext.User == null)
return false;
if (!httpContext.User.Identity.IsAuthenticated)
return false;
// HasPermission function implements looking up by user name and action
// to see if user has a role that would give them access to this action
return PermissionChecker.HasPermission(httpContext.User.Identity.Name, _action);
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
// handle unauthorized requests here
// return 503 error or whatever
}
}
// example of using custom attribute in MVC controller action
[HttpGet]
[CustomAuthorize("View")]
public ActionResult MyActionActualViewMethodName()
{
var result = {
id = 1,
name = ""
};
return Json(result);
}
[HttpDelete]
[CustomAuthorize("Delete")]
public ActionResult MyActionActualDeleteMethodName(int id)
{
// do delete action
return Json(true);
}
// static permission checker implementation
public static class PermissionChecker
{
static List<GenericIdNameClass> users = new List<GenericIdNameClass>() {
new GenericIdNameClass { Id = 1, Name = "John" },
new GenericIdNameClass { Id = 2, Name = "Bob" },
};
static List<GenericIdNameClass> roles = new List<GenericIdNameClass>() {
new GenericIdNameClass { Id = 10, Name = "User" },
new GenericIdNameClass { Id = 11, Name = "Admin" },
};
static List<GenericIdNameClass> actions = new List<GenericIdNameClass>() {
new GenericIdNameClass { Id = 100, Name = "View" },
new GenericIdNameClass { Id = 101, Name = "Create/Edit" },
new GenericIdNameClass { Id = 102, Name = "Delete" },
};
static List<GenericEntityRelationClass> roleActionMappings = new List<GenericEntityRelationClass>() {
new GenericEntityRelationClass{ Id1 = 10, Id2 = 100 },
new GenericEntityRelationClass{ Id1 = 11, Id2 = 100 },
new GenericEntityRelationClass{ Id1 = 11, Id2 = 101 },
new GenericEntityRelationClass{ Id1 = 11, Id2 = 102 },
};
// John only has User role, Bob has User and Admin
static List<GenericEntityRelationClass> userRoleMappings = new List<GenericEntityRelationClass>() {
new GenericEntityRelationClass{ Id1 = 1, Id2 = 10 },
new GenericEntityRelationClass{ Id1 = 2, Id2 = 10 },
new GenericEntityRelationClass{ Id1 = 2, Id2 = 11 },
};
public static bool HasPermission(string userName, string actionName)
{
var user = users.SingleOrDefault(x => x.Name == userName);
if (user == null)
return false;
var action = actions.SingleOrDefault(x => x.Name == actionName);
if (action == null)
return false;
var userRoles = userRoleMappings.Where(x => x.Id1 == user.Id).Select(x => x.Id2).ToList();
return roleActionMappings.Any(x => userRoles.Contains(x.Id1) && x.Id2 == action.Id);
}
public class GenericIdNameClass
{
public int Id { get; set; }
public string Name { get; set; }
}
public class GenericEntityRelationClass
{
public int Id1 { get; set; }
public int Id2 { get; set; }
}
}
Alsamil,
If you have time, please read about the new way Microsoft is doing Claims-Based Authorization
And if you have even more time, I really recommend you this conference. Dominick Baier & Brock Allen are really known in the security industry and they explain how to do Authorisation in a really nice way which is related to the Claims-Based Authorization article. If I am not wrong they are the minds behind this new way of authorisation.
The answer that worked for the above question is here:
AuthorizationController
#region CustomAuthorizationAttribute
public class CustomAuthorizationAttribute : AuthorizeAttribute
{
private PermissionRepository _permission = new PermissionRepository();
private PermissionFuncRepository _permissionFun = new PermissionFuncRepository();
// roles start
public string IdentityRoles
{
get { return _permissionName ?? String.Empty; }
set
{
_permissionName = value;
}
}
private string _permissionName;
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
//do the base class AuthorizeCore first
if (httpContext.User.Identity.IsAuthenticated)
{
string RoleID = FormsAuthentication.Decrypt(httpContext.Request.Cookies[FormsAuthentication.FormsCookieName].Value).Name.Split('|')[1];
var permisionID = _permissionFun.FindByName(_permissionName);
if(permisionID != null)
{
var permis = _permission.GetPermission().Where(a => a.Perm_PermFuncID == permisionID.PermFunc_ID && a.Perm_RollID.ToString() == RoleID).FirstOrDefault();
if (permis != null)
{
return true;
}
}
}
return false;
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
//if the user is not logged in use the deafult HandleUnauthorizedRequest and redirect to the login page
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
base.HandleUnauthorizedRequest(filterContext);
}
else
//if the user is logged in but is trying to access a page he/she doesn't have the right for show the access denied page
{
filterContext.Result = new RedirectResult("~/Home/AccessDenied");
}
}
}
#endregion
Foreach ActionController, I accessed these Authorization as follows:
[CustomAuthorization(IdentityRoles = "AdjustmentsView")]
public ActionResult AdjustmentIndex()
{
var adjlist = _Adj.GetAdjustmentHead();
List<AdjustmentHeadViewModel> adjustlist = new List<AdjustmentHeadViewModel>();
foreach (var item in adjlist)
{
Mapper.Initialize(cfg => cfg.CreateMap<AdjustmentHead, AdjustmentHeadViewModel>());
AdjustmentHeadViewModel entity = Mapper.Map<AdjustmentHead, AdjustmentHeadViewModel>(item);
adjustlist.Add(entity);
}
return View(adjustlist);
}
You need to create a custom AuthorizeAttribute and tag your actions with it.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class RequireFunction : AuthorizeAttribute
{
private string _function;
public RequireFunction(string func) { _function = func; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext.User == null)
return false;
if (!httpContext.User.Identity.IsAuthenticated)
return false;
// modified code sample from question
string mail = httpContext.User.Identity.Name;
var user = _user.GetUserByMail(mail);
var permFunc = _permissionfunc.FindByName(_function);
var permission = _permission.checkIfPermitted(Convert.ToInt64(usr.Usr_Role_ID), permFunc.PermFunc_ID);
return permission != null;
}
}
I was reading about user Identity in Microsoft and trying to apply them in my MVC5 app.
Up to my knowledge the Id is the key, while the userName is not key and the definition says that it can be null,
so I was asking myself... why in the MVC5 project template, when you enter an already existing userName you will receive an Error message ??
I tried to reach to the userName validation but I couldn't.
Here is the database definition:
CREATE TABLE [dbo].[AspNetUsers] (
[Id] NVARCHAR (128) NOT NULL,
[UserName] NVARCHAR (MAX) NULL,
and here is the IdentityUser definition, notice (no validation):
namespace Microsoft.AspNet.Identity.EntityFramework
{
public class IdentityUser : IUser
{
public IdentityUser();
public IdentityUser(string userName);
public virtual ICollection<IdentityUserClaim> Claims { get; }
public virtual string Id { get; set; }
public virtual ICollection<IdentityUserLogin> Logins { get; }
public virtual string PasswordHash { get; set; }
public virtual ICollection<IdentityUserRole> Roles { get; }
public virtual string SecurityStamp { get; set; }
public virtual string UserName { get; set; }
}
}
and on registration, the UserManager.CreateAsync method is called, here is the definition:
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser() { UserName = model.UserName };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await SignInAsync(user, isPersistent: false);
return RedirectToAction("Index", "Home");
}
else
{
AddErrors(result);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
and this is the last thing I reach regarding CreateAsync:
public virtual Task<IdentityResult> CreateAsync(TUser user, string password);
I don't see validation anywhere in the code, however it won't allow you to enter an existing userName.
I think understanding how does this works will improve my experience with the Identity concept of asp.net and will improve my code.
Any guidance is highly appreciated
This happens in IdentityDbContext<TUser>, which your ApplicationDbContext probably inherits from. It overrides DbContext's ValidateEntity method to do the check. See this decompiled code:
protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
{
if ((entityEntry != null) && (entityEntry.State == EntityState.Added))
{
TUser user = entityEntry.Entity as TUser;
if ((user != null) && this.Users.Any<TUser>(u => string.Equals(u.UserName, user.UserName)))
{
return new DbEntityValidationResult(entityEntry, new List<DbValidationError>()) { ValidationErrors = { new DbValidationError("User", string.Format(CultureInfo.CurrentCulture, IdentityResources.DuplicateUserName, new object[] { user.UserName })) } };
}
IdentityRole role = entityEntry.Entity as IdentityRole;
if ((role != null) && this.Roles.Any<IdentityRole>(r => string.Equals(r.Name, role.Name)))
{
return new DbEntityValidationResult(entityEntry, new List<DbValidationError>()) { ValidationErrors = { new DbValidationError("Role", string.Format(CultureInfo.CurrentCulture, IdentityResources.RoleAlreadyExists, new object[] { role.Name })) } };
}
}
return base.ValidateEntity(entityEntry, items);
}
If you don't want this behavior you can inherit from DbContext directly.
When I'm looking at the example of ASP.NET Identity (https://www.nuget.org/packages/Microsoft.AspNet.Identity.Samples) I noticed that they use a UserValidator which is default set to RequireUniqueEmail = true;
The example uses the following code to set the RequireUniqueEmail property to true.
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
// Configure validation logic for usernames
manager.UserValidator = new UserValidator<ApplicationUser>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
return manager;
}
Perhaps this is the reason that the username is unique in your MVC application.
Try setting the property to false!?