our

Latest Blogs

Angular application with Role-Based Authorization

iFour Team -June 23, 2021

Listening is fun too.

Straighten your back and cherish with coffee - PLAY !

  •  
  •  
  •  
Angular application with Role-Based Authorization

What is Role-Based Authorization?

In the system’s security, the role-based authorization / role-based access control (RBAC) is a way of restricting and managing machine access to authorized users.

RBAC (Role-based access control) confines access to the network based on the employee’s role in an organization.

Understanding role-based authorization with angular

We will make an angular app to restrict the access of sources of a page by implementing role-based authorization. This application will contain 3 pages (a login page, a home page, an admin page). In our application, the user can either be a normal user or an admin. A normal user has only access to the home page and an admin can access the home page as well as the admin page.

Start by Creating y our application and select whatever style property you want by using this command in the terminal.

ng new role-based-authorization –-routing

Now, we will create folders for each module i.e., home, admin, login. And additional folders for helpers, services and models with prefix underscore (_) to differentiate them from other components.

We have made the following 3 components in the application:

Admin Component

Admin.component.html:

This file is an admin component template that contains html code for displaying content on the admin page.


 

Admin

Admin dashboard!!

All users from secure (admin only) api end point:

 
  •  
  • {{user.firstName}} {{user.lastName}}
  •  

admin.component.ts:

The admin component calls the user service and finds all users from one secure API endpoint. It stores them in a local user’s property which is accessible to the admin component template.

import { Component, OnInit } from '@angular/core';
                  import { first } from 'rxjs/operators';
                  import { User } from '../_models';
                  import { UserService } from '../_services';
                  @Component({ templateUrl: 'admin.component.html' })
                  export class AdminComponent implements OnInit {
                      loading = false;
                      users: User[] = [];
                      constructor(private userService: UserService) { }
                      ngOnInit() {
                          this.loading = true;
                          this.userService.getAll().pipe(first()).subscribe(users => {
                              this.loading = false;
                              this.users = users;
                          });
                      }
                  }

Home Component

home.component.html:

This file is a home component template which contains html code for displaying content on the home page.


 

Home

Welcome to home page!!

Your role is: {{currentUser.role}}.

home.component.ts:

The home component fetches the current user from the authentication service so gets the current user from the api with the user service.

  import { Component, OnInit } from '@angular/core';
  import { Router, ActivatedRoute } from '@angular/router';
  import { FormBuilder, FormGroup, Validators } from '@angular/forms';
  import { first } from 'rxjs/operators';
  import { AuthenticationService } from '../_services';
  
  @Component({ templateUrl: 'login.component.html' })
  export class LoginComponent implements OnInit {
      loginForm: FormGroup;
      loading = false;
      submitted = false;
      returnUrl: string;
      error = '';
  
      constructor(
          private formBuilder: FormBuilder,
          private route: ActivatedRoute,
          private router: Router,
          private authenticationService: AuthenticationService
      ) { 
          if (this.authenticationService.currentUserValue) { 
              this.router.navigate(['/']);
          }
      }
      ngOnInit() {
          this.loginForm = this.formBuilder.group({
              username: ['', Validators.required],
              password: ['', Validators.required]
          });
          this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
      }
      get f() { return this.loginForm.controls; }
      onSubmit() {
          this.submitted = true;
          if (this.loginForm.invalid) {
              return;
          }
          this.loading = true;
          this.authenticationService.login(this.f.username.value, this.f.password.value)
              .pipe(first())
              .subscribe(
                  data => {
                      this.router.navigate([this.returnUrl]);
                  },
                  error => {
                      this.error = error;
                      this.loading = false;
                  });
      }
  }
  

Login component

login.component.html

The home component fetches the current user from the authentication service and then gets the current user from the api with the user service.


 

Role-Based-Authorization

Username is required
Password is required
{{error}}

login.component.ts:

