Angular Authorization Tutorial: Implementing Role-Based Access Control with Angular Guard

Implementing secure role-based access control (RBAC) is essential for modern web applications. Angular provides a robust way to manage authorization using guards, which can restrict access to routes based on user roles. This tutorial guides you through creating an Angular guard that enforces role-based permissions.

Understanding Role-Based Access Control in Angular

RBAC allows you to assign different permissions to users based on their roles, such as ‘admin’, ‘editor’, or ‘viewer’. Angular guards can check these roles before activating a route, ensuring users only access permitted areas of the application.

Setting Up User Roles and Authentication

Before implementing guards, establish a way to identify user roles. Typically, this involves:

  • Authenticating users via a login system
  • Storing user roles in a token or user service
  • Providing a method to retrieve current user roles

For this tutorial, assume roles are stored in a JWT token and accessible through an AuthenticationService.

Creating the Role Guard

Generate a new guard using Angular CLI:

ng generate guard role

Open role.guard.ts and modify it as follows:

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router } from '@angular/router';
import { Observable } from 'rxjs';
import { AuthService } from './auth.service';

@Injectable({
  providedIn: 'root'
})
export class RoleGuard implements CanActivate {

  constructor(private authService: AuthService, private router: Router) {}

  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): boolean | UrlTree | Observable | Promise {
    const expectedRoles = route.data['roles'] as Array;
    const userRoles = this.authService.getUserRoles();

    if (this.hasRequiredRole(userRoles, expectedRoles)) {
      return true;
    } else {
      return this.router.parseUrl('/access-denied');
    }
  }

  private hasRequiredRole(userRoles: Array, expectedRoles: Array): boolean {
    return expectedRoles.some(role => userRoles.includes(role));
  }
}

Configuring Routes with Role Guards

Apply the guard to routes in your app-routing.module.ts and specify required roles:

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AdminComponent } from './admin/admin.component';
import { UserComponent } from './user/user.component';
import { RoleGuard } from './role.guard';

const routes: Routes = [
  {
    path: 'admin',
    component: AdminComponent,
    canActivate: [RoleGuard],
    data: { roles: ['admin'] }
  },
  {
    path: 'user',
    component: UserComponent,
    canActivate: [RoleGuard],
    data: { roles: ['user', 'admin'] }
  },
  {
    path: 'access-denied',
    component: AccessDeniedComponent
  }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule {}

Implementing the Authentication Service

The AuthService should provide the getUserRoles() method. Here’s a simple example:

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  private roles: Array = [];

  constructor() {
    // Example: fetch roles from token or API
    this.roles = this.getRolesFromToken();
  }

  getUserRoles(): Array {
    return this.roles;
  }

  private getRolesFromToken(): Array {
    // Logic to decode token and extract roles
    // For demonstration, return a static role
    return ['user'];
  }
}

Testing Role-Based Access Control

Navigate to different routes in your application. Users with appropriate roles will access protected pages, while others will be redirected to the access denied page.

This setup ensures a secure, scalable way to manage route access based on user roles in Angular applications.