How to Implement Role-Based Access Control with JWT-Based Authorization in Angular
Tags: angular, howto, tutorial
Published: 2023-03-12 14:30:00

<p class="paragraph"> As web applications become more complex and user data is increasingly sensitive, it is essential to implement proper access control mechanisms to secure the application resources.  </p><p class="paragraph"> One effective method for implementing access control is <b>Role-Based Access Control (RBAC)</b>.  </p><p class="paragraph"> In this blog post, we will show you how to implement RBAC with<b> JSON Web Token (JWT)</b> based authorization in an Angular application. </p><h2>Step 1: Setup Angular Application</h2><p class="paragraph"> First, we need to set up an Angular application,Open your terminal and run the following command:<br> </p><pre><code class="code-block">ng new angular-rbac-jwt
</code></pre><h2>Step 2: Create User and Role Interfaces</h2><p class="paragraph"> Next, we need to define user and role interfaces. In this example, we will create two interfaces in a new file named <code>user.ts</code>: </p>
Also read,[Optimize Page Load Speed With Lazy Loading Images in Angular](https://www.tutscoder.com/post/lazy-loading-images-angular)
<pre><code class="code-block">export interface User {
  id: number;
  username: string;
  password: string;
  role: Role;
}

export interface Role {
  id: number;
  name: string;
  permissions: string[];
}</code></pre><p class="paragraph"> We define a user interface with an id, username, password, and role. The role property is an object of type Role that has an id, name, and permissions array. </p><h2>Step 3: Create JWT Service</h2><p class="paragraph"> To handle JWT-based authentication and authorization, we need to create a service. Create a new file named <code>auth.service.ts</code> and add the following code: </p><pre><code class="code-block">import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import { User } from './user';

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  private apiUrl = 'http://localhost:3000/api/auth';

  constructor(private http: HttpClient) { }

  login(username: string, password: string): Observable&lt;any&gt; {
    return this.http.post&lt;any&gt;(`${this.apiUrl}/login`, { username, password })
      .pipe(
        tap(res =&gt; {
          localStorage.setItem('access_token', res.access_token);
        })
      );
  }

  logout() {
    localStorage.removeItem('access_token');
  }

  public get loggedIn(): boolean {
    return localStorage.getItem('access_token') !== null;
  }

  get currentUser(): User {
    const token = localStorage.getItem('access_token');
    if (!token) {
      return null;
    }
    const payload = JSON.parse(atob(token.split('.')[1]));
    return payload.user;
  }

  public hasPermission(permission: string): boolean {
    const user = this.currentUser;
    if (!user || !user.role || !user.role.permissions) {
      return false;
    }
    return user.role.permissions.includes(permission);
  }
}
</code></pre><p class="paragraph"> We define an <code>AuthService</code> class with a <code>login()</code> method that sends a POST request to our API with the username and password. If the API returns an access token, we store it in local storage using the <code>tap()</code> operator. We also define a <code>logout()</code> method to remove the access token from local storage. </p><p class="paragraph"> The <code>loggedIn</code> property returns a boolean indicating whether the user is currently authenticated based on the presence of the access token in local storage. The <code>currentUser</code> property returns the user object stored in the access token's payload. Finally, the <code>hasPermission()</code> method takes a permission string and checks if the current user has that permission based on their role. </p><h2>Step 4: Implement Role-Based Access Control</h2><p class="paragraph"> To implement role-based access control, we will create a guard that checks if the user has the required permission to access a route. Create a new file named <code>role.guard.ts</code> and add the following code: </p><pre><code class="code-block">import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
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 {
    const requiredPermission = route.data.requiredPermission;
    if (this.authService.hasPermission(requiredPermission)) {
      return true;
    }
    this.router.navigate(['/login']);
    return false;
  }
}</code></pre><p class="paragraph"> The <code>RoleGuard</code> class implements the <code>CanActivate</code> interface and defines a <code>canActivate()</code> method. This method checks if the current user has the required permission to access the route. If the user has the required permission, the method returns <code>true</code>, allowing the user to access the route. If the user does not have the required permission, the method redirects the user to the login page and returns <code>false</code>. </p><h2>Step 5: Use the RoleGuard</h2><p class="paragraph"> Now that we have defined the <code>RoleGuard</code>, we can use it to protect routes that require specific permissions. For example, suppose we have a route that allows only users with the <code>viewReports</code> permission to access. We can protect this route by adding the <code>RoleGuard</code> to the route's <code>canActivate</code> property and passing the required permission as data. Here's an example: </p><pre><code class="code-block">import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { LoginComponent } from './login/login.component';
import { ReportsComponent } from './reports/reports.component';
import { RoleGuard } from './role.guard';

