Core 3.0 ApplicationUser is always empty - asp.net-core

I have extended Identityuser
public class ApplicationUser : IdentityUser
{
[MaxLength(150)]
public string FirstName { get ; set ; }
[MaxLength(150)]
public string LastName { get ; set ; }
public int AlternateUserId { get ; set ; }
[MaxLength(150)]
public string CompanyName { get ; set ; }
[MaxLength(38)]
[Required]
public string ClientId { get ; set ; }
[Required]
public int ShortClient { set ; get ; }
public bool Locked { set ; get ; }
}
In Startup.cs i have:
services.AddIdentity<ApplicationUser, IdentityRole>().AddDefaultUI().AddEntityFrameworkStores<ApplicationDbContext>();
services.AddSingleton<ApplicationUser>();
But in
public static class IdentityExtentionMethods
{
public static string FirstName(this IIdentity identity)
{
var claim = ((ClaimsIdentity)identity).FindFirst(ClaimTypes.GivenName);
// Test for null to avoid issues during local testing
return (claim != null) ? claim.Value : string.Empty;
}
}
Claim is always null and anywhere I try to inject ApplicationUser the variable is available but it is not populated with the user information.
#inject ApplicationUser applicationUser
#inject SignInManager<ApplicationUser> signInManager;
Instead it has some dummy values in a few of the Guid fields and most everything else is null.

Yeah, you can't just inject ApplicationUser. After authentication, all you have is a ClaimsPrincipal, not an ApplicationUser instance. If you need an actual ApplicationUser instance, then you must query that out of the database based on the user id present in the ClaimsPrincipal (HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier)).

As Chris Pratt points out you can't get ApplicationUser via injection in Core 3.x I am not sure about earlier versions. To bad its not in the documentation anywhere that I could see.
But you can get
SignInManager<ApplicationUser> _signInManager,
UserManager <ApplicationUser> _userManager
ApplicationDbContext _dbContext
And as Chris also points out you can get
ClaimsPrincipal
and
IPrincipal
I have IPrincipal and as the code shows below, that with SignInManager & UserManager is all you need to get ApplicationUser
public static class IdentityExtentionMethods
{
public static bool IsSysAdmin(this IPrincipal _principal,
SignInManager<ApplicationUser> _signInManager,
UserManager <ApplicationUser> _userManager)
{
var x = isSysAdmin(_principal, _signInManager, _userManager);
if (x.Result == false)
return false;
else
return true;
}
public static async Task<bool> isSysAdmin(this IPrincipal _principal,
SignInManager<ApplicationUser> _signInManager,
UserManager <ApplicationUser> _userManager)
{
var ci = _principal.Identity as ClaimsIdentity;
var userName = ci != null ? ci.FindFirst(ClaimTypes.Name) : null;
string username = userName?.Value;
// get ApplicationUser
var appUser = await _userManager.FindByNameAsync( username);
var _userClaims = await
_signInManager.ClaimsFactory.CreateAsync(appUser);
if (_userClaims.UserHasThisPermission(Permissions.AccessAll))
return true;
else
return false;
}
public static bool HasRole( this IPrincipal _principal,
string roleName,
SignInManager<ApplicationUser> _signInManager,
UserManager <ApplicationUser> _userManager,
ApplicationDbContext _dbContext)
{
var x = hasrole ( _principal , roleName , _signInManager , _userManager , _dbContext ) ;
if (x.Result == false)
return false;
else
return true;
}
private static async Task<bool> hasrole ( this IPrincipal _principal,
string roleName,
SignInManager<ApplicationUser> _signInManager,
UserManager <ApplicationUser> _userManager,
ApplicationDbContext _dbContext)
{
if (roleName == null)
throw new ArgumentNullException(nameof(roleName));
var ci = _principal.Identity as ClaimsIdentity;
var userName = ci != null ? ci.FindFirst(ClaimTypes.Name) : null;
string username = userName?.Value;
var appUser = await _userManager.FindByNameAsync( username);
if (_dbContext.Find<UserToRole>(appUser.Id, roleName) != null)
{
return true ;
}
return false ;
}
}
You access like this from _layout.cshtml
#using Microsoft.AspNetCore.Identity
#inject ApplicationDbContext dbcontext ;
#inject UserManager<ApplicationUser> userManager ;
#inject SignInManager<ApplicationUser> signInManager;
;;
;;
#if ( this.User.IsSysAdmin ( signInManager , userManager ) )
{
<!-- add menu stuff -->
}
#if ( this.User.HasRole ( signInManager , userManager,dbcontext ) )
{
<!-- add menu stuff -->
}
certainly seems like a lot of stuff to pass around but it gets the job done.
BTW, the claims stuff is from https://www.thereformedprogrammer.net/part-7-adding-the-better-asp-net-core-authorization-code-into-your-app/
Jon Smith has written a wonderful app which has an MIT open source license and allows you to use roles and permissions in Core 3.0/1 It is very complex but he provided a scaled down version https://github.com/JonPSmith/PermissionsOnlyApp that works well. Thanks Jon.

