Showing posts with label IdentityRole. Show all posts
Showing posts with label IdentityRole. Show all posts

Sunday, October 1, 2023

Seed Users and Roles using EF Code First approach in ASP.NET Razor Pages

In this tutorial, I shall describe the steps you need to follow if you want to use Code First migration to seed both users and roles data. The seeding will be done inside the OnModelCreating() method of the Entity Framework DbContext class. To keep things simple, we will use SQLite.

In order to proceed with this tutorial you need to have the following prerequisites:

  • VS Code
  • You have installed .NET 8.0
  • You have installed the dotnet-ef tool

Getting Started

In a terminal window, execute the following command to create an ASP.NET Razor Pages application that supports database authentication using the lightweight SQLite database:

dotnet new razor --auth individual -f net8.0 -o Code1stUsersRoles

Change directory to the newly created folder then run the application:

cd Code1stUsersRoles
dotnet watch

Click on the Register link on the top-right side of your keyboard to add a new user. 


When you click on the Register button, you will receive a page that looks like this:


Click on the link “Click here to confirm your account” to simulate email confirmation. Thereafter, login with the newly created account email and password.

Click on Logout in the top-right corner.

Open the application folder in VS Code.

Create a class named SeedUsersRoles in the Data folder of your application. This will contain seed data for roles, users, and information about users that belong to roles. Below is the code for the SeedUsersRoles class:

public class SeedUsersRoles {
    private readonly List<IdentityRole> _roles;
    private readonly List<IdentityUser> _users;
    private readonly List<IdentityUserRole<string>> _userRoles; 
 
    public SeedUsersRoles() {
      _roles = GetRoles();
      _users = GetUsers();
      _userRoles = GetUserRoles(_users, _roles);
    } 
    public List<IdentityRole> Roles { get { return _roles; } }
    public List<IdentityUser> Users { get { return _users; } }
    public List<IdentityUserRole<string>> UserRoles { get { return _userRoles; } }
    private List<IdentityRole> GetRoles() {
      // Seed Roles
      var adminRole = new IdentityRole("Admin");
      adminRole.NormalizedName = adminRole.Name!.ToUpper();
      var memberRole = new IdentityRole("Member");
      memberRole.NormalizedName = memberRole.Name!.ToUpper();
      List<IdentityRole> roles = new List<IdentityRole>() {
adminRole,
memberRole
      };
      return roles;
    }
    private List<IdentityUser> GetUsers() {
      string pwd = "P@$$w0rd";
      var passwordHasher = new PasswordHasher<IdentityUser>();
      // Seed Users
      var adminUser = new IdentityUser {
        UserName = "aa@aa.aa",
        Email = "aa@aa.aa",
        EmailConfirmed = true,
      };
      adminUser.NormalizedUserName = adminUser.UserName.ToUpper();
      adminUser.NormalizedEmail = adminUser.Email.ToUpper();
      adminUser.PasswordHash = passwordHasher.HashPassword(adminUser, pwd);
      var memberUser = new IdentityUser {
        UserName = "mm@mm.mm",
        Email = "mm@mm.mm",
        EmailConfirmed = true,
      };
      memberUser.NormalizedUserName = memberUser.UserName.ToUpper();
      memberUser.NormalizedEmail = memberUser.Email.ToUpper();
      memberUser.PasswordHash = passwordHasher.HashPassword(memberUser, pwd);
      List<IdentityUser> users = new List<IdentityUser>() {
adminUser,
memberUser,
      };
      return users;
    }
    private List<IdentityUserRole<string>> GetUserRoles(List<IdentityUser> users, List<IdentityRole> roles) {
      // Seed UserRoles
      List<IdentityUserRole<string>> userRoles = new List<IdentityUserRole<string>>();
      userRoles.Add(new IdentityUserRole<string> {
        UserId = users[0].Id,
        RoleId = roles.First(q => q.Name == "Admin").Id
      });
      userRoles.Add(new IdentityUserRole<string> {
        UserId = users[1].Id,
        RoleId = roles.First(q => q.Name == "Member").Id
      });
      return userRoles;
    }
}

Open Data/ApplicationDbContext.cs in your editor. Add the following OnModelCreating() method to the class:

protected override void OnModelCreating(ModelBuilder builder) {
  base.OnModelCreating(builder);
  // Use seed method here
  SeedUsersRoles seedUsersRoles = new();
  builder.Entity<IdentityRole>().HasData(seedUsersRoles.Roles);
  builder.Entity<IdentityUser>().HasData(seedUsersRoles.Users);
  builder.Entity<IdentityUserRole<string>>().HasData(seedUsersRoles.UserRoles);
} 
 
In the Program.cs class, replace the call to builder.Services.AddDefaultIdentity statement so that it registers IdentityRole. Replace the entire builder.Services.AddDefaultIdentity statement with the following code:

builder.Services.AddIdentity<IdentityUser, IdentityRole>(
options => {
    options.Stores.MaxLengthForKeys = 128;
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddRoles<IdentityRole>()
.AddDefaultUI()
.AddDefaultTokenProviders();

Delete the Data/Migrations folder and the app.db file because we will add new migrations. Thereafter, type the following from a terminal window inside of the root folder of your application:

dotnet ef migrations add M1 -o Data/Migrations

We will apply migrations and update the database with the following command:

dotnet ef database update

Now start the application. To prove that user and role data are successfully seeded, login with one of the below credentials that were previously seeded:

Email Password Role
aa@aa.aa P@$$w0rd Admin
mm@mm.mm P@$$w0rd Member

Add the following above the IndexModel class in Index.cshtml.cs to only allow users that belong to the Member role:

[Authorize (Roles="Member")]

Also, add the following above the PrivacyModel class in Privacy.cshtml.cs to only allow users that belong to the Admin role:

[Authorize (Roles="Admin")]

Only mm@mm.mm is allowed into the / home page and aa@aa.aa is allowed in the /privacy page.

We have succeeded in seeding user and role. Happy Coding.

Seeding Users and Roles in ASP.NET Razor Pages with RoleManager & UserManager

In this tutorial, I shall describe the steps you need to follow if you want seed both users and roles data. We will also look at how you can go about securing an ASP.NET Razor Pages application by user and by roles. To keep things simple, we will use the SQLite database.

To proceed with this tutorial, you need to have the following prerequisites:

  • VS Code
  • You have installed .NET 8.0

Getting Started

In a terminal window, execute the following command to create an ASP.NET Razor Pages application that supports database authentication using the lightweight SQLite database:

dotnet new razor --auth individual -f net8.0 -o SeedIdentity

Change directory to the newly created folder then run the application:

cd SeedIdentity

dotnet watch

Click on the Register link on the top-right side of your keyboard to add a new user. 




When you click on the Register button, you will receive a page that looks like this:



Click on the link “Click here to confirm your account” to simulate email confirmation. Thereafter, login with the newly created account email and password.


Click on Logout in the top-right corner.

Open the application folder in VS Code.

Let us create some sample data for roles and users. Create class named IdentitySeedData in the Data folder, and then add to it the following method: 

public class IdentitySeedData {
    public static async Task Initialize(ApplicationDbContext context,
        UserManager<IdentityUser> userManager,
        RoleManager<IdentityRole> roleManager) {
        context.Database.EnsureCreated();

        string asdminRole = "Admin";
        string memberRole = "Member";
        string password4all = "P@$$w0rd";

        if (await roleManager.FindByNameAsync(asdminRole) == null) {
            await roleManager.CreateAsync(new IdentityRole(asdminRole));
        }

        if (await roleManager.FindByNameAsync(memberRole) == null) {
            await roleManager.CreateAsync(new IdentityRole(memberRole));
        }

        if (await userManager.FindByNameAsync("aa@aa.aa") == null){
            var user = new IdentityUser {
                UserName = "aa@aa.aa",
                Email = "aa@aa.aa",
                PhoneNumber = "6902341234"
            };

            var result = await userManager.CreateAsync(user);
            if (result.Succeeded) {
                await userManager.AddPasswordAsync(user, password4all);
                await userManager.AddToRoleAsync(user, asdminRole);
            }
        }

        if (await userManager.FindByNameAsync("bb@bb.bb") == null) {
            var user = new IdentityUser {
                UserName = "bb@bb.bb",
                Email = "bb@bb.bb",
                PhoneNumber = "7788951456"
            };

            var result = await userManager.CreateAsync(user);
            if (result.Succeeded) {
                await userManager.AddPasswordAsync(user, password4all);
                await userManager.AddToRoleAsync(user, asdminRole);
            }
        }

        if (await userManager.FindByNameAsync("mm@mm.mm") == null) {
            var user = new IdentityUser {
                UserName = "mm@mm.mm",
                Email = "mm@mm.mm",
                PhoneNumber = "6572136821"
            };

            var result = await userManager.CreateAsync(user);
            if (result.Succeeded) {
                await userManager.AddPasswordAsync(user, password4all);
                await userManager.AddToRoleAsync(user, memberRole);
            }
        }

        if (await userManager.FindByNameAsync("dd@dd.dd") == null) {
            var user = new IdentityUser {
                UserName = "dd@dd.dd",
                Email = "dd@dd.dd",
                PhoneNumber = "6041234567"
            };

            var result = await userManager.CreateAsync(user);
            if (result.Succeeded) {
                await userManager.AddPasswordAsync(user, password4all);
                await userManager.AddToRoleAsync(user, memberRole);
            }
        }
    }
}

In the Program.cs class, replace the call to builder.Services.AddDefaultIdentity statement so that it uses the new IdentityUser & IdentityRole. Replace the entire statement with the following code:

builder.Services.AddIdentity<IdentityUser, IdentityRole>(
options => {
    options.Stores.MaxLengthForKeys = 128;
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddRoles<IdentityRole>()
.AddDefaultUI()
.AddDefaultTokenProviders();

We need to call the Initialize() method in the IdentitySeedData class from Program.cs so that when the application starts, the users are seeded. In Program.cs, just before “app.Run();” at the bottom, add this code:

using (var scope = app.Services.CreateScope()) {
    var services = scope.ServiceProvider;

    var context = services.GetRequiredService<ApplicationDbContext>();    
    context.Database.Migrate();

    var userMgr = services.GetRequiredService<UserManager<IdentityUser>>();  
    var roleMgr = services.GetRequiredService<RoleManager<IdentityRole>>();  

    IdentitySeedData.Initialize(context, userMgr, roleMgr).Wait();
}

In the above code the following takes place:
  • Instances of ApplicationDbContext, UserManager<IdentityUser> & RoleManager<IdentityRole> are obtained 
  • If there are any outstanding migrations, they are automatically executed
  • The IdentitySeedData.Initialize() method is called
At this stage, all the database tables are created. However, data is not yet seeded. Let us run our application so that the sample roles and users are seeded in the database. Make sure the application is running. Logout, if you are already logged in.


To prove that user and role data are successfully seeded, login with one of the below credentials that were previously seeded:

Email Password Role
aa@aa.aa P@$$w0rd Admin
mm@mm.mm P@$$w0rd Member

We have succeeded in seeding user and role. Happy Coding.