This component makes use of the authentication service for logging into the application. In the case where the user is already logged in, it will automatically redirect them to the home page.

                    import { Component, OnInit } from '@angular/core';
                    import { Router, ActivatedRoute } from '@angular/router';
                    import { FormBuilder, FormGroup, Validators } from '@angular/forms';
                    import { first } from 'rxjs/operators';
                    import { AuthenticationService } from '../_services';
                    
                    @Component({ templateUrl: 'login.component.html' })
                    export class LoginComponent implements OnInit {
                        loginForm: FormGroup;
                        loading = false;
                        submitted = false;
                        returnUrl: string;
                        error = '';
                    
                        constructor(
                            private formBuilder: FormBuilder,
                            private route: ActivatedRoute,
                            private router: Router,
                            private authenticationService: AuthenticationService
                        ) { 
                            if (this.authenticationService.currentUserValue) { 
                                this.router.navigate(['/']);
                            }
                        }
                        ngOnInit() {
                            this.loginForm = this.formBuilder.group({
                                username: ['', Validators.required],
                                password: ['', Validators.required]
                            });
                            this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
                        }
                        get f() { return this.loginForm.controls; }
                        onSubmit() {
                            this.submitted = true;
                            if (this.loginForm.invalid) {
                                return;
                            }
                            this.loading = true;
                            this.authenticationService.login(this.f.username.value, this.f.password.value)
                                .pipe(first())
                                .subscribe(
                                    data => {
                                        this.router.navigate([this.returnUrl]);
                                    },
                                    error => {
                                        this.error = error;
                                        this.loading = false;
                                    });
                        }
                    }
                    

_helpers

The followings are the files under the “_helper” folders.

Auth Guard

The auth guard is an angular route guard which is used to shut out unauthenticated or unauthorized users from accessing restricted routes.

This is done by implementing the CanActivate interface that permits the guard to determine if a route is activated with the canActivate(). If this method returns true it means that the route is activated, the user is allowed to proceed, and if this method returns false the route is blocked.

The auth guard uses the authentication service to visualize if the user is logged in, if they’re logged in, it checks if their role is permitted to access the requested route. If they’re logged in and licensed the canActivate() method returns true, otherwise, it returns false and redirects the user to the login page.

Angular route guards are associated with the routes in the router config, this auth guard is used in app.routing.ts to protect the home page and admin page routes.

auth.guard.ts

                  import { Injectable } from '@angular/core';
                  import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
                  
                  import { AuthenticationService } from '../_services'
                  @Injectable({ providedIn: 'root' })
                  export class AuthGuard implements CanActivate {
                      constructor(
                          private router: Router,
                          private authenticationService: AuthenticationService
                      ) { }
                  
                      canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
                          const currentUser = this.authenticationService.currentUserValue;
                          if (currentUser) {
                              if (route.data.roles &&
                                  route.data.roles.indexOf(currentUser.role) === -1) 
                        {
                                  this.router.navigate(['/']);
                                  return false;
                               }
                              return true;
                          }
                  
                          this.router.navigate(['/login'], 
                                { queryParams: { returnUrl: state.url } });
                          return false;
                      }
                  }
                 

HTTP Error Interceptor

The Error Interceptor intercepts HTTP responses from the api to check if there have been any errors. If there’s a 401 Unauthorized or 403 Forbidden response the user is automatically logged out of the application, all other errors will be re-thrown up to the calling service so that an alert with the error can be displayed on the screen.

This interceptor is applied using the HttpInterceptor class contained in the HttpClientModule.

Error.interceptor.ts:

                  import { Injectable } from '@angular/core';
                  import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
                  import { Observable, throwError } from 'rxjs';
                  import { catchError } from 'rxjs/operators';
                  
                  import { AuthenticationService } from '../_services';
                  
                  @Injectable()
                  export class ErrorInterceptor implements HttpInterceptor {
                      constructor(private authenticationService: AuthenticationService) { }
                  
                      intercept(request: HttpRequest, next: HttpHandler): Observable> {
                          return next.handle(request).pipe(catchError(err => {
                              if ([401, 403].indexOf(err.status) !== -1) {

                                  this.authenticationService.logout();
                                  location.reload(true);
                              }

                              const error = err.error.message || err.statusText;
                              return throwError(error);
                          }))
                      }
                  }
                  

Fake Backend Provider

In this application, we used a fake backend API to intercept the HTTP requests to run and test the angular application instead of using a real backend API. We have done this by a class that implements the Angular HttpInterceptor interface.

