MVC Identity validate user name and password along with custom property - c#

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

Related

C# Entity Framework Core how to add nested ThenInclude for User -> Manage relation

I have an entity called User and it has a navigation property Manager' which refers to a User entity.
public class User
{
public virtual User Manager { get; set; }
}
return await _context.User
.AsNoTracking()
.Include(user => user.Manager)
.ThenInclude(manager => manager.Manager)
private int? FindUser(int tagId, User user)
{
if (user.Manager == null)
{
Log.Error($"Cannot find User");
return null;
}
if (!user.Manager.UserTag.Any(x => !x.Disabled && x.TagId == tagId))
{
return FindExCoUser(tagId, user.Manager);
}
}
My requirement is for some cases I had to go up in the hierarchy of user managers'. In the above query I get only the current user and his manager but if I had to go up in the hierarchy, the manager is returned as null.
Do I need to keep adding .ThenInclude but how many times?
Or is there a better solution to achieve this?
Thanks
You can recursively select your managers. Here is an example I found on the Internet: https://michaelceber.medium.com/implementing-a-recursive-projection-query-in-c-and-entity-framework-core-240945122be6
But if you want to select all managers, your exit condition must be user.Manager == null instead of a max depth.

How can I add one more field to username and password for account creation and authentication in ASP.NET Identity?

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

CreateUserIdenityAsync returns "UserId not found" exception for custom IdentityUser

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.

ASPNET Identity user return the old record when get user by id

I had a problem when using identity user. After call the Update(user), the record in database has been changed. The problem occur when I call Method(string userId) again, contain FindById(userId). The FindById(userId) returns a user with all record still remain as the first although I has updated it before.
I implement user table:
public class ApplicationUser : IdentityUser
{
public int MyProperty { set; get; }
}
The user manager class:
public class ApplicationUserManager : UserManager<ApplicationUser>, IApplicationUserManager
{
public Task MethodAsync(string userId)
{
// Find user
// return object before change (MyProperty not equal 1)
ApplicationUser user = await base.FindByIdAsync(userId);
user.MyProperty = 1;
await base.UpdateAsync()
}
}
Does it happen only when you are updating the current logged-in user's properties? If so, try to re-sign in again:
SignInManager.SignIn(user, false, false)
The user object can be gotten from the UserManager.FindById method.

Dependency Injection with child objects

I have a class called User and it has a property of List <Role>.
Using dependency injection how do I populate the Roles given that I need to know the user first? I.e. the roles are dependent on the user so I can't inject them on instantiation as I do not know the user at this point as I need to first call a method like GetUser (username); which returns a user object.
Update
This is the class structure I have
public partial class User:IUser
{
private List<IUserRole> _userRoles = new List<IUserRole>();
public User()
{
}
public User GetUserByID(int id, dbEntities context)
{
User user = new User();
using(context)
{
user = (from u in context.Users where u.ID == id select u).FirstOrDefault();
}
return user;
}
public User GetUser(string username, string password, dbEntities context)
{
User user = new User();
using (context)
{
user = (from u in context.Users where u.Username==username && u.Password==password select u).FirstOrDefault();
}
return user;
}
List<IUserRole> UserRoles
{
get
{
return _userRoles;
}
set
{
_userRoles = value;
}
}
}
As you can see, a populated user object is retrieved via various methods. However I really want the UserRoles to be returned with the object too but injecting the roles would not work as they can not be retrieved until the user is populated.
Having directly accessible List<Roles> forces you populate the list at the moment User is created. It means your "factory" for the User object need to also call "role provider" to obtain list of roles for the user.
Assuming you are using some DI container like Unity code would look similar to:
_container.RegisterFactory(container =>
{
var user = new User();
user.Roles = container.Resolve<IRolesProvider>()
.GetRolesForUser(user);
return user;
});
Alternatively you can add dependency to "roles provider" to User and compute (possibly with caching) on demand:
class User
{
IRolesProvider roles;
public User(IRolesProvider roles,....)
{
this.roles = roles;
...
}
public List<Roles> { get { return roles.GetRolesForUser(this);}}
...
}

Categories