const routes: Routes = [
  { path: '', component: HomeComponent },
  { path: 'login', component: LoginComponent },
  { path: 'reports', component: ReportsComponent, canActivate: [RoleGuard], data: { requiredPermission: 'viewReports' } }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }</code></pre><p class="paragraph"> In the above code, we add the <code>RoleGuard</code> to the <code>canActivate</code> property of the <code>ReportsComponent</code> route and pass the <code>viewReports</code> permission as data. </p><p class="paragraph"> Step 6: Test the Application </p><p class="paragraph"> To test the application, we need to create a mock API that returns an access token with a user object containing a role with permissions. For this example, we will use <code>json-server</code> to create a mock API. </p><p class="paragraph"> Create a <code>db.json</code> file with the following code: </p><pre><code class="code-block">{
  "users": [
    {
      "id": 1,
      "username": "admin",
      "password": "admin",
      "role": {
        "id": 1,
        "name": "Administrator",
        "permissions": ["viewReports", "editReports", "deleteReports"]
      }
    },
    {
      "id": 2,
      "username": "user",
      "password": "user",
      "role": {
        "id": 2,
        "name": "User",
        "permissions": ["viewReports"]
      }
    }
  ]
}
</code></pre><p class="paragraph"> This file defines two users with different roles and permissions. </p><p class="paragraph"> To start the mock API, run the following command in your terminal: </p><pre><code class="code-block">json-server --watch db.json
</code></pre><p class="paragraph"> This will&nbsp;start a mock API server at <code>http://localhost:3000</code>. </p><p class="paragraph"> Now we can test the application by logging in with the <code>admin</code> user and trying to access the <code>/reports</code> route. The <code>admin</code> user has the <code>viewReports</code>, <code>editReports</code>, and <code>deleteReports</code> permissions, so they should be able to access the <code>/reports</code> route. </p><p class="paragraph"> To log in, enter the <code>admin</code> username and password on the login page. Once logged in, click on the "Reports" link in the navigation menu. This should take you to the <code>ReportsComponent</code>. </p><p class="paragraph"> To test if the <code>RoleGuard</code> is working, add the <code>canActivate</code> property to the <code>HomeComponent</code> route and set it to the <code>RoleGuard</code> with the <code>viewReports</code> permission. This will prevent users without the <code>viewReports</code> permission from accessing the home page. </p><p class="paragraph"> Here's an example of how to protect the home page with the <code>RoleGuard</code>: </p><pre><code class="code-block">{ path: '', component: HomeComponent, canActivate: [RoleGuard], data: { requiredPermission: 'viewReports' } },
</code></pre><p class="paragraph"> Now, if you log in with the <code>user</code> username and password, which only has the <code>viewReports</code> permission, you should be redirected to the login page when trying to access the home page. </p><p class="paragraph"> <b>Congratulations!</b> You have successfully implemented role-based access control with JWT-based authorization in Angular. </p>
[How To Implement Role-Based Access Control With JWT-Based Authorization In Angular
](https://www.tutscoder.com/post/role-based-access-control-jwt-angular-guide)