Implementing role-based authentication in Angular using jwtHelper for accessing the admin component

I have built a frontend using Angular 6 and I am trying to implement role-based authorization using JWT helper-service. However, I am facing an issue where I am unable to access the admin component even though I have the necessary authorization.

Below is my LoginComponent:

import { Component, OnInit } from '@angular/core';
import { AuthService } from 'src/app/auth.service';
import { Router } from '@angular/router';


@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {

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

  ngOnInit() {
  }
  loginUser(event)
  {
    event.preventDefault()
    const target = event.target
    const email= target.querySelector('#email').value
    const password = target.querySelector('#password').value

     this.Auth.getUserDetails(email,password).subscribe(data =>{
       if(this.Auth.isAuthenticated(),data.token)
       {
         localStorage.setItem('token',data.token);
         return true;
       }

       else
       {
         window.alert("Authentication Failed");
       }
     });
     console.log(email,password)
  }


}

This is my role.guard.ts file:

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

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

  constructor( public auth: AuthService,public router: Router) {}
  
   // Methods for checking role-based authentication
  
}

Here is my auth.guard.ts implementation:

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


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


  constructor(private auth: AuthService,private router:Router, private user: UserService)
  {

  }


  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {


      // Check for user authentication before allowing access

      return true


    }

}

My auth.service.ts code looks like this:

import { Injectable } from '@angular/core';
import{ HttpClient } from '@angular/common/http';
import { JwtHelperService } from '@auth0/angular-jwt';

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  
  // Implementation of methods for handling authentication
  
}

Here's a snippet from my app.module.ts file:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule, Component } from '@angular/core';
import { RouterModule } from '@angular/router';
import { AuthGuard } from './auth.guard';
import { HttpClientModule } from '@angular/common/http';
import { MatTableModule, MatInputModule, MatSelectModule
 } from '@angular/material'
 import { RoleGuard } from './role.guard'

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { HomeComponent } from './components/home/home.component';
import { LoginComponent } from './components/login/login.component';
import { AdmindashboardComponent } from './components/admindashboard/admindashboard.component';
import { AuthService } from './auth.service';
import { UserService } from './user.service';
import { ViewallstudentsComponent } from './components/viewallstudents/viewallstudents.component';
import { AdminComponent } from './components/admin/admin.component';
import { AddsectionadminsComponent } from './components/addsectionadmins/addsectionadmins.component';
import { PagenotfoundComponent } from './components/pagenotfound/pagenotfound.component';
import { from } from 'rxjs';
import { JwtHelperService, JwtModule } from '@auth0/angular-jwt';

@NgModule({
  declarations: [
    AppComponent,
    HomeComponent,
    LoginComponent,
    AdmindashboardComponent,
    ViewallstudentsComponent,
    AdminComponent,
    AddsectionadminsComponent,
    PagenotfoundComponent
  ],
  imports: [
    JwtModule,
    MatSelectModule,
    MatTableModule,
    MatInputModule,
    BrowserModule,
    HttpClientModule,
    AppRoutingModule,
    BrowserAnimationsModule,
    RouterModule.forChild([
      // Route configurations
      
    ])
  ],
  providers: [ AuthGuard, UserService,AuthService,RoleGuard,JwtHelperService],
  bootstrap: [AppComponent]
})
export class AppModule { }

Answer №1

If you encounter an HTTP status code of 403, indicating "unauthorized to access", there could be several reasons for this issue:

  1. localstorage.getItem('token') may be consistently returning false. Check your browser's local storage (F12 -> Application -> expand the local storage) to confirm if the token is present.
  2. The token is in the localStorage, but due to asynchronicity of getItem('token'), the checks are failing.
  3. this.jwtHelper.isTokenExpired(token)
    is consistently returning a false value.

A few adjustments to your code can resolve this issue:

import { Injectable } from '@angular/core';
import{ HttpClient } from '@angular/common/http';
import { JwtHelperService } from '@auth0/angular-jwt';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  uri : String = 'http://localhost:4000';
  public isLoggedIn = new BehaviorSubject<boolean>(false); // {1}
  token: string = "";
   canActivatestateChanged = false;

  private jwtHelper = new JwtHelperService();

  constructor(private http: HttpClient) { }

   //Getter for the Behavioral variable `isLoggedIn` to use in the Guards
   get isLoggedIn() {

        return this.isLoggedIn.asObservable(); 
    }

  public isAuthenticated() : boolean {
    const token = this.getToken()
    // Check whether the token is expired and return
    // true or false
    if (token === undefined || token  === "") {
                this.isLoggedIn.next(false);
                this.canActivatestateChanged  = false;
                this.router.navigate(['/login']);                
     }else{
           if(!this.jwtHelper.isTokenExpired(token)){
                this.isLoggedIn.next(true);
                this.canActivatestateChanged  = true;
               this.router.navigate(['/admin']);

            }else{
            this.isLoggedIn.next(false);
            this.canActivatestateChanged  = false;
            this.router.navigate(['/login']);
           }
      }
    return this.canActivatestateChanged

  }

  getUserDetails(email: String,password:String){

    //post these details to the database
    return this.http.post(`${this.uri}/auth`,{ email,password});
  }
  signupadminsections(email:String,password:String,name:String,admintype:String,college:String,department:String)
  {
    //add new admin section
    return this.http.post(`${this.uri}/register`,{ 
     email,password,name,admintype,college,department});
  }

//You may need this to get the token from the local storage
public getTokenFromLocalStorage(): string {
        return localStorage.getItem('token');
 }
}

To check the RoleGuard.ts, include the following code:

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


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


