Security authorization
Angular ASP.NET Core Identity C# SPA Typescript Visual Studio Code Web API

How to Implement Role Based Authorization in Angular Web API Applications

Hi and welcome back to my blogs on development!

Today I will go over how you can implement role-based authorization within your Angular Web API .NET Core applications.

In a previous post I showed how to setup ASP.NET Identity for EF Core within an ASP.NET Core application. In another post, I showed how to create an Identity Service API using ASP.NET Core. Building on those posts, In today’s post I will show how to make use of the Identity API to access roles that are stored within our database and use these to restrict access to UI application components.

There are two different types of authorization that are most popular these days:

  • Role-based authorization
  • Claims-based authorization

I will focus on the role-based authorisation as it is commonly adopted for basic applications.

When do you need to use role-based authorization within your application?

The answer is based on the need to secure different areas of your application parts of your application based on access levels and user roles. 

How ASP.NET Identity Roles are Stored in a SQL Database

To setup role-based authorization we store all user roles within a SQL data base.

There are two tables we use primarily for roles-based management in ASP.NET Identity.

These tables are:

AspNetRoles, which stores the names of all roles which can be assigned to a user login account.

AspUserRoles, which associates a role to a user login account.

When selecting data from these tables, all primary and foreign keys are GUIDs. 

SELECT [Id],
       [ConcurrencyStamp],
       [Name],
       [NormalizedName]
FROM   [aspnet-IdentityDb].[dbo].[AspNetRoles]

An example output of the above query is shown below:

The user roles are selectable with the following query:

SELECT  [UserId],[RoleId]
FROM 	[aspnet-IdentityDb].[dbo].[AspNetUserRoles]

An example output of the above query is shown below:

Accessing User Roles with the ASP.NET Core Identity API

As you can see from the query output in the previous section, role and user role records are identified by GUIDs. To access user roles from our API we can use two different methods:

  1. By user id.
  2. By user login name.

Access by user id is required whenever we wish to retrieve user roles for a particular user based on their id. This would occur when we are in a UI and have retrieved user details in id and wish to retrieve the associated user roles in a separate call on the API.

Our controller would be as follows:

[HttpGet("api/[controller]/UserRoleEdit/{id}")]
public async Task<ActionResult> UserRoleEdit(string id)
{
    var userrole = await _userRoleService.GetUserRoleDetailsById(id);
    return Ok(userrole);
}

Our service method would be as follows:

public async Task<BookLoan.Models.ManageUserViewModels.UserRoleViewModel> GetUserRoleDetailsById(string userId)
{
    var roleManager = new RoleStore<IdentityRole>(db);
    var user = await userManager.FindByIdAsync(userId);
    var userRoles = await userManager.GetRolesAsync(user);
    var allRoles = roleManager.Roles.ToList();
    List<string> stringRoles = new List<string>();
    foreach (IdentityRole item in allRoles)
    {
        stringRoles.Add(item.Name);
    }
    return new Models.ManageUserViewModels.UserRoleViewModel()
    {
        DisplayName = user.UserName,
        LoginName = user.Email,
        UserID = user.Id,
        UserRoles = userRoles.ToList(),
        AvailableRoles = stringRoles
    };
}

To access the data from the role tables that we discussed earlier, we make use of the RoleManager class to retrieve all possible roles from the AspNetRoles table from the Roles collection.

We make use of the UserManager class to retrieve the following:

  1. User object by user id using FindByIdAsync().
  2. User roles by user object using GetRolesAsync().

To retrieve user roles by user login name we have a different API. The controller API method is shown below:

[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();
    }
}

The dependent service is shown below:

public async Task<List<string>> GetUserRoles(string userName)
{
    var user = await userManager.FindByEmailAsync(userName);
    IList<string> roles = await userManager.GetRolesAsync(user);
    if (roles != null)
        return roles.ToList();
    return new List<string>();
}

We make use of the UserManager class to retrieve the following:

  1. User object by user login name (email) using FindByEmailAsync().
  2. User roles by user object using GetRolesAsync().

Because all the UserManager and RoleManager ASP.NET Identity class methods are asynchronous, all API methods should be using the asyncawait pattern to ensure that requests from HTTP requests are re-entrant.

Obtaining User Roles within the Angular Application from the Web API

On our UI web application, to access the roles we need to first declare API services for the HTTP GET requests:

