5

I am new to asp.net core. So this might not be a good question.

I have 3 classes: Admin, Doctor, Patient.

I want all those users to be able to login and see their dashboard. All of them will have different level of access.

So I derived these classes from IdenetityUser like this:

public class Admin : IdentityUser
{
}

public class Doctor : IdentityUser
{
    public const int MaxAppointPerDay = 28;

    public Gender Gender { get; set; }
}

public class Patient : IdentityUser
{
    public DateTime DateOfBirth { get; set; }
    public Gender Gender { get; set; }
}

I am just stuck after this.

When I tried to add multiple Identitiy by the method AddIdentity<TUser, TRole>(), it gave me error.

What do I do next?

Most of the examples in the internet involves one IdentityUser and multiple IdentityRole. But since the user models are all different, I couldn't go that way.

Thank you.

Muzib
  • 2,412
  • 3
  • 21
  • 32
  • You can extend your base identitymodel with multiple properties and manage access over the roles. I can recommend you, not create multiple tables for manage your users. This will make a lot of work for you and you can not using standard implementations. What will happen, if anyone would like to have a accesslevel for a nurse? – David Stania Oct 15 '20 at 11:14
  • You cannot repeatedly use `AddIdentity` to add an identity. ASP.NET Core provides a built-in method:`AddIdentityCore` You can use it like this: `services.AddIdentityCore();` – Yinqiu Oct 16 '20 at 02:45
  • @Yinqiu If I use `AddIdentityCore`, would I be able to add more than one Identity? – Muzib Oct 16 '20 at 04:23
  • Yes,you can try it. – Yinqiu Oct 16 '20 at 04:59
  • Hi @Muzib,any updates about this case? – Yinqiu Oct 19 '20 at 00:24
  • @Yinqiu sorry for the late reply. Yes it works. – Muzib Oct 19 '20 at 05:15
  • What it does is, it merges all the properties of all the added identities into one `AspNetUsers` table. – Muzib Oct 19 '20 at 05:16
  • of course, do it – Muzib Oct 19 '20 at 05:21
  • If this solves your problem, you can mark it as an answer, which will help people who encounter the same problem in the future.Thank you. – Yinqiu Oct 19 '20 at 05:36

2 Answers2

5

You cannot repeatedly use AddIdentity to add an identity.

ASP.NET Core provides a built-in method:AddIdentityCore<TUser>.

You can use it like this: services.AddIdentityCore<Admin>();

Edit:

Adding to Yinqiu's answer, for the future readers, if you use this AddIdentityCore method, the AspNetUsers table will merge all the properties of each IdentityUser. Here is an example:

Let Teacher and Student are two IdentityUser and both of them have at least one distinct properties, like this:

public class Teacher : IdentityUser
{
    public string Subject { get; set; }
}

public class Student : IdentityUser
{
    public int Grade { get; set; }
}

Then I add both of their Identity in the startup.cs like this:

services.AddIdentityCore<Teacher>().AddEntityFrameworkStores<AppDbContext>();
services.AddIdentityCore<Student>().AddEntityFrameworkStores<AppDbContext>();

The AspNetUsers table will include both the Subject property and the Grade property. Here is the proof from migration class:

migrationBuilder.CreateTable(
    name: "AspNetUsers",
    columns: table => new
    {
        Id = table.Column<string>(nullable: false),
        UserName = table.Column<string>(maxLength: 256, nullable: true),
        NormalizedUserName = table.Column<string>(maxLength: 256, nullable: true),
        Email = table.Column<string>(maxLength: 256, nullable: true),
        NormalizedEmail = table.Column<string>(maxLength: 256, nullable: true),
        EmailConfirmed = table.Column<bool>(nullable: false),
        PasswordHash = table.Column<string>(nullable: true),
        SecurityStamp = table.Column<string>(nullable: true),
        ConcurrencyStamp = table.Column<string>(nullable: true),
        PhoneNumber = table.Column<string>(nullable: true),
        PhoneNumberConfirmed = table.Column<bool>(nullable: false),
        TwoFactorEnabled = table.Column<bool>(nullable: false),
        LockoutEnd = table.Column<DateTimeOffset>(nullable: true),
        LockoutEnabled = table.Column<bool>(nullable: false),
        AccessFailedCount = table.Column<int>(nullable: false),
        Discriminator = table.Column<string>(nullable: false),
        Grade = table.Column<int>(nullable: true),
        Subject = table.Column<string>(nullable: true)
    },
    constraints: table =>
    {
        table.PrimaryKey("PK_AspNetUsers", x => x.Id);
    });

Notice the last two properties are Grade and Subject.

It's similar to using a base class for all the IdentitiyUser which will derive from IdentityUser and include combination of all propeties and then just adding that one base class using AddIdentity<TUser, TRole> method.

Muzib
  • 2,412
  • 3
  • 21
  • 32