constructor(private auth: AuthService,private router:Router, private user: UserService)
  {

  }

  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {

     return this.auth.isLoggedIn       // {1}
      .take(1)                               // {2} 
      .toPromise().then((isLoggedIn: boolean) => {
      try {
          if (!this.auth.isAuthenticated()) {
              return false;
            }
          else {
              return true;
            }
    } catch(e){
     console.log(e);
    }

}
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Creating a nested observable in Angular2: A step-by-step guide

Currently, I am exploring a new approach in my Angular2 service that involves using observables. The data source for this service will initially be local storage and later updated when an HTTP call to a database returns. To achieve this dynamic updating of ...

Viewing documents stored on the file server using the frontend interface (angular)

I am currently working on an Angular application that interacts with a .NET Core API. I have encountered an issue where the file upload process successfully uploads files to a file server, but I am unable to access these files on the front end. Despite at ...

When accessing APIs, create an array of observables for each user call and then trigger a function once all are successfully resolved

As I aim to generate a list of observables while a user engages with the webpage, I am faced with the challenge of individually subscribing to each observable, but desiring another function to execute after individual or multiple simultaneous API calls are ...

io-ts: Defining mandatory and optional keys within an object using a literal union

I am currently in the process of defining a new codec using io-ts. Once completed, I want the structure to resemble the following: type General = unknown; type SupportedEnv = 'required' | 'optional' type Supported = { required: Gene ...

What is the best way to identify the initial item in an array that is also present in a different array through TypeScript?

I have two arrays of objects and I only want to retrieve the first matching object from one array if it is found in the other array. I need to stop the search after finding the first match, but I am having trouble breaking out of the loop. Example 1:- var ...

Guide for submitting Angular 5 form data to a database using HTTP POST requests

After creating a fresh object using Angular 5 reactive forms without any specific id elements, I encounter a situation where I am unsure how to submit the form's JavaScript object to a web API. The JavaScript object is obtained from form.value but lac ...

Trouble authenticating user through express-session with Typescript

I have developed a small app for registration and login, but I am encountering issues with using session-express to maintain the session. Check out server.ts below where I establish session, cors, etc... import express, { json } from "express"; ...

Executing Angular Dependency Injection after initializing an NGRX createEffect() function

I am currently in the process of migrating an ngrx effect class to a new repository, and I have encountered issues with dependency injection during unit testing. Below is a simplified version of the effects class: import { Injectable } from '@angular/ ...

Encountering a syntax issue with pipeable operators in Angular Rxjs

I am currently in the process of rewriting this code snippet: Observable .merge(this.searchQuery$, this.lazyQuery$) .do(() => this.loadingPage()) .map(filter => this.buildURL("jaume", Config.security['appName'], filter)) .s ...

What are the ways to integrate JSON data into Angular?

I am facing some issues with integrating JSON in Angular. Within my project, I am generating components and deleting the spec.ts files. Subsequently, I create a new file called prod.data.json { "products" : [ { ...

Leveraging an Angular component for form input management

When I consider the form below <div [formGroup]="form"> <div [ngSwitch]="control.controlType"> <div *ngSwitchCase="'checkbox'"> <label [attr.for]="co ...

What is the best way to integrate environment-specific configuration options into an AngularJS and Typescript project?

Currently, I am working on a project using AngularJS, Typescript, and VisualStudio. One of the key requirements for this project is to have a configuration file containing constants that control various settings such as REST API URLs and environment names. ...

What is the best way to ensure that the function is referencing the class appropriately?

Typically when using this, it points to the class being referenced. However, in this scenario, this is set to dataChannel. How can I make this point back to VideoService? Thank you. export class VideoService { dataChannel:any; setupPeerConnectio ...

Tips for assigning a distinct name to ngModelGroup within a form at various locations

One of the components I have is app-common-component with a selector of app-common-component. This component is based on an ngModelGroup: <fieldset ngModelGroup> </fieldset> In my form, I have divided it into two components: app-component ...

Unable to install a specific commit of an angular library from GitHub using npm

While utilizing Angular 2.0.0-beta.15, I encountered the inability to upgrade it. Thus, I had to search for a specific commit from the ng2-dnd library on GitHub. Upon locating a compatible commit for version 2.0.0-beta.17: "ng2-dnd": "git://github.com/ak ...

What role does enum play in typescript?

What is the purpose of an enum in typescript? If it's only meant to improve code readability, could we achieve the same result using constants? enum Color { Red = 1, Green = 2, Blue = 4 }; let obj1: Color = Color.Red; obj1 = 100; // IDE does not sh ...

Unique: "Unique One-Step Deviation in Date Comparison"

A Little Background Information: I am working with data points that span from the current day to 5 days ahead, in 3-hour intervals (such as 10pm, 1am, 4am, 7am...). My goal is to organize these data points into arrays sorted by date, with each array repre ...

New to TypeScript: Encounter a compilation error while launching Apollo Server and attempting to resolve a promise

Seeking assistance with starting an Apollo server in a TypeScript project utilizing Express. The code snippet for my app.ts is displayed below. Upon execution, I encounter the following error: Argument of type '(value: unknown) => void' is not ...

TypeORM: Create case-insensitive search functionality

Creating a basic search feature where the records are as follows: AB CD A BCD ABC D ABD C If the search term is "BCD", the expected output should be: AB CD A BCD ABC D The current query looks like this: await connection.manager .createQueryBuilder(RefTra ...

Struggling to successfully pass React props.children

Currently, I am utilizing react in my project and I have encountered an error when passing props.children to the Header component. Despite passing React as the type, the following error message is displayed. I am unsure why this error is happening. e ...