0

So I was trying to implement Route Guards for my webapp but came into a lot of errors. What did I do wrong?

My webapp consists of multiple routes, one of which is dashboard, which needs to be protected by a login component. That's exactly what I tried implemented but failed at it. My login component generates a JWT Token from the BackEnd and adds it into LocalStorage (in format of an object "token": "").

My dashboard.component.ts:

import { Component, OnInit } from '@angular/core';
import { User } from '../models/user.model';

@Component({
  selector: 'app-dashboard',
  templateUrl: './dashboard.component.html',
  styleUrls: ['./dashboard.component.css']
})
export class DashboardComponent implements OnInit {
  user: User;

  constructor() { }

  ngOnInit() {
  }

  canDeactivate(): Promise<any> | boolean {
    if (localStorage.getItem('currentUserToken') === 'token') {
            return (true);
          } else {
            return (false);
          }

}
}

My auth.guard.ts:

import {Injectable} from '@angular/core';
import {ActivatedRoute, ActivatedRouteSnapshot, CanActivate, CanLoad, Route, Router, RouterStateSnapshot} from '@angular/router';
@Injectable()
export class AuthGuard implements CanActivate, CanLoad {
    constructor(private router: Router) {
    }
    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
        return this.checkToken();
    }
    canLoad(route: Route): boolean {
        return this.checkToken();
    }
    checkToken() {
        if (localStorage.getItem('currentUserToken')) {
            return true;
        } else {
            this.router.navigateByUrl('/avior/login');
            return false;
        }
    }
}

My can-exit.guard.ts:

import {Injectable} from '@angular/core';
import {CanDeactivate} from '@angular/router';
import {CanExit} from './canExit';
@Injectable()
export class CanExitGuard implements CanDeactivate<CanExit> {
 canDeactivate(component: CanExit) {
   if (component.canDeactivate) {
     return component.canDeactivate();
   }
   return true;
 }
}

My canExit.ts:

import {Observable} from 'rxjs';
export interface CanExit {
 canDeactivate: () => Observable<boolean> | Promise<boolean> | boolean;
}

The errors in the browser are:

DashboardComponent.ngfactory.js? [sm]:1 ERROR TypeError: Cannot read property 'id' of undefined
DashboardComponent.ngfactory.js? [sm]:1 ERROR CONTEXT DebugContext_ {view: {…}, nodeIndex: 1, nodeDef: {…}, elDef: {…}, elView: {…}}
ERROR TypeError: Cannot read property 'id' of undefined
ERROR CONTEXT DebugContext_ {view: {…}, nodeIndex: 1, nodeDef: {…}, elDef: {…}, elView: {…}}

UPDATE: StackBlitz: https://stackblitz.com/edit/angular-v9u7nw?file=src%2Fapp%2Fapp.routing.ts

Munchkin
  • 857
  • 5
  • 24
  • 51
  • whats your problem here. first you want only login page and then want dashboard page only after login is succeed? – Developer Nov 18 '19 at 10:53
  • @GaurangDhorda The dashboard needs to be protected by checking whether a token in LocalStorage exists and if so then allow access, otherwise deny it – Munchkin Nov 18 '19 at 10:54
  • A word of advice - Never ever store JWTs in local storage. They can be easily read by any other site. – batbrain9392 Nov 18 '19 at 10:58
  • @batbrain9392 this is only a prototype and in the future it should use secure HttpOnly cookies. On a side note: how do you read the localStorage cross-site? Because it's also a problem i'm having (my BackEnd should clear my FrontEnd LocalStorage) – Munchkin Nov 18 '19 at 11:00
  • Could you kindly replicate this problem in stackblitz? Because to me, it seems that the "cannot read id" issue might not be related to your route guards. More on the local storage problem: https://dev.to/rdegges/please-stop-using-local-storage-1i04 – batbrain9392 Nov 18 '19 at 11:06
  • @batbrain9392 updated the answer. Apparently the StackBlitz is broken for some reason. – Munchkin Nov 18 '19 at 11:41
  • But hey at least you can see more code. – Munchkin Nov 18 '19 at 11:52

1 Answers1

1

for redirecting user to login component you need router guard in routing path defined. I am here not setting token part for this use-case. you can find complete working StackBlitz Link here. if You directly redirect to /dashboard, auth-guard redirect to /login if user has not logged in.

your auth-guard is..

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

   if(this.authService.isUserLoggedIn()){
       return true;
   }else{
      this.router.navigate(['/login'],{relativeTo: this.activatedRouter});
        return false;
    }
}

Your Authentication service looks like this..

 isAuthenticated(username: string, password: string): Observable <boolean>{
return this.getAllUsers().pipe(
  map(users => {
    let user = users.find( user => (user.username === username) && (user.password === password) );
        console.log(user)
        if (user) {
          this.isLoggedin = true;
          this.loggedInUser = user;
        } else {
          this.isLoggedin = false;
        }
        return this.isLoggedin;
  })
)
}

Your app-routing module is..

const routes: Routes = [{path: 'login', component: LoginComponent},
                    {path: '', redirectTo: 'login',  pathMatch: 'full'},
                    {path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuard]}
                    ];

Your Login submit method..

this.authService.isAuthenticated(this.loginForm.get('username').value,this.loginForm.get('password').value).subscribe(authenticated =>{
    if (authenticated){
      let url = this.authService.getRedirectUrl();
       this.router.navigate([url]);
    }
});
Developer
  • 3,309
  • 2
  • 19
  • 23
  • Hmm your code appears to give me errors like Property 'isLoggedin' does not exist on type 'AuthenticationService'., Property 'find' does not exist on type 'unknown'. etc – Munchkin Nov 18 '19 at 12:49
  • Have you checked StackBlitz link? – Developer Nov 18 '19 at 12:50
  • I have the StackBlitz link you sent me cloned locally and I used it as reference for building my own app. – Munchkin Nov 18 '19 at 12:51
  • what have you changed? – Developer Nov 18 '19 at 12:55
  • I tried copy-pasting the code snippets you supplied in the answer, now I'm trying to take a look at your StackBlitz project, which appears to be a more complete (for my needs) version of the locally stored copy. – Munchkin Nov 18 '19 at 12:56