getUserRoleInfoById(id): Observable<UserRole> {
    return this.http.get<UserRole>(this.baseIdentityAPI + 
        '/ManageUser/UserRoleEdit/' + id); 
}

getUserRoles(userName): Observable<string[]> {
    return this.http.get<string[]>(this.baseIdentityAPI + 
        '/ManageUser/GetUserRoles/' + encodeURIComponent(userName)); 
}

In a UI component with TypeScript, we would subscribe to our user and user roles and build an array to store the user’s roles. From here it would be straightforward to populate a control, such as a drop-down list or a checkbox group control to display the current user roles.

We could define a basic structure, a DTO to hold selected roles:

export class SelectedRole
{
    public role: string;
    public isSelected: boolean;
}

Then in our UI source we call our API services to obtain the user and user role structures based on the user id. This would be most useful in the context of a form that displayed a list of users (read in from the API) that we wish to maintain.

const getUser$ = this.api.getUser(this.id);
const getUserRole$ = this.api.getUserRoleInfoById(this.id);

combineLatest(
[
    getUser$,
    getUserRole$
]).subscribe(([curruser, userrole]) => {
    if (!curruser || !userrole)
        return;

From here we first initialize the structure to unselect all roles:

let userRoles: SelectedRole[] = [];

roles.forEach(r =>
{
    userRoles.push({ role: r, isSelected: false });
});

Then we select the roles that are applicable for the user:

if (!userrole['userRoles'])
    return;

let selectedUserRoles: string[] = [];

userrole['userRoles'].forEach(r => 
{
    const item = userRoles.find(s => s.role === r);
    if (item)
    {
        item.isSelected = true;
        selectedUserRoles.push(r);
    }
});
this.userSelectedRoles = selectedUserRoles;

The other applicable use for our user role is when selecting by the user email.

This is appropriate when logging in and authenticating our user:

We have an authentication service:

export class AuthService {
    public currUser: Observable<User>;
    public currUserRoles: BehaviorSubject<string[]>;
    public userRoles: Observable<string[]>;

    public loginResponse: Subject<string> = new Subject<string>();

    public get isUserAdmin() 
    {
        return this.isUserInAdminRole();
    } 
   ...

We have a login method that authenticates from the credentials and returns the user roles:

login(userName: string, password: string) {
    var config = {
        headers: new HttpHeaders({
            'Content-Type': 'application/json',
        })
    };        
    this.http.post<any>
('http://localhost/BookLoan.Identity.API/api/Users/Authenticate', 
    { 
        "UserName": userName, 
        "Password": password 
    }, config)
    .subscribe(
        res => { this.authenticate(res)     
            this.apiService.getUserRoles(this.currLoggedInUserValue)
            .subscribe(r => 
            {
                this.authenticateRole(r);
                this.loginResponse.next("login success");
            });
        },
        error => { 
            console.error('There was an error!', error); 
            this.loginResponse.next(error);
        },
        ()=> {  
        }
    );
}

We store the roles within local storage:

authenticateRole(roles: string[]) {
    localStorage.setItem('role', roles.toString());
}

And make them accessible within our application:

isUserInAdminRole(): boolean
{
    if (!localStorage.getItem('role'))
        return false;
    return localStorage.getItem('role').includes('Admin');
}

From our authorization service we can determine if the currently logged in user is an admin (or any other role we wish to check) and show or hide menus and menu items applicable to the user role:

<mat-menu #loginMenu="matMenu">
  <div *ngIf="authService.isUserAdmin && authService.isLoggedIn()">
    <button mat-menu-item id="btnUserAdmin" (click)="adminusers()"><a>User Admin</a> 
    </button>
  </div>

  <div *ngIf="authService.isLoggedIn()">
    <button mat-menu-item id="welcomeMsg">Welcome {{authService.currLoggedInUserValue}} 
    </button>
    <button mat-menu-item id="btnMenuLogout" (click)="logout()">
      <a>Logout</a>
    </button>
  </div>
  <div *ngIf="!authService.isLoggedIn()">
    <button mat-menu-item id="btnMenuLogin">
      <a routerLink="/login" routerLinkActive="activebutton">Login</a>
    </button>
  </div>
</mat-menu>

Of course, this is one way of implementing authorization of users. Another method would be to have our API methods return the user, user roles and authorized application menu structures in one API call. This post gives you the groundwork to expand on this and implement your own customized authorization.

That’s all for today’s post.

I hope you found it useful and informative.

Social media & sharing icons powered by UltimatelySocial