I'm creating an IdentityUser through the Identity context, using OnModelCreating, but I'm having problems passing the password, as it must be encrypted.
Here is the code where I create the user:
builder.Entity<IdentityUser>().HasData(new IdentityUser { Id= "0CD0E173758F4492B41EDD4A51D36BA5", UserName = "admin#admin.com", Email = "admin#admin.com",EmailConfirmed = true });
I would like to know how I can pass the password, because I need to login to this user.
Related
I've added a custom property IsDeleted by deriving IdentityUser
public class AppUser : IdentityUser
{
public int IsDeleted { get; set; }
}
Currently I am trying to authenticate the user name and password provided by the user using the following code.
var userManager = HttpContext.GetOwinContext().GetUserManager<AppUserManager>();
AppUser user = userManager.Find(userDetails.UserName, userDetails.Password);
Assume here, I've created a user with user name "abc#xyz.com" and then deleted the user. When I deleted the user, the IsDeleted property will be set to 1.
Later, I created another or the same user with the same user name 'abc#xyz.com' and the later one is active in the system.
How could I ensure the userManager.Find will validate the active user and not the deleted one?
You can do as follows:
AppUser user = userManager.Users.Where(u => u.UserName == userDetails.UserName && u.IsDeleted != 1)
.FirstOrDefault();
If you need to include password in checking then yo can do as follows:
var userHashedPassword = new PasswordHasher().HashPassword(userDetails.Password);
AppUser user = userManager.Users.Where(u => u.UserName == userDetails.UserName &&
u.PasswordHash == userHashedPassword && u.IsDeleted != 1).FirstOrDefault();
I am trying to register and authenticate users who need more than username and password for authentication because their username is their school registration number and we are enrolling many schools on our platform where registration number may clash and wouldn't be unique anymore except we distinguish it with School Id ...
For instance, when searching for the user within the model instead of using this
var user = UserManager.Find(model.UserName, model.Password);
We use
var user = UserManager.Find(model.UserName, model.Password, model.SchooId);
You need to create a new model that inherits from IdentityUser:
public class ApplicationUser : IdentityUser
{
//add more attributes
public string SchoolID { get; set; }
}
I'm following along the bitoftech tutorial about creating Identity and role based claims with JWT. My application user is a custom User table with int PK.
Currently, the GenerateUserIdentityAsync method just returns a weird UserId not found error. here's my code:
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, "JWT");
and the implementation in User entity:
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<User, int> manager, string authenticationType)
{
//error on this line: CreateIdentityAsync throws error
var userIdentity = await manager.CreateIdentityAsync(this, authenticationType);
return userIdentity;
}
My UserManager class is defined like so:
public class AppUserManager : UserManager<User, int>
Weirdly enough, when I debug, the instance this in GenerateIdentityAsync does have a UserId property, but the base only has an id and I wonder if that is where it's erroring out? (it doesnt sound right)
I was looking at the source code (line 80) but I can't figure out where the exception is being thrown.
The exact exception being thrown is:
UserId not found.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details:
System.InvalidOperationException:
UserId not found.
And stack trace isn't all that helpful (to me)
How do I find out why / where the UserId is not available?
Mode details:
My GrantResourceOwnerCredentials():
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] {"*"});
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
User user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null) // this is NOT null
{
context.SetError("invalid_grant", "The username or password is incorrect");
return;
}
// this line fails
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, "JWT");
var ticket = new AuthenticationTicket(oAuthIdentity, null);
context.Validated(ticket);
}
And the ApplicationUser (which, in my case, is just User)
public partial class User : IdentityUser<int, CustomUserLogin, CustomUserRole, CustomUserClaim>
{
public int UserId { get; set; }
public string Fullname { get; set; }
public string Address { get; set; }
public string ContactNumber { get; set; }
}
As you found out while debugging IdentityUser has an Id which in your case would represent the User's Id.
You need to remove the UserId from your User class, use the base Id from IdentityUser and rename the UserId column in your custom User table to Id.
Any properties you have in your User class needs to also have a matching column in your user table in the database. If not then you will get the same error for properties that do not match.
That would mean Fullname, Address and ContactNumber must have matching column names in the AspNetUsers table or else you will get the same error for those properties as well.
You have both UserId and Id properties in your User class - Id is inherited from IdentityUser. The problem is that you probably configured UserId to be the primary key for User.
The exception you get is thrown in ClaimsIdentityFactory.CreateAsync method, on line 97 UserManager.GetSecurityStampAsync. As you can see, user.Id used for retrieving a security stamp.
If you look inside UserManager.GetSecurityStampAsync you will see that the exception you get is thrown exactly here:
public virtual async Task<string> GetSecurityStampAsync(TKey userId)
{
ThrowIfDisposed();
var securityStore = GetSecurityStore();
var user = await FindByIdAsync(userId).WithCurrentCulture();
if (user == null)
{
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, Resources.UserIdNotFound,
userId));
}
return await securityStore.GetSecurityStampAsync(user).WithCurrentCulture();
}
Thus, remove UserId property from User class and start using Id (inherited from IdentityUser) instead.
I faced exact same issue. After much of a head ache I could sort out the real problem.
When you change data type of the Id property of User class to string, a Guid().ToString() is assigned to the Id property in the constructor and the same value is saved to the database and Identity retrieves the user details using that value.
However, if you changed the data type of the Id property to int and did not provide a value for that property in the constructor, the Identity still tries to retrieve the User details by using the default int value (0) this causes throws the message "System.InvalidOperationException: UserId not found".
I solved this by retrieving the value from the database by command.ExecuteScalar() and assign the value to user.Id.
Hope this will help some one facing similar problem.
What does your ApplicationUser class look like?
What does this method look like in your application?
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context){}
Taiseer's comments about GrantResourceOwnerCredentials are:
"we are building an identity for the logged in user, this identity
will contain all the roles and claims for the authenticated user"
I had to add the ClaimTypes.NameIdentifier to the ClaimsIdentity to resolve a similar issue. Here is the important part of my GrantResourceOwnerCredentials method:
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()));
In my case, this error happened because I added another SignInManager inside the method Login in the Account Controller (following an example of adding a role). It was executing SignInManager.PasswordSignInAsync and SignInManager.SignInAsync in the same method and this calls GenerateUserIdentityAsync(...) in ApplicationUser twice, for which the first one succeeded and the second one gave me the exception "UserId" Not Found.
Server=xxxx;Initial Catalog=xxxx;;User ID=xxxx_user;Password=xxxx; where user id has a space in it as opposed to
Server=xxxx;Initial Catalog=xxxx;;UserID=xxxx_user;Password=xxxx; WRONG!!!!!!!
See https://www.connectionstrings.com/sql-azure/ for confirmation.
I have an Entity Framework application using ASP.NET Identity 2.2 (i.e., my context inherits from IdentityDbContext<T> and I have a User class that inherits from IdentityUser). I am successfully seeding the AspNetUsers table using the following calls in my seed method:
var testUser = new User() {
UserName = "TestUser",
Email = "TestUser#Domain.tld"
};
manager.Create(testUser, "TestPassword");
I have extended the model to include a Post class which includes a reference to my User class:
public class Post {
public Post() {}
[Required]
public int Id { get; set; }
[Required]
public User User { get; set; }
}
And, of course, this corresponds to the following in my User class:
public class User : IdentityUser {
public User() : base() {
this.Posts = new HashSet<Post>();
}
public ICollection<Post> Posts { get; set; }
//Additional members...
}
I am then seeding the Posts collection with the following:
var testPost = new Post() { Id = 1, User = testUser };
context.Posts.AddOrUpdate(
post => post.Id,
testPost
);
Technically, this works fine; the Post instance is created, and the automatically generated User_Id field is correctly populated with the Id of the newly created User instance.
So what's the problem? Every time it runs, I get the following in my EntityValidationErrors: "The User field is required". It doesn't prevent my application from working, but it makes it difficult to detect legitimate errors.
Obviously, I could add custom code to my DbContext.SaveChanges() method in order to ignore this error, but I'd rather understand why it's happening in the first place, particularly if there's a problem with how I'm seeding my data.
It seems that when the UserManager.Create() method is called, it doesn't update the User instance with the information needed to create a reference. My assumption is that it's not populating the Id field, but I haven't confirmed.
Regardless, the solution is to reload the user after the call to UserManager.Create(). My final code looks something like:
var manager = new UserManager<User>(new UserStore<User>(context));
var testUser = new User() {
UserName = "TestUser",
Email = "TestUser#Domain.tld"
};
if (manager.Users.Count<User>() == 0) {
manager.Create(testUser, "TestPassword");
}
testUser = (User)manager
.Users
.Where<User>(u => u.UserName == "TestUser")
.FirstOrDefault<User>();
I was then able to seed the Post record the same way as before:
var testPost = new Post() { Id = 1, User = testUser };
context.Posts.AddOrUpdate(
post => post.Id,
testPost
);
Note: If using a [ForeignKey] attribute, it is apparently necessary to assign the User by Id (e.g., UserId = testUser.Id) instead of by object reference (e.g., User = testUser).
In my actual application this is all shuffled off to a CreateUser() helper method so it's easy to create multiple test users, but this covers the basics. This also addresses a flaw in my original code in that I wasn't previously checking to determine if the user had already been created or not.
I'm trying to write my own account registration logic in an Mvc 4 application. I started with a basic template instead of the internet template. At the moment I have a Tenant model with a number of properties. Using code first I've created a tenant table in my database (there will be more models and tables added over time - I'm just trying to get one working first).
I've created a RegisterTenantModel - similar to the one in the internet template except I'm using email instead of username.
public class TenantRegisterModel
{
[Required]
[Display(Name="Email")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
In my RegisterTenant method below I want to:
Allow a user (tenant) to initially sign up only with an email address and password, and only allow them to enter the rest of their tenant related properties once their account has been verified by email, hence the use of the TenantRegisterModel.
On calling Membership.CreateUser, how do I know that model.Email and model.Password will be added to the Tenant table? Likewise, if I was doing the same scenario as described above for a Landlord, how would I know that the details entered are being added to the Landlord table? Is there a way with Membership.CreateUser I can specify what table to add the user's details too?
RegisterTenant:
public ActionResult RegisterTenant(TenantRegisterModel model)
{
if (ModelState.IsValid)
{
// Attempt to register a tenant account
MembershipCreateStatus createStatus;
Membership.CreateUser(model.Email, model.Password);
// ...
}
}
I of course have a context class, which at the moment has public DbSet<LetLord.Models.Tenant> Tenants { get; set; }, so how do I know what table a user's details are being added if I had multiple DbSets?
MVC 4 Does not use the same membership as MVC 3. You want to look up simplemembership. This is actually a very good solution to the common issue of storing additional data for a user.
http://weblogs.asp.net/jgalloway/archive/2012/08/29/simplemembership-membership-providers-universal-providers-and-the-new-asp-net-4-5-web-forms-and-asp-net-mvc-4-templates.aspx
this is a good place to start reading about it. Not sure how familiar you are with the MVC3 Membership Provider and Database Schema but this has changed a lot in MVC4. It took me a while to find everything.
Basically with the new setup you can create your own user table with any info you want and simply map 1 column of your database for the id (which is now an int and not a (guid) like the mvc3 membership provider and schema) and the "username" which can be an email or any unique identifier like that
WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: false);
You link up your connection string to the database and it will create the membership schema with that database. Then you can add the model to your context and use Entity framework for functions like dbcontext.UserProfile.FirstName and such.
here is some more starting point material
http://patrickdesjardins.com/blog/asp-net-membership-with-mvc4
good luck
You should probably implement Membership by yourself and register it as MembershipProvider in your web.config. This way you have full control what happens with the data and you still have all the nice features.
There is a sample project in MVC3 that implements a Code-first Membership- and Roleprovider which would give you an idea about how it's done.
Alternatively, you could use the ProfileProvider to save some activation key (and your other custom user fields) and add the user to some role like "NotActivated".
Update: to my knowledge, the standard Membership-Provider uses the DefaultConnection in the web.config. If you use Code-First in the same database, it will break because there's a structure that is not made from code-first already. You could use database-first alternatively, I think.
How do I know that model.Email and model.Password will be added to the Tenant table?
I added additional properties to the UserProfile model. Then in the CreateUserAndAccount method I dynamically passed the properties in as follows:
WebSecurity.CreateUserAndAccount(model.UserName, model.Password,
new { Email = model.Email,
FirstName = model.FirstName,
LastName = model.LastName,
AccountType = model.AccountType,
DaytimeContactNumber = "",
EveningContactNumber = "" },
false);
Likewise, if I was doing the same scenario as described above for a Landlord, how would I know that the details entered are being added to the Landlord table? Is there a way with Membership.CreateUser I can specify what table to add the user's details too?
I created roles for landlords and tenants, and in Register, just after CreateUserAndAccount I checked the value of enum AccountType:
if (model.AccountType == AccountType.Tenant)
{
try
{
Roles.AddUserToRole(model.UserName, "Tenant");
}
catch (Exception e)
{
ModelState.AddModelError("Unable to add user to role", e);
}
}