I am reviewing the scaffolded identity account management pages in an Asp.Net Core website project. The OnGetAsync() in the change email Razor page loads the current user using UserManager.GetUserAsync().
It then calls another method to load the email address and IsEmailConfirmed value into properties of the page model.
The load method uses UserManager.GetEmailAsync(user) instead of just user.Email. Why would the Microsoft coders do that?
It does the same thing for the IsEmailConfirmed by using UserManager.IsEmailConfirmedAsync(user) instead of just user.IsEmailConfirmed.
What am I missing?
public partial class Email : PageModel
{
public string Email { get; set; }
[BindProperty]
public InputModel Input { get; set; }
public bool IsEmailConfirmed { get; set; }
public async Task<IActionResult> OnGetAsync()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
// user should be valid here.
await LoadAsync(user);
return Page();
}
protected async Task LoadAsync(IdentityUser user)
{
// user should be valid here.
// Why not just "string email = user.Email"?
string email = await _userManager.GetEmailAsync(user);
Email = email;
Input = new InputModel { NewEmail = email };
// Why not just do "IsEmailConfirmed = user.IsEmailConfirmed"?
IsEmailConfirmed = await _userManager.IsEmailConfirmedAsync(user);
}
public class InputModel
{
[Required]
[EmailAddress]
[Display(Name = "New email")]
public string NewEmail { get; set; }
}
}```
That has to be an implementation issue. Looking at the UserManager class at github, the GetEmailAsync makes a request to the database and also the duo. Which is not proper since the first request already retrieves everything about the user. You can implement that yourself anyway.
I have a service in my application which creates a User, and saves it to the database. Here's a method of my service:
public async Task<UserDTO> CreateUserAsync(User newUser)
{
var result = (UserDTO)await _userRepository.CreateUserAsync(newUser);
if (result != null)
{
await _userRepository.SaveChangesAsync();
}
return result;
}
And a method from a UserRepository:
public async Task<User> CreateUserAsync(User newUser) => (await _dbContext.AddAsync(newUser)).Entity;
Here's a User class:
public class User
{
public int UserId { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
}
The problem is when the user is being added via service, UserId is not known yet. It has default value 0, then ef core saves it to a database, finding a proper UserId. But value returned by my methods has no UserId updated - it is still 0, and i would like to return updated value. How to achieve that in a proper way?
newUser WILL have an Id read from the database.
Your code is casting from User to UserDTO, which is unlikely to work.
I am creating my Login form in my project as same from Session, but now in this project i want to create Login form without using any session. Anyone here to guide me?
There is my Work using Session,
This is Controller of LogIn:
public class LoginController : Controller
{
IMEI_WEB_MVC.Connections.IMEI_DBEntities imeidb = new Connections.IMEI_DBEntities();
Functions func = new Functions();
// GET: Login
public ActionResult Index()
{
return View();
}
[HttpGet]
public ActionResult Autherize(log_variable logvariable_model)
{
log_variable lgv = new Models.log_variable();
string pwd = func.security(logvariable_model.usr_pwd);
var userDetails = imeidb.new_usr.Where(x => x.usr_nam == logvariable_model.usr_nam && x.usr_pwd == pwd).FirstOrDefault();
if (userDetails == null)
{
logvariable_model.LogErrorMessage = " Invalid Name or Password";
return View("Index", logvariable_model);
}
else
{
Session["usr_id"] = userDetails.usr_id;
Session["com_id"] = logvariable_model.com_id;
Session["br_id"] = logvariable_model.br_id;
//lgv.usr_id = userDetails.usr_id;
//lgv.com_id = logvariable_model.com_id;
//lgv.br_id = logvariable_model.br_id;
return RedirectToAction("index", "m_dpt");
This is my Model:
public class log_variable
{
[Required(ErrorMessage = "User Name cannot be blank")]
[DisplayName("Name")]
public string usr_nam { get; set; }
[Required(ErrorMessage = "Password cannot be Blank")]
[DataType(DataType.Password)]
[DisplayName("Password")]
public string usr_pwd { get; set; }
public int usr_id { get; set; }
[Required(ErrorMessage = "Company ID cannot be blank")]
[MaxLength(2)]
[DisplayName("Company_ID")]
public string com_id { get; set; }
[Required(ErrorMessage ="Branch ID cannot be blank")]
[MaxLength(3)]
[DisplayName("BRANCH_ID")]
public string br_id { get; set; }
public string LogErrorMessage { get; set; }
}
AFAIK, to control this kind of user's access, you have basically only two options:
You can manage when they logging in and when they are logging out (sessions), OR
You can give them a ticket and they have to pass it to your application (Security token, like JWT).
(If anybody knows anotehr method to do it, please, tell me)
Ask your boss if it's about storing data in a in-memory session, or to avoid cookis, bc there are ways to do that with sessions. Take a look at
this (sessions in db) and this (cookieless sessions)
If you find any other way, please, tell us.
Good luck.
I have a problem using UserManager.CheckPassword(...) with a custom IUser implementation.
I am using .net Identity with Owin, creating my own implementations of IUser and IUserStore.
For the store, I only use the optional Email- and Password-Stores.
The IUser implementation is seen here, with the basic fields:
public class RegisteredUser : IUser<string>
{
public RegisteredUser()
{
Id = Guid.NewGuid().ToString();
}
public string Id { get; }
public string UserName { get; set; }
public string PasswordHash { get; set; }
public string Email { get; set; }
public bool EmailConfirmed { get; set; }
}
The problem occurs when validating the user when logging in.
I am unable to use the SignInManager.PasswordSignIn(...) as my store doesnt implement the LockoutStore.
Instead I use the UserManager.CheckPassword(...), but this throws an FormatException, saying:
The input is not a valid Base-64 string...
UserSignInManager manager = HttpContext.GetOwinContext().Get<UserSignInManager>();
RegisteredUser user = manager.UserManager.FindByEmail(model.Email);
if (user != null && manager.UserManager.CheckPassword(user, model.Password))
{
manager.SignIn(user, model.RememberMe, model.RememberMe);
return RedirectToAction("UserProfile");
}
Thinking about this, I also fail to understand how the CheckPassword() method is able to check the password of my custom implementation without knowing where to look.
What am I doing wrong?
Alright - Brainfart on my behalf..
The UserManager is able to check the password as my UserStore implements the following method
public Task<string> GetPasswordHashAsync(RegisteredUser user)
{
string passwordHash = AWSUser.GetPasswordHash(user.Id);
return Task.FromResult(passwordHash);
}
The problem was in the AWSUser class, which has the following method
public static string GetPasswordHash(string id)
{
return tblUsers.Find(u => u.Id.Equals(id)).Id;
}
And as I am errorously returning the ID, that ofcourse fails..
I'm trying to use the new ASP.NET Identity in my MVC5 application, specifically I'm trying to integrate ASP.NET Identity into an existing database. I've already read the questions/answers on SO pertaining to DB First and ASP.NET Identity, and having followed all the recommendations I still can't add roles to my database, although I have no problems adding users. Here's my code:
var context = new PayrollDBEntities();
var roleManager = new RoleManager<AspNetRole>(new RoleStore<AspNetRole>(context));
bool roleExists = roleManager.RoleExists(roleDto.Name);
if (roleExists){
return false;
}
var role = new AspNetRole(roleDto.Name){
Name = roleDto.Name,
};
IdentityResult result = roleManager.Create(role);//Getting exception here
At the last line of code I get an exception of type 'System.InvalidOperationException': The entity type IdentityRole is not part of the model for the current context.
Here is my context:
public partial class PayrollDBEntities : IdentityDbContext
{
public PayrollDBEntities()
: base("name=PayrollDBEntities")
{
}
public virtual DbSet<AspNetRole> AspNetRoles { get; set; }
public virtual DbSet<AspNetUserClaim> AspNetUserClaims { get; set; }
public virtual DbSet<AspNetUserLogin> AspNetUserLogins { get; set; }
public virtual DbSet<AspNetUser> AspNetUsers { get; set; }
......
}
My AspNetUser and AspNetRole classes derive from IdentityUser and IdentityRole respectively, but I'm still getting that exception. Here is my database diagram:
Any help would be greatly appreciated.
You have to specify during the creation of User Store that AspNetRole is used instead of IdentityRole. You can achieve this by using the UserStore class with 6 type parameters:
new UserStore<AspNetUser, AspNetRole, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>(new PayrollDBEntities());
This indicates changes at User Manager creation as well. Here is a simplified example about the creation of needed instances:
public class AspNetUser : IdentityUser { /*customization*/ }
public class AspNetRole : IdentityRole { /*customization*/ }
public class PayrollDBEntities : IdentityDbContext //or : IdentityDbContext <AspNetUser, AspNetRole, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>
{
}
public class Factory
{
public IdentityDbContext DbContext
{
get
{
return new PayrollDBEntities();
}
}
public UserStore<AspNetUser, AspNetRole, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim> UserStore
{
get
{
return new UserStore<AspNetUser, AspNetRole, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>(DbContext);
}
}
public UserManager<AspNetUser, string> UserManager
{
get
{
return new UserManager<AspNetUser, string>(UserStore);
}
}
public RoleStore<AspNetRole> RoleStore
{
get
{
return new RoleStore<AspNetRole>(DbContext);
}
}
public RoleManager<AspNetRole> RoleManager
{
get
{
return new RoleManager<AspNetRole>(RoleStore);
}
}
}
After a few days of trying to get this to work in a clean manner, I've come to the conclusion that if you're using Database first and want to integrate ASP.NET Identity into your app, by far the easiest and cleanest solution is to create your own membership provider by overriding ASP.NET Identity. It's actually pretty easy, so far I've implemented UserStore and RoleStore to my liking. I've added columns/relations specific to my domain in my database, and whenever I create a user or a role, I take care of my database commits by adding the required relations. My UserStore implementation is quite similar to this. My RoleStore implementation is something like this:
public class ApplicationRoleStore : IRoleStore<ApplicationRoleDTO>
{
private PayrollDBEntities _context;
public ApplicationRoleStore() { }
public ApplicationRoleStore(PayrollDBEntities database)
{
_context = database;
}
public Task CreateAsync(ApplicationRoleDTO role)
{
if (role == null)
{
throw new ArgumentNullException("RoleIsRequired");
}
var roleEntity = ConvertApplicationRoleDTOToAspNetRole(role);
_context.AspNetRoles.Add(roleEntity);
return _context.SaveChangesAsync();
}
public Task DeleteAsync(ApplicationRoleDTO role)
{
var roleEntity = _context.AspNetRoles.FirstOrDefault(x => x.Id == role.Id);
if (roleEntity == null) throw new InvalidOperationException("No such role exists!");
_context.AspNetRoles.Remove(roleEntity);
return _context.SaveChangesAsync();
}
public Task<ApplicationRoleDTO> FindByIdAsync(string roleId)
{
var role = _context.AspNetRoles.FirstOrDefault(x => x.Id == roleId);
var result = role == null
? null
: ConvertAspNetRoleToApplicationRoleDTO(role);
return Task.FromResult(result);
}
public Task<ApplicationRoleDTO> FindByNameAsync(string roleName)
{
var role = _context.AspNetRoles.FirstOrDefault(x => x.Name == roleName);
var result = role == null
? null
: ConvertAspNetRoleToApplicationRoleDTO(role);
return Task.FromResult(result);
}
public Task UpdateAsync(ApplicationRoleDTO role)
{
return _context.SaveChangesAsync();
}
public void Dispose()
{
_context.Dispose();
}
private ApplicationRoleDTO ConvertAspNetRoleToApplicationRoleDTO(AspNetRole aspRole)
{
return new ApplicationRoleDTO{
Id = aspRole.Id,
EnterpriseId = aspRole.EnterpriseId,
Name = aspRole.Name
};
}
private AspNetRole ConvertApplicationRoleDTOToAspNetRole(ApplicationRoleDTO appRole)
{
return new AspNetRole{
Id = appRole.Id,
EnterpriseId = appRole.EnterpriseId,
Name = appRole.Name,
};
}
}
And my ApplicationRoleDTO:
public class ApplicationRoleDTO : IRole
{
public ApplicationRoleDTO()
{
Id = Guid.NewGuid().ToString();
}
public ApplicationRoleDTO(string roleName)
: this()
{
Name = roleName;
}
public string Id { get; set; }
public string Name { get; set; }
public Guid EnterpriseId { get; set; }
}
I also found these 2 articles pretty helpful:
Overview of Custom Storage Providers for ASP.NET Identity
Implementing a Custom MySQL ASP.NET Identity Storage Provider
I'll explain here with the code exampels :).
The trick is, they are already in the IdentityDbContext (AspNetRoles, AspNetUserClaims, AspNetUsers, ....)
In the IdentityModel you will see ApplicationUser is empty at the top. If you want to customize these users or roles, just add properties here and then update your database via the console
Example of my context
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
public DbSet<Request> Requests { get; set; }
public DbSet<Reservation> Reservations { get; set; }
public DbSet<PriceType> PriceTypes { get; set; }
public DbSet<Product> Products { get; set; }
public DbSet<Price> Prices { get; set; }
public DbSet<GuestbookPost> Posts { get; set; }
public DbSet<Count> Counts { get; set; }
public DbSet<Invoice> Invoices { get; set; }
public DbSet<InvoiceLine> InvoiceLines { get; set; }
...
}
So no application user is defined here, but I did add more properties to it, example:
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string GroupName { get; set; }
public string Email { get; set; }
[StringLength(15)]
public string Phone { get; set; }
public string Remark { get; set; }
public DateTime? BirthDate { get; set; }
public DateTime ValidFrom { get; set; }
public DateTime ValidUntil { get; set; }
public string Street { get; set; }
public string ZipCode { get; set; }
public string City { get; set; }
public virtual ICollection<Request> Requests { get; set; }
}
I know this is an old question, but just in case someone else is having a hard time adding roles/users when they modified asp identity to use numeric primary keys (int/long) instead of the default string for the Identity Roles, so if you have changed the IdentityUserRole in IdentityModels.cs to something like this:
public class Role : IdentityRole<long, UserRole>
{
public Role() { }
public Role(string name) { Name = name; }
}
You have to use the class Role instead of the default IdentityRole when constructing the RoleManager, so your code should be like this:
public static void RegisterUserRoles()
{
ApplicationDbContext context = new ApplicationDbContext();
var RoleManager = new RoleManager<Role, long>(new RoleStore(context));
if (!RoleManager.RoleExists("Administrador"))
{
var adminRole = new Role {
Name = "Administrador",
};
RoleManager.Create(adminRole);
}
}
So this should populate your database properly, I think all experienced ASP programmers already know this, but for others this could take some time to figure out.
I solved with a different way.
First I splited in two different Projects and Contexts.
My project that Handle the Identity has this context:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>, IDisposable
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
This is my ApplicationUser:
public class ApplicationUser : IdentityUser
{
//Put here the extra properties that Identity does not handle
[Required]
[MaxLength(150)]
public string Nome { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
}
And my ApplicationUserManager looks like this:
public class ApplicationUserManager : UserManager<ApplicationUser>
{
public ApplicationUserManager(IUserStore<ApplicationUser> store)
: base(store)
{
//Setting validator to user name
UserValidator = new UserValidator<ApplicationUser>(this)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
//Validation Logic and Password complexity
PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = false,
RequireDigit = false,
RequireLowercase = false,
RequireUppercase = false,
};
//Lockout
UserLockoutEnabledByDefault = true;
DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
MaxFailedAccessAttemptsBeforeLockout = 5;
// Providers de Two Factor Autentication
RegisterTwoFactorProvider("Código via SMS", new PhoneNumberTokenProvider<ApplicationUser>
{
MessageFormat = "Seu código de segurança é: {0}"
});
RegisterTwoFactorProvider("Código via E-mail", new EmailTokenProvider<ApplicationUser>
{
Subject = "Código de Segurança",
BodyFormat = "Seu código de segurança é: {0}"
});
//Email service
EmailService = new EmailService();
// Definindo a classe de serviço de SMS
SmsService = new SmsService();
var provider = new DpapiDataProtectionProvider("Braian");
var dataProtector = provider.Create("ASP.NET Identity");
UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(dataProtector);
}
}
I hope that this helps someone.
This solution was from this article:
Eduardo Pires - But it is in Portuguese
I fixed this issue by changing the web.config DefaultConnection connectionString property so it points to the new SQLServer database