Related

ASP.NET Core Authorize & UserRoles in Multy Tenancy

We have am ASP.NET Core 6.0 multi tenant application with a single database. The tenant tables have a TenantId column to show only tenant data. We don't need to discuss whether is better or not to have multiple databases, for our purpose we need it this way.
All tenants share the users table and there is a UserTenant table to filter which users a tenant can see. But it is not relevant for this question.
We need to Authorize some controllers and for that we need to do this using roles:
[Authorize(Roles = "RoleA,RoleB,RoleC")]
public class SomeController : Controller
This way, a user can get into the controller only if belongs to one of those roles.
The problem is we need to assign roles to users in a certain tenant. So a "UserA" can have the "RoleA" in "TenantA", but not in "TenantB".
The Roles would be the same for all the tenants, so no tenantId in the table "aspnetroles", but we do need another property for table "aspnetuserroles" since the key sould be "UserId, RoleId,TenantId".
Our Application user is configured this way:
public class ApplicationUser : IdentityUser<int>
{
public string Name { get; set; } = String.Empty;
public string Surname { get; set; } = String.Empty;
public string? Alias { get; set; }
....
}
public class ApplicationRole : IdentityRole<int> { }
public class ApplicationUserLogin : IdentityRole<int> { }
How can we change the Identity UserRoles in order to, not only store the UserId, RoleId and TenantId but also to work with the Authorize in the controller?
Thanks in advance.
UPDATE 1
I have been able to customize the aspnetuserroles table, the Client is our Tenant. This way we have already the table modified:
public class ApplicationUserRole : IdentityUserRole<int>
{
public int ClientId { get; set; }
public Client Client { get; set; }
public static void OnModelCreating(ModelBuilder modelBuilder)
{
var e = modelBuilder.Entity<ApplicationUserRole>();
e.HasKey(p => new { p.UserId, p.RoleId, p.ClientId });
e.HasOne(p => p.Client).WithMany().HasForeignKey(tr => tr.ClientId).HasConstraintName("FK_UserRole_Client").IsRequired().OnDelete(DeleteBehavior.Cascade);
}
}
UPDATE 2
We have been able to modify the UserManager so we can add roles using the TenantId, in our case the ClientId.
public class ApplicationUserManager : UserManager<ApplicationUser>
{
private readonly UserStore<ApplicationUser, ApplicationRole, DataContext, int, IdentityUserClaim<int>, ApplicationUserRole, IdentityUserLogin<int>, IdentityUserToken<int>, IdentityRoleClaim<int>> _store;
public ApplicationUserManager(
IUserStore<ApplicationUser> store,
IOptions<IdentityOptions> optionsAccessor,
IPasswordHasher<ApplicationUser> passwordHasher,
IEnumerable<IUserValidator<ApplicationUser>> userValidators,
IEnumerable<IPasswordValidator<ApplicationUser>> passwordValidators,
ILookupNormalizer keyNormalizer,
IdentityErrorDescriber errors,
IServiceProvider services,
ILogger<UserManager<ApplicationUser>> logger)
: base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
{
_store = (UserStore<ApplicationUser, ApplicationRole, DataContext, int, IdentityUserClaim<int>, ApplicationUserRole, IdentityUserLogin<int>, IdentityUserToken<int>, IdentityRoleClaim<int>>)store;
}
public virtual async Task<IdentityResult> AddToRoleByClientAsync(ApplicationUser user, string role, int clientId)
{
ThrowIfDisposed();
if (user == null)
throw new ArgumentNullException(nameof(user));
if (string.IsNullOrWhiteSpace(role))
throw new ArgumentNullException(nameof(role));
if (clientId == 0)
throw new ArgumentNullException(nameof(clientId));
var normalizedRole = NormalizeName(role);
var applicationRole = _store.Context.Roles.FirstOrDefault(p => p.NormalizedName == normalizedRole);
if (applicationRole == null)
return IdentityResult.Failed(ErrorDescriber.InvalidRoleName(normalizedRole));
if (await IsInRoleByRoleIdClientAsync(user, applicationRole.Id, clientId))
return IdentityResult.Failed(ErrorDescriber.UserAlreadyInRole(normalizedRole));
_store.Context.Set<ApplicationUserRole>().Add(new ApplicationUserRole { RoleId = applicationRole.Id, UserId = user.Id, ClientId = clientId });
return await UpdateUserAsync(user);
}
private async Task<bool> IsInRoleByRoleIdClientAsync(ApplicationUser user, int roleId, int clientId, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
if (user == null)
throw new ArgumentNullException(nameof(user));
if (roleId == 0)
throw new ArgumentNullException(nameof(roleId));
if (clientId == 0)
throw new ArgumentNullException(nameof(clientId));
var userRole = await _store.Context.Set<ApplicationUserRole>().FindAsync(new object[] { user.Id, roleId, clientId }, cancellationToken);
return userRole != null;
}
public async Task<bool> IsInRoleByClientAsync(ApplicationUser user, string role, int clientId)
{
ThrowIfDisposed();
if (user == null)
throw new ArgumentNullException(nameof(user));
if (string.IsNullOrWhiteSpace(role))
throw new ArgumentNullException(nameof(role));
if (clientId == 0)
throw new ArgumentNullException(nameof(clientId));
var normalizedRole = NormalizeName(role);
var applicationRole = _store.Context.Roles.FirstOrDefault(p => p.NormalizedName == normalizedRole);
if (applicationRole == null)
return false;
var userRole = await _store.Context.Set<ApplicationUserRole>().FindAsync(new object[] { user.Id, applicationRole.Id, clientId });
return userRole != null;
}
}
We still need to modify the [Authorize(Roles = "admin")] in order to check the roles of the current Tenant and not all of them. We are investigating this.