Yinqiu
  • 6,609
  • 1
  • 6
  • 14
  • what to do if you want to use asp net cores built in GenerateEmailConfirmationTokenAsync for example but you haven't registered the token provider and cant use defaulttokenproviders for both classes? – Noah Bergh Feb 20 '23 at 14:39
0

You can do it like has implemented in Active Directory, instead of creating a new table(entity) for each Access level group you can create only one where you will store Accees level groups Admin , Doctor , Patient , and so on. And include IdenetityUser to any group ... Of course, each group should have some roles.

For example - model:

    public class BaseGuidIdTable
{
    [Key]
    public Guid Id { get; set; }
}
    public class SecurityGroupRole: BaseGuidIdTable
{

    public string Role { get; set; }

    public string Description { get; set; }

}
    public class SecurityRight: BaseGuidIdTable
{

    public string Right { get; set; }

    public string Description { get; set; }
}

    public class SecurityGroupAccess: BaseGuidIdTable
{

    public Guid GroupId { get; set; }

    [ForeignKey(nameof(GroupId))]
    public SecurityGroupRole GroupRole { get; set; }

    public Guid RightId { get; set; }

    [ForeignKey(nameof(RightId))]
    public SecurityRight Right { get; set; }
}

Add some data :

            builder.Entity<SecurityGroupRole>().HasData(
            new SecurityGroupRole
            {
                Id = new Guid(OwnerGuidTxt),
                Role = "Doctor",
                Description = "Doctor"
            },
            new SecurityGroupRole
            {
                Id = new Guid(AdminGuidTxt),
                Role = "Admin",
                Description = "Admin"
            },
            new SecurityGroupRole
            {
                Id = new Guid(UserGuidTxt),
                Role = "Patient",
                Description = "Patient"
            }

        );
            builder.Entity<SecurityRight>().HasData(
            new SecurityRight
            {
                Id = new Guid(AddDeleteNewUsers),
                Right = "AddDeleteNewUsers",
                Description = "Add or delete Users"
            },
            new SecurityRight
            {
                Id = new Guid(PasswordChangeYourself),
                Right = "PasswordChangeYourself",
                Description = "Can password change by yourself"
            },
            new SecurityRight
            {
                Id = new Guid(ViewAll),
                Right = "ViewAll",
                Description = "Can view all objects"
            },
            new SecurityRight
            {
                Id = new Guid(CanChangeTags),
                Right = "CanChangeTags",
                Description = "Can change tags"
            } 
        );
                new SecurityGroupAccess {Id = new Guid("DD4C2CC3-65E2-4DD1-A620-723B5ADB8758"), GroupId = ownerGroupGuid, RightId = new Guid(AddDeleteNewUsers) },
            new SecurityGroupAccess { Id = new Guid("23CD8B4E-A572-4335-B1EF-2EF115E14947"), GroupId = ownerGroupGuid, RightId = new Guid(PasswordChangeYourself) },
            new SecurityGroupAccess { Id = new Guid("6A6A3A41-1103-46BD-B482-AB59903172D9"), GroupId = ownerGroupGuid, RightId = new Guid(ViewAll) },
            new SecurityGroupAccess { Id = new Guid("EB133F40-AB3B-4094-9AE7-EF6FD853F36B"), GroupId = ownerGroupGuid, RightId = new Guid(CanChangeTags) },

            new SecurityGroupAccess { Id = new Guid("29EE3EDA-08ED-46F1-9EF7-79A2D4021E86"), GroupId = adminGroupGuid, RightId = new Guid(PasswordChangeYourself) },
            new SecurityGroupAccess { Id = new Guid("59309220-F49B-4E30-B265-CB2ED71867B0"), GroupId = adminGroupGuid, RightId = new Guid(ViewAll) },
            new SecurityGroupAccess { Id = new Guid("F2F3FDF3-1ABC-46FF-BB62-E19B3F48E0AC"), GroupId = adminGroupGuid, RightId = new Guid(CanChangeTags) },

            new SecurityGroupAccess { Id = new Guid("7AC0F6A1-A585-40D6-B09B-DD6B86772935"), GroupId = userGroupGuid, RightId = new Guid(PasswordChangeYourself) }

So - in example we add 3 SecurityGroupRole Admin , Doctor , Patient, and give each some right's, Patient can change password for himself for example ( excuse me - this is from other project - so naming can looking wrong)

Aleksandr Zolotov
  • 1,078
  • 3
  • 19
  • 32
  • I think the `IdentityRole` can do the work that you suggested. – Muzib Oct 16 '20 at 04:25
  • @Muzib - yes - as described here https://learn.microsoft.com/en-us/aspnet/core/security/authentication/customize-identity-model?view=aspnetcore-2.2#default-model-configuration – Aleksandr Zolotov Oct 16 '20 at 10:31