Here I'm going to post whatever that is interesting for me. All things about programming using JavaScript and ASP.NET Core. And something else.

A blog about ASP.Net Core and JavaScript

There are a lot of interesting articles, tutorials and blog posts which describe authorization with roles and policies. Simple googling will help you to find among them, for example:

  • Official ASP.NET Core documentation tutorial.
  • Temi Lajumoke’s article about using custom user roles for ASP.NET Core.
  • Juan Carlos Sanchez’s post.

But even after you'll read all of them, it's hard to get answers to the few questions:

  • How to seed initial records of the users and roles to the database?
  • How to restrict access to the page menu items for those users, who belongs only to special roles?
  • And how to assign the new user to the default role?

 

Well documented things

It is quite easy to find the answer how to add a policy, based on existing roles. For example, if you have "Admin" role specified, then you can use it to register "RequireAdminRole" policy. Normally this occurs in the ConfigureServices() method in the Startup.cs file.

public void ConfigureServices(IServiceCollection services)
{
    //...
    services.AddMvc();
    services.AddAuthorization(options =>
    {
        options.AddPolicy("RequireAdminRole",
            policy => policy.RequireRole("Admin"));
    });
}

Policies can be applied using the Policy property on the AuthorizeAttribute attribute:

[Authorize(Policy = "RequireAdminRole")]
public class AdminController : Controller
{
    public ActionResult Index()
    {
        ViewData["PageID"] = "Admin";
        ViewData["Message"] = "Admin page restricted to the super admin user";
        return View();
    }
    public IActionResult Settings()
    {
        ViewData["Message"] = "Settings page restricted to the super admin user";
        return View();
    }
}

It is very useful things, but how to add the users and roles to the application database?

Seeding roles and admin user to the database

Temi Lajumoke in his article suggests creating roles and admin user in the Configure() method in the Startup.cs file.

But Tom Dykstra and Rick Anderson in their Getting started with ASP.NET Core MVC and Entity Framework Core tutorial recommend using the Configure() method to only set up the request pipeline. Application startup code should belong to the Main() method.

Therefore, what you can do is to create DbInitializer class in the DbInitializer.cs and to add InitializeAsync() method.

public static class DbInitializer
    {
    public static async Task InitializeAsync(ApplicationDbContext context,
IServiceProvider serviceProvider) { context.Database.EnsureCreated(); var RoleManager = serviceProvider
.GetRequiredService<RoleManager<IdentityRole>>(); string[] roleNames = { "Admin", "Member" }; IdentityResult roleResult; foreach (var roleName in roleNames) { var roleExist = await RoleManager.RoleExistsAsync(roleName); if (!roleExist) { roleResult = await RoleManager.CreateAsync(new IdentityRole(roleName)); } } var config = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json") .Build(); var userManager = serviceProvider
.GetRequiredService<UserManager<ApplicationUser>>(); var _user = await userManager
.FindByEmailAsync(config.GetSection("AppSettings")["UserEmail"]); if (_user == null) { var poweruser = new ApplicationUser { UserName = config.GetSection("AppSettings")["UserEmail"], Email = config.GetSection("AppSettings")["UserEmail"] }; string UserPassword = config.GetSection("AppSettings")["UserPassword"]; var createPowerUser = await userManager.CreateAsync(poweruser, UserPassword); if (createPowerUser.Succeeded) { await userManager.AddToRoleAsync(poweruser, "Admin"); } } } }

The code above is very similar to Temi Lajumoke’s example code. Here you can create the roles and the power user you need.

Then you should simply call DbInitializer.InitializeAsync() in the Main() method:

public static void Main(string[] args)
{
    var host = BuildWebHost(args);
    using (var scope = host.Services.CreateScope())
    {
        var services = scope.ServiceProvider;
        try
        {
            var context = services.GetRequiredService<ApplicationDbContext>();
            DbInitializer.InitializeAsync(context, services).Wait();
        }
        catch (Exception ex)
        {
            var logger = services.GetRequiredService<ILogger<Program>>();
            logger.LogError(ex, "An error occurred while seeding the database.");
        }
    }
    host.Run();
}

Adding users to the default role

If you want to add the user to the default role, the good place to do this is the Register() method of the AccountController. Nothing fancy.

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult>
                Register(RegisterViewModel model, string returnUrl = null)
{
    ViewData["ReturnUrl"] = returnUrl;
    if (ModelState.IsValid)
    {
        var user = new ApplicationUser {
                        UserName = model.Email,
                        Email = model.Email
                        };
        var result = await _userManager.CreateAsync(user, model.Password);
        if (result.Succeeded)
        {
            // Adding user to default "Member" role
            await _userManager.AddToRoleAsync(user, "Member");
            //... Other code here
        }
        AddErrors(result);
    }
    // If we got this far, something failed, redisplay form
    return View(model);
}

Restricting access to the page menu items

My way to do this is the next. You can create a new partial page layout called, for example, _AdminPartial.cshtml and put the next code inside it.

@using Microsoft.AspNetCore.Identity
@using AuthWithRoles.Models
@inject SignInManager<ApplicationUser> SignInManager
// is the current user authenticated?
@if (SignInManager.IsSignedIn(User))
{
    // is the current user authorized to see the next part of the page?
    if (User.IsInRole("Admin"))
    {
        <li><a asp-area="" asp-controller="Admin" asp-action="Index">Admin</a></li>
    }
}

The last thing to do is to inject a partial page to the main page layout. Obviously, the best place to perform this is the main menu declaration in _Layout.cshtml.

<ul class="nav navbar-nav">
    <li><a asp-area="" asp-controller="Home" asp-action="Index">Home</a></li>
    <li><a asp-area="" asp-controller="Home" asp-action="About">About</a></li>
    <li><a asp-area="" asp-controller="Home" asp-action="Contact">Contact</a></li>
    @*Adding administration menu*@
    @await Html.PartialAsync("_AdminPartial")
</ul>

Hope this post will be useful for someone.

Check and try by yourself

The whole code solution with all the code above can be found on GitHub inside of the AuthWithRoles repository.

Sources

  1. Role based Authorization”.
  2. Custom user roles and role-based authorization in ASP.NET core”.
  3. ASP.NET Core 1 – Authorization using Policies”.