Admin lock or unlock account user in .Net Core

I am doing the management of a user's account when necessary I can Lock a user's account in case they violate it. Or can be unlocked if required. I got an error like this. Where am I wrong, I use .Net Core 5 to build my program. Error: "An unhandled exception occurred while processing the request.
NullReferenceException: Object reference not set to an instance of an object."
enter image description here
Interface
public bool LockUser(string email);
public bool UnlockUser(string email);
Repo
public bool LockUser(string email)
{
var userTask = _userManager.FindByEmailAsync(email);
userTask.Wait();
var user = userTask.Result;
var lockUserTask = _userManager.SetLockoutEnabledAsync(user, true);
lockUserTask.Wait();
var lockDateTask = _userManager.SetLockoutEndDateAsync(user, DateTimeOffset.Now);
lockDateTask.Wait();
return lockDateTask.Result.Succeeded && lockUserTask.Result.Succeeded;
}
Controller
public ActionResult LockUser(string email)
{
if (!_userRepository.LockUser(email))
{
throw new ArgumentException("Error");
}
return RedirectToAction("Index");
}
Please refer the following sample code, the UserRepository should like this, add the usermanager via the constructor parameter:
public interface IUserRepository
{
public bool LockUser(string email);
public bool UnlockUser(string email);
}
public class UserRepository : IUserRepository
{
private readonly UserManager<IdentityUser> _userManager;
public UserRepository(UserManager<IdentityUser> userManager)
{
_userManager = userManager;
}
public bool LockUser(string email)
{
var userTask = _userManager.FindByEmailAsync(email);
userTask.Wait();
var user = userTask.Result;
var lockUserTask = _userManager.SetLockoutEnabledAsync(user, true);
lockUserTask.Wait();
var lockDateTask = _userManager.SetLockoutEndDateAsync(user, DateTimeOffset.Now);
lockDateTask.Wait();
return lockDateTask.Result.Succeeded && lockUserTask.Result.Succeeded;
}
public bool UnlockUser(string email)
{
//...
throw new NotImplementedException();
}
}
Then, add the service to the service container:
public void ConfigureServices(IServiceCollection services)
{
//...
services.AddScoped<IUserRepository, UserRepository>();
services.AddControllersWithViews();
}
Then, in the MVC controller:
public class HomeController : Controller
{
private readonly IUserRepository _userRepository;
public HomeController(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public IActionResult Index(int id)
{
string email = "aa#hotmail.com";
if (!_userRepository.LockUser(email))
{
throw new ArgumentException("Error");
}
return View();
}
The debug screenshot like this:

How can I pass the authenticated User Id to the class library project in asp.net core via DI?

I have a NLayers application:
asp.net core mvc
asp.net web api
and some of my class libraries:
DataLayer
DomainClasses
Models
Services
here is my BaseService in ServicesLayer:
public abstract partial class BaseService
{
protected BaseService(AppDbContext dbContext
, UserManager<MyApplicationUser> userManager
, int authenticatedUserId)
{
DbContext = dbContext;
AuthenticatedUserId = authenticatedUserId;
MyUserManager = userManager;
Init();
}
public AppDbContext DbContext { get; }
protected UserManager<MyApplicationUser> MyUserManager;
public string AuthenticatedUserId { get; }
protected virtual void Init()
{
//
}
...
}
and one of my child service classes:
public class BookService :BaseService
{
public BookService(AppDbContext dbContext
, UserManager<MyApplicationUser> userManager
, int authenticatedUserId)
:base(dbContext,userManager, authenticatedUserId)
{
}
}
I want to access the authenticated user id (from Asp net core) in my services (class library). How can I pass it via DI or something else?
Updated based on #Frank's suggestion:
public class CommonServicesContainer
{
public AppDbContext DbContext { get; set; }
public AppUserManager UserManager { get; set; }
public int AuthenticatedUserId{ get; set; }
public CommonServicesContainer(AppDbContext appDbContext, AppUserManager userManager, string authenticatedUserId)
{
DbContext = dbContext;
UserManager = userManager;
AuthenticatedUserId = autheticatedUserId;
}
}
my startup:
services.AddScoped<AppDbContext>();
services.AddScoped<AppUserManager>();
services.AddScoped(x =>
{
var authenticatedUserId = x.GetRequiredService<IHttpContextAccessor>().HttpContext.User.Identity.Name;
return new CommonServicesContainer(x.GetRequiredService<AppDbContext>()
, x.GetRequiredService<AppUserManager>()
, authenticatedUserId);
});
AccountController :
private readonly CommonServicesContainer _commonServicesContainer;
public AccountController(CommonServicesContainer commonServicesContainer)
{
_commonServicesContainer = commonServicesContainer;
// ...
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginInputModel model)
{
// ...
if(ModelState.IsValid)
{
var isValid = await _usersService.AreCredentialsValidAsync(model.Username, model.Password);
if(isValid)
{
var foundUser = await _usersService.GetByUserNameAsync(model.Username);
await HttpContext.SignInAsync(
foundUser.SubjectId,
foundUser.UserName);
//_commonServicesContainer.AuthenticatedUserId = foundUser.Id;
// ...
}
// ...
}
You can do that by register a AuthenticatedUser type as a AddScoped.
class AuthenticatedUser {
public int? UserId {get;set;}
public bool IsAuthenticated => int.HasValue;
}
in Startup.cs of your AspNetCore project:
public IServiceProvider ConfigureServices(IServiceCollection services) {
...
services.AddScoped<AuthenticatedUser>();
...
}
Somewhere you do the authentication, you get the AuthenticatedUser and set the UserId.
Since AuthenticatedUser is added as scoped it acts as global (same instance) for the particular httprequest scope. So all .GetService<AuthenticatedUser> / .GetRequiredService<AuthenticatedUser> will have the same instance - within the same scope.
Each http-request has it is own scope, and thereby also their own AuthenticatedUser.
When the user is Authenticated, using AspNetCore Identity, you can find the AspNetUsers Id by:
if( httpContext.User.Identity.IsAuthenticated ) {
var userIdClaim = httpContext.User.Claims.SingleOrDefault(c => c.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier");
var aspNetUsersId = userIdClaim?.Value ?? 0;
}
This can be done as Middleware, then setting AuthenticatedUser.UserId.

How do I get the current logged in user ID in the ApplicationDbContext using Identity?

I have created a .net core 2.1 MVC application using the template in Visual Studio with the Identity preset (user accounts stored in the application) and I am trying to automate some auditing fields.
Basically what I'm trying to do is overriding the SaveChangesAsync() method so that whenever changes are made to an entity the current logged in user ID is set to the auditing property of CreatedBy or ModifiedBy properties that are created as shadow properties on the entity.
I have looked at what seems to be tons of answers and surprisingly none of them work for me. I have tried injecting IHttpContext, HttpContext, UserManager, and I either can't seem to access a method that returns the user ID or I get a circular dependency error which I don't quite understand why it is happening.
I'm really running desperate with this one. I think something like this should be really straightforward to do, but I'm having a real hard time figuring out how to do it. There seem to be well documented solutions for web api controllers or for MVC controllers but not for use inside the ApplicationDbContext.
If someone can help me or at least point me into the right direction I'd be really grateful, thanks.
Let's call it DbContextWithUserAuditing
public class DBContextWithUserAuditing : IdentityDbContext<ApplicationUser, ApplicationRole, string>
{
public string UserId { get; set; }
public int? TenantId { get; set; }
public DBContextWithUserAuditing(DbContextOptions<DBContextWithUserAuditing> options) : base(options) { }
// here we declare our db sets
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.NamesToSnakeCase(); // PostgreSQL
modelBuilder.EnableSoftDelete();
}
public override int SaveChanges()
{
ChangeTracker.DetectChanges();
ChangeTracker.ProcessModification(UserId);
ChangeTracker.ProcessDeletion(UserId);
ChangeTracker.ProcessCreation(UserId, TenantId);
return base.SaveChanges();
}
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken))
{
ChangeTracker.DetectChanges();
ChangeTracker.ProcessModification(UserId);
ChangeTracker.ProcessDeletion(UserId);
ChangeTracker.ProcessCreation(UserId, TenantId);
return (await base.SaveChangesAsync(true, cancellationToken));
}
}
Then you have request pipeline and what you need - is a filter hook where you set your UserID
public class AppInitializerFilter : IAsyncActionFilter
{
private DBContextWithUserAuditing _dbContext;
public AppInitializerFilter(
DBContextWithUserAuditing dbContext
)
{
_dbContext = dbContext;
}
public async Task OnActionExecutionAsync(
ActionExecutingContext context,
ActionExecutionDelegate next
)
{
string userId = null;
int? tenantId = null;
var claimsIdentity = (ClaimsIdentity)context.HttpContext.User.Identity;
var userIdClaim = claimsIdentity.Claims.SingleOrDefault(c => c.Type == ClaimTypes.NameIdentifier);
if (userIdClaim != null)
{
userId = userIdClaim.Value;
}
var tenantIdClaim = claimsIdentity.Claims.SingleOrDefault(c => c.Type == CustomClaims.TenantId);
if (tenantIdClaim != null)
{
tenantId = !string.IsNullOrEmpty(tenantIdClaim.Value) ? int.Parse(tenantIdClaim.Value) : (int?)null;
}
_dbContext.UserId = userId;
_dbContext.TenantId = tenantId;
var resultContext = await next();
}
}
You activate this filter in the following way (Startup.cs file)
services
.AddMvc(options =>
{
options.Filters.Add(typeof(OnRequestInit));
})
Your app is then able to automatically set UserID & TenantID to newly created records
public static class ChangeTrackerExtensions
{
public static void ProcessCreation(this ChangeTracker changeTracker, string userId, int? tenantId)
{
foreach (var item in changeTracker.Entries<IHasCreationTime>().Where(e => e.State == EntityState.Added))
{
item.Entity.CreationTime = DateTime.Now;
}
foreach (var item in changeTracker.Entries<IHasCreatorUserId>().Where(e => e.State == EntityState.Added))
{
item.Entity.CreatorUserId = userId;
}
foreach (var item in changeTracker.Entries<IMustHaveTenant>().Where(e => e.State == EntityState.Added))
{
if (tenantId.HasValue)
{
item.Entity.TenantId = tenantId.Value;
}
}
}
I wouldn't recommend injecting HttpContext, UserManager or anything into your DbContext class because this way you violate Single Responsibility Principle.
Thanks to all the answers. In the end I decided to create a UserResolveService that receives through DI the HttpContextAccessor and can then get the current user's name. With the name I can then query the database to get whatever information I may need. I then inject this service on the ApplicationDbContext.
IUserResolveService.cs
public interface IUserResolveService
{
Task<string> GetCurrentSessionUserId(IdentityDbContext dbContext);
}
UserResolveService.cs
public class UserResolveService : IUserResolveService
{
private readonly IHttpContextAccessor httpContextAccessor;
public UserResolveService(IHttpContextAccessor httpContextAccessor)
{
this.httpContextAccessor = httpContextAccessor;
}
public async Task<string> GetCurrentSessionUserId(IdentityDbContext dbContext)
{
var currentSessionUserEmail = httpContextAccessor.HttpContext.User.Identity.Name;
var user = await dbContext.Users
.SingleAsync(u => u.Email.Equals(currentSessionUserEmail));
return user.Id;
}
}
You have to register the service on startup and inject it on the ApplicationDbContext and you can use it like this:
ApplicationDbContext.cs
var dbContext = this;
var currentSessionUserId = await userResolveService.GetCurrentSessionUserId(dbContext);

DataContext.Users returns empty result in Razor pages

Hi I have a problem accessing Users table from a Razor page in ASP.NET Core
I Created an AppDataContext class that extends IdentityDbContext<Models.User, Models.UserRole, string>
I can use it with other controller classes and services without problems. But when I start working on razor pages the dataContext.Users always return empty enumerable. Other DbSets still working properly, only the Users not work.
This also happens when I try to access data from other services like UserManager.Users or SigniInManager.UserManager.Users
Here's some part of my files
AppDataContext
public class AppDataContext : IdentityDbContext<Models.User, Models.UserRole, string>
{
// Other DbSet's
public AppDataContext(DbContextOptions<AppDataContext> options) : base(options) { }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Other entities building
MapIdentityTables(builder);
}
private void MapIdentityTables(ModelBuilder builder)
{
builder.Entity<Models.User>().ToTable("Users");
builder.Entity<IdentityUser>().ToTable("Users");
builder.Entity<Models.UserRole>().ToTable("UserRoles");
builder.Entity<IdentityRole>().ToTable("UserRoles");
builder.Entity<IdentityUserClaim<string>>().ToTable("UserClaims");
builder.Entity<IdentityUserRole<string>>().ToTable("UserUserRoles");
builder.Entity<IdentityUserLogin<string>>().ToTable("UserLogins");
builder.Entity<IdentityRoleClaim<string>>().ToTable("UserRoleClaims");
builder.Entity<IdentityUserToken<string>>().ToTable("UserTokens");
}
LoginPage.cshtml.cs
public class LoginModel : PageModel
{
private readonly Identity.AppUserManager userManager;
private readonly SignInManager<Models.User> signInManager;
private readonly Data.AppDataContext dataContext;
public LoginModel(Identity.AppUserManager userManager, SignInManager<Models.User> signInManager, Data.AppDataContext dataContext)
{
this.userManager = userManager;
this.signInManager = signInManager;
this.dataContext = dataContext;
}
public IList<Model.User> Users { get; private set; }
public void OnGet()
{
Users = userManager.Users.ToList(); // Empty
Users = signInManager.UserManager.Users.ToList(); // Empty
Users = dataContext.Users.ToList(); // Empty
}
}
User class
public class User : Microsoft.AspNetCore.Identity.IdentityUser
{
public ICollection<UserDevice> Devices { get; set; }
public IList<UserPassword> Passwords { get; set; }
}
Have I done anything wrong or am I missing something?
UPDATE
The problem is gone somehow now after I gave up and do something else. But it's not a solution since the original problem still there if I did the same.
What I have done was to revert all my changes and added AddSecondIdentity from this SO answer. Created StaffUser : IdentityUser and StaffUserManager<StaffUser, UserRole> (same UserRole as the original UserManager) to handle those new IdentityUser objects.
Then I just use StaffUserManager and SignInManager<StaffUser> instead of AppUserManager and SignInManager<User> in Login.cshtml.cs
public LoginModel(StaffUserManager userManager, SignInManager<Models.StaffUser> signInManager, Data.AppDataContext context)
{
var users = context.Users.ToList() // 1 user
}
Which now confuses me further. But I don't have time for this now. I think it has something to do with the Discriminator part of the database since the returned user is the one with StaffUser discriminator value but there are some others without the discriminator that are not returned.
Make sure you add services.AddIdentity
services
.AddIdentity<User, ApplicationRole>(options =>
{
options.Password.RequireDigit = false;
options.Password.RequiredLength = 4;
options.Password.RequireLowercase = false;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
//lock out attempt
options.Lockout.AllowedForNewUsers = true;
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
options.Lockout.MaxFailedAccessAttempts = 3;
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
If this doesn't work please provide me your source code so I can take a look on it. Perhaps your github.