fake-backend.ts


                    import { Injectable } from '@angular/core';
                    import { HttpRequest, HttpResponse, HttpHandler, HttpEvent, HttpInterceptor, HTTP_INTERCEPTORS } from '@angular/common/http';
                    import { Observable, of, throwError } from 'rxjs';
                    import { delay, mergeMap, materialize, dematerialize } from 'rxjs/operators';

                    import { User, Role } from '../_models';

                    const users: User[] = [
                        { id: 1, username: 'admin', password: 'admin', firstName: 'Admin', lastName: 'User', role: Role.Admin },
                        { id: 2, username: 'user', password: user', firstName: 'Normal', lastName: 'User', role: Role.User }
                    ];

                    @Injectable()
                    export class FakeBackendInterceptor implements HttpInterceptor {
                        intercept(request: HttpRequest, next: HttpHandler): Observable> {
                            const { url, method, headers, body } = request;

                            return of(null)
                                .pipe(mergeMap(handleRoute))
                                .pipe(materialize())
                                .pipe(delay(500))
                                .pipe(dematerialize());

                            function handleRoute() {
                                switch (true) {
                                    case url.endsWith('/users/authenticate') && method === 'POST':
                                        return authenticate();
                                    case url.endsWith('/users') && method === 'GET':
                                        return getUsers();
                                    case url.match(/\/users\/\d+$/) && method === 'GET':
                                        return getUserById();
                                    default:
                                        return next.handle(request);
                                }

                            }
                      function authenticate() {
                                const { username, password } = body;
                                const user = users.find(x => x.username === username
                                                        && x.password === password);
                                if (!user) return error('Username or password is incorrect');
                                return ok({
                                    id: user.id,
                                    username: user.username,
                                    firstName: user.firstName,
                                    lastName: user.lastName,
                                    role: user.role,
                                    token: `fake-jwt-token.${user.id}`
                                });
                            }

                            function getUsers() {
                                if (!isAdmin()) return unauthorized();
                                return ok(users);
                            }

                            function getUserById() {
                                if (!isLoggedIn()) return unauthorized();
                                if (!isAdmin() && currentUser().id !== idFromUrl())
                                   return unauthorized();

                                const user = users.find(x => x.id === idFromUrl());
                                return ok(user);
                            }

                            function ok(body) {
                                return of(new HttpResponse({ status: 200, body }));
                            }

                            function unauthorized() {
                                return throwError({ status: 401,
                                                    error: { message: 'unauthorized' } });
                            }

                            function error(message) {
                                return throwError({ status: 400, error: { message } });
                            }


                            function isLoggedIn() {
                                const authHeader = headers.get('Authorization') || '';
                                return authHeader.startsWith('Bearer fake-jwt-token');
                            }

                            function isAdmin() {
                                return isLoggedIn() && currentUser().role === Role.Admin;
                            }

                            function currentUser() {
                                if (!isLoggedIn()) return;
                                const id = parseInt(headers.get('Authorization').split('.')[1]);
                                return users.find(x => x.id === id);
                            }

                            function idFromUrl() {
                                const urlParts = url.split('/');
                                return parseInt(urlParts[urlParts.length - 1]);
                            }
                        }
                    }

                    export const fakeBackendProvider = {
                        provide: HTTP_INTERCEPTORS,
                        useClass: FakeBackendInterceptor,
                        multi: true
                    };
                    

One-stop-oplossing voor Angular-webontwikkeling - Uw zoekopdracht eindigt hier.

JWT Interceptor

This JWT Interceptor is used to intercept http requests from the angular application and to add a JWT auth token in the Authorization header.

jwt.interceptor.ts


                  import { Injectable } from '@angular/core';
                  import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
                  import { Observable } from 'rxjs';

                  import { environment } from '@environments/environment';
                  import { AuthenticationService } from '../_services';

                  @Injectable()
                  export class JwtInterceptor implements HttpInterceptor {
                      constructor(private authenticationService: AuthenticationService) { }

                      intercept(request: HttpRequest, next: HttpHandler): Observable> {
                          const currentUser = this.authenticationService.currentUserValue;
                          const isLoggedIn = currentUser && currentUser.token;
                          const isApiUrl = request.url.startsWith(environment.apiUrl);
                          if (isLoggedIn && isApiUrl) {
                              request = request.clone({
                                  setHeaders: {
                                      Authorization: `Bearer ${currentUser.token}`
                                  }
                              });
                          }

                          return next.handle(request);
                      }
                  }
                  

_models

Followings are the files under the “_models” folder:

Role Model

The role model file consists of an enum that states the roles supported by our application.

role.ts:

            
                      export enum Role {
                          User = 'User',
                          Admin = 'Admin'
                      }
                      
                  

User Model

The user model file is a small class consisting of the properties of a user along with their role as well as jwt auth token.

user.ts

            
                  import { Role } from "./role";

                  export class User {
                      id: number;
                      username: string;
                      password: string;
                      firstName: string;
                      lastName: string;
                      role: Role;
                      token?: string;
                  }
                  
              

_services

The following files are put under the “_service” folder:

Authentication Service

This service is used to login & logout in the Angular application. When the user logs in and out, it will be notified to other components.

authentication.service.ts

            
                import { Injectable } from '@angular/core';
                import { HttpClient } from '@angular/common/http';
                import { BehaviorSubject, Observable } from 'rxjs';
                import { map } from 'rxjs/operators';
                import { environment } from '@environments/environment';
                import { User } from '../_models';
                @Injectable({ providedIn: 'root' })
                export class AuthenticationService {
                    private currentUserSubject: BehaviorSubject;
                    public currentUser: Observable;

                    constructor(private http: HttpClient) {
                        this.currentUserSubject = new BehaviorSubject(JSON.parse(localStorage.getItem('currentUser')));
                        this.currentUser = this.currentUserSubject.asObservable();
                    }

                    public get currentUserValue(): User {
                        return this.currentUserSubject.value;
                    }

                    login(username: string, password: string) {
                        return this.http.post(`${environment.apiUrl}/users/authenticate`, { username, password })
                            .pipe(map(user => {
                                if (user && user.token) {
                                    localStorage.setItem('currentUser', JSON.stringify(user));
                                    this.currentUserSubject.next(user);
                                }
                              return user;
                            }));
                    }

                    logout() {
                        localStorage.removeItem('currentUser');
                        this.currentUserSubject.next(null);
                    }
                }
                
              

User Service

The user service consists of methods to retrieve user data from the api. This acts as the interface between our Angular application and the backend api.

user.service.ts


      import { Injectable } from '@angular/core';
      import { HttpClient } from '@angular/common/http';

      import { environment } from '@environments/environment';
      import { User } from '../_models';

      @Injectable({ providedIn: 'root' })
      export class UserService {
          constructor(private http: HttpClient) { }

          getAll() {
              return this.http.get(`${environment.apiUrl}/users`);
          }

          getById(id: number) {
              return this.http.get(`${environment.apiUrl}/users/${id}`);
          }
      }
      

Structure of the project:

Structure of the project

Figure 1 Structure of the project

The “index.ts” files in every folder are barrel files that cluster the exported modules from a folder together so that they will be imported using the folder path instead of importing the full module path, as well as to enable importing of multiple modules in a single import.

When we run the project by executing the ng serve –-open command we will get the following output:

User Service

Login Page

Figure 2 Login Page

When we try to log in as a normal user by entering Username=” user” and Password=” user”, we can access the only home page.

Home Page:

Home page as a User

Figure 3 Home page as a User

A normal user can logout by entering on Logout tab.

When someone tries to login as an admin by entering Username=”admin” and password=”admin”, he/she be able to access the home page as well as the admin page.

Home page

Home page as an Admin

Figure 4 Home page as an Admin

Admin Page

Admin Page

Figure 5 Admin Page

Conclusion

Role-Based-Authorization is necessary for any organization to protect the resources from being used by an unauthorized person as in any organization many employees use one system.

RBA allows employees of an organization to have access rights only to the information they require to do their tasks and restricts them from accessing information that doesn't concern with their role.

Work with us

Well do everything we can to make our next best project!

Our Insights

Check out our most recent blogs

An in-depth guide on Angular Dependency Providers
An in-depth guide on Angular Dependency Providers

What is Angular? Angular is a frontend development framework used for building single-page client applications using HTML and Typescript. It is written in Typescript. What...

A simple guide to Build Angular Reactive Templates with Ngif and Async Pipe
A simple guide to Build Angular Reactive Templates with Ngif and Async Pipe

Angular Templates seamlessly help to expedite the development process with flawless assistance of components and other specific elements. These are dynamic and renders according...

A simple guide on AOT Compilation in Angular
A simple guide on AOT Compilation in Angular

What is a Compiler? A compiler is nothing but a part of code that converts one programming language to another. If we talk about some simple programming languages like C, C++,...

Our Partners

Logo
Logo
Logo
Logo
Logo
Logo
Logo
Logo
Logo
Logo
Logo
Logo
Logo
Logo
Logo
Logo
Logo
Logo
Logo
Logo
Logo
Logo
Logo
Logo
Logo
Logo
Logo
Logo
Logo
Logo
Logo
Logo

Work With Us

Well do everything we can to make our next best project!