Welcome to today’s post.
In this post I will be showing how to manage user roles within a custom identity provider API using .NET Core.
In a previous post I have shown how to implement a basic identity service using ASP.NET Core Identity API. In that post I showed how to register and authenticate user accounts. In security frameworks that utilize role-based authorization, there is a need to provide security administrators with a set of URIs that allow roles to be listed, added, and removed from a user account.
To keep this post brief I will go over some API URLs that would be useful for role management and show how these are implemented and tested.
Here are a list of useful API methods we will use for role management:
api/ManageUser/UserList
api/ManageUser/RoleList
api/ManageUser/AddRole
api/ManageUser/DeleteRole
api/ManageUser/GetUserRoles
Testing API Role Management URIs
With the above API we run the following HTTP GET URI requests and get the results as shown.
To obtain a list of all users use the following HTTP GET URI:
HTTP GET:
http://localhost/BookLoan.Identity.API/api/ManageUser/UserList
[
{
"id": "cc85629b-4cf1-4238-8134-6c5683c5ced7",
"userName": "test@bookloan.com",
"email": "test@bookloan.com",
"firstName": null,
"lastName": null,
"dob": "0001-01-01T00:00:00",
"password": null
}
]
To obtain a list of all available roles use the following HTTP GET URI:
HTTP GET: http://localhost/BookLoan.Identity.API/api/ManageUser/RoleList
[
"Admin",
"Member",
"Manager"
]
To add a role to a specific user account, we specify the login name and selected role that is to be added with the following HTTP POST URI:
HTTP POST: http://localhost/BookLoan.Identity.API/api/ManageUser/AddRole
{
"loginName": "test@bookloan.com",
"SelectedRole": "Admin"
}
To remove a role from a specific user account, we specify the login name and selected role that is to be removed with the following HTTP POST URI:
HTTP POST: http://localhost/BookLoan.Identity.API/api/ManageUser/DeleteRole
{
"loginName": "test@bookloan.com",
"SelectedRole": "Member"
}
To display the roles that are attached to a given account, we specify the login name in the URL path in the HTTP GET URI as shown:
HTTP GET: http://localhost/BookLoan.Identity.API/api/ManageUser/GetUserRoles/
admin@bookloan.com
[
"Admin",
"Member",
"Manager"
]
The above API can be tested using POSTMAN.
Implementation of API Role Management URIs
For the API api/ManageUser/UserList the implementation is shown below:
API Controller
// GET: ManageUsers/UserList
[HttpGet("api/[controller]/UserList")]
public async Task<ActionResult> UserList()
{
return Ok(await _userRoleService.GetUsers());
}
Role Service
public async Task<List<BookLoan.Models.ManageUserViewModels.UserViewModel>> GetUsers()
{
List<BookLoan.Models.ManageUserViewModels.UserViewModel>
userViews = new List<Models.ManageUserViewModels.UserViewModel>();
userManager.Users.ToList().ForEach(async u =>
{
Task<bool> isInRoleResult =
userManager.IsInRoleAsync(u, "Admin");
bool isInRole = isInRoleResult.GetAwaiter().GetResult();
if (isInRole == false)
userViews.Add(
new Models.ManageUserViewModels.UserViewModel()
{
ID = u.Id,
UserName = u.UserName,
Email = u.Email
}
);
});
return userViews;
}
We can test the UserList() API method Running in POSTMAN:
For the AddRole() and DeleteRole() methods, the implementations are below:
API Controller
[HttpPost("api/[controller]/AddRole")]
public async Task<ActionResult> AddRole(
[FromBody]Models.ManageUserViewModels.UserRoleConfirmAction model)
{
try
{
await _userRoleService.AddUserToRole(
model.LoginName,
model.SelectedRole);
return Ok();
}
catch
{
return BadRequest();
}
}
[HttpPost("api/[controller]/DeleteRole")]
public async Task<ActionResult> DeleteRole([FromBody]
Models.ManageUserViewModels.UserRoleConfirmAction model)
{
try
{
await _userRoleService.DeleteUserFromRole(
model.LoginName,
model.SelectedRole);
return Ok();
}
catch
{
return BadRequest();
}
}
Role Service
In the role service we use the RoleStore, UserManager and RoleManager to retrieve user account data and update role details to the UserRoles table.
public async Task AddUserToRole(string userName, string role)
{
var roleManager = new RoleStore<IdentityRole>(db);
var user = await userManager.FindByEmailAsync(userName);
bool isInRole = await this.IsUserInRole(userName, role);
if (!isInRole)
{
if ((role == "Member") || (role == "Manager"))
{
var memberRole = roleManager.FindByNameAsync(role);
var memberUser = userManager.FindByEmailAsync(userName);
if (memberRole != null && memberUser != null)
{
db.UserRoles.Add(new IdentityUserRole<string>()
{
RoleId = memberRole.Result.Id.ToString(),
UserId = memberUser.Result.Id.ToString()
});
await db.SaveChangesAsync();
}
}
}
}
In the removal of role from user account we utilize the identity API to remove a role from the UserRole table.
public async Task DeleteUserFromRole(string userName, string role)
{
var roleManager = new RoleStore<IdentityRole>(db);
var user = await userManager.FindByEmailAsync(userName);
bool isInRole = await this.IsUserInRole(userName, role);
if (isInRole)
{
if ((role == "Member") || (role == "Manager"))
{
var memberRole = roleManager.FindByNameAsync(role);
var memberUser = userManager.FindByEmailAsync(userName);
if (memberRole != null && memberUser != null)
{
var currUserRole = db.UserRoles.Where(
r => r.UserId == memberUser.Result.Id &&
r.RoleId ==
memberRole.Result.Id).SingleOrDefault();
if (currUserRole != null)
{
db.UserRoles.Remove(currUserRole);
await db.SaveChangesAsync();
}
}
}
}
}
For the GetUserRoles() API method, the implementation is shown:
API Controller
[HttpGet("api/[controller]/GetUserRoles/{userName}")]
public async Task<ActionResult> GetUserRoles(string userName)
{
List<string> userRoles = new List<string>();
try
{
userRoles = await _userRoleService.GetUserRoles(userName);
return Ok(userRoles);
}
catch
{
return BadRequest();
}
}
Role Service
When retrieving roles, we utilize the ASP.NET identity API UserManager method IsInRoleAsync() to retrieve user roles, filtering out the non-admin role.
public async Task<List<BookLoan.Models.ManageUserViewModels.UserViewModel>> GetUsers()
{
List<BookLoan.Models.ManageUserViewModels.UserViewModel>
userViews = new List<Models.ManageUserViewModels.UserViewModel>();
userManager.Users.ToList().ForEach(
async u =>
{
Task<bool> isInRoleResult =
userManager.IsInRoleAsync(u, "Admin");
bool isInRole = isInRoleResult.GetAwaiter().GetResult();
if (isInRole == false)
userViews.Add(new
Models.ManageUserViewModels.UserViewModel()
{
ID = u.Id,
UserName = u.UserName,
Email = u.Email
});
});
return userViews;
}
We can test the above using POSTMAN:
Adding a role:
Deleting a role:
Retrieving user roles:
We can also verify the data in the SQL AspNetUserRoles table that ASP.NET Identity uses to store our user roles:
Another thing to remember is to set up the user manager and identity stores for ASP.NET Identity to be able to use the UserManager API and RoleStore API on the ApplicationUser and IdentityRole data contexts you will need to setup the identity for these stores in the ConfigureServices() method within startup.cs using the AddIdentity() extension method:
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
Once this has been setup and your services have been setup for dependency injection, they can be injected into your services for use to manage your roles and users.
That’s all for today’s post.
I hope this post has been useful and informative.
Andrew Halil is a blogger, author and software developer with expertise of many areas in the information technology industry including full-stack web and native cloud based development, test driven development and Devops.