'AuthGuard/Authentication Service Does Not work as expected in Angular 13 App

I try to implement a simple login/logout mechanism in an angular/rest api training application ! Even after a logout() in my authentication.service.ts :

import { Injectable } from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {
  BehaviorSubject,
  Observable
} from 'rxjs';
import {map} from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class AuthenticationService {
  private trainingUserSubject: BehaviorSubject<any>;
  public trainingUser: Observable<any>;
  private LOGIN = '/api/User/Login';

  constructor(
    private http: HttpClient
  ) {
    this.trainingUserSubject = new BehaviorSubject<any>(JSON.parse(localStorage.getItem('trainingUser') || '{}'));
    this.trainingUser = this.trainingUserSubject.asObservable();
  }
  // get the current user value
  public get trainingUserValue() {
    return this.trainingUserSubject.value;
  }

  // set the current user value
  public setTrainingUser(value: any) {
    this.trainingUserSubject.next(value);
  }
  // log user in
  login(data: FormData) {
    return this.http.post<any>(this.LOGIN, data)
      .pipe(map(response => {
        return response;
      }));
  }
  // log user out and remove his information stored in local storage
  logout() {
    // remove user from local storage and set current user to null
    localStorage.removeItem('trainingUser');
    if (this.trainingUserSubject) {
      this.trainingUserSubject.next(null);
    }
    window.location.reload();
  }
}

I still can navigate to my root page application. Here my authorisation.guard.ts :



import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot, UrlTree, Router } from '@angular/router';
import { Observable } from 'rxjs';
import {AuthenticationService} from '@app/_services/authentication.service';


@Injectable({
  providedIn: 'root'
})
export class AuthorisationGuard implements CanActivate {
  constructor(
    private router: Router,
    private authenticationService: AuthenticationService
  ) {}
  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    const trainingUser = this.authenticationService.trainingUserValue;

    if (trainingUser) {
      // authorized so return true
      return true;
    }
    window.alert('Access Denied, Login is Required to Access This Page!');
    this.router.navigate(['/login'], {queryParams: { returnUrl: state.url}});
    return false;
  }
}

if (trainingUser) {
      // authorized so return true
      return true;
    }


return always true !

Here my routing.module.ts :


import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import {Routes, RouterModule} from '@angular/router';
import {HomeComponent} from '@page/home/home.component';
import {LoginComponent} from '@page/login/login.component';
import {AuthorisationGuard} from '@app/_helpers/authorisation.guard'
import {AuthenticationService} from "@app/_services/authentication.service";

const routes: Routes = [
  {
    path: 'home',
    component: HomeComponent,
    canActivate: [AuthorisationGuard]
  },
  {
    path: '',
    redirectTo: '/home',
    pathMatch: 'full',
    canActivate: [AuthorisationGuard]
  },
  {
    path: 'login',
    component: LoginComponent
  },
  // 404 pages redirect to home
  { path: '**', redirectTo: '/home' }
];


@NgModule({
  declarations: [],
  imports: [
    CommonModule,
    RouterModule.forRoot(routes)
  ],
  providers: [AuthorisationGuard, AuthenticationService],
  exports: [
    RouterModule
  ]
})
export class RoutingModule {
}

Here my login.component.ts :


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 '@app/_services/authentication.service';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
  loginForm: FormGroup;
  loading = true;
  submitted = false;
  returnUrl: string;
  error: string;
  txt = {
    title: 'Connexion',
    subtitle: 'Veuillez saisir vos identifiants'
  };
  constructor(
    private formBuilder: FormBuilder,
    private route: ActivatedRoute,
    private router: Router,
    private authenticationService: AuthenticationService
  ) {
    // redirect to home if already logged in
    if (this.authenticationService.trainingUserValue) {
      this.router.navigate(['/']);
    }
  }

  ngOnInit(): void {
    this.loginForm = this.formBuilder.group({
      username: ['', [Validators.required, Validators.email]],
      password: ['', Validators.required]
    });
    // get return url from route parameters or default to '/'
    this.loading = false;
  }

  onSubmit() {
    this.submitted = true;

    // stop here if form is invalid
    if (this.loginForm.invalid) {
      return;
    }
    const data = new FormData();
    data.append('Email', this.formBuilder.control(['username']).value);
    data.append('Password', this.formBuilder.control(['password']).value);

    this.loading = true;
    this.authenticationService.login(data)
      .pipe(first())
      .subscribe(
        response => {
          this.loading = false;
          if (response.statusCode === 401) {
            this.error = response.message;
          } else {
            const user = {
              username : this.formBuilder.control(['username']).value,
            };
            localStorage.setItem('trainingUser', JSON.stringify(user));

            this.authenticationService.setTrainingUser(user);
            //this.router.navigate(['/home']);
            window.location.reload();
          }
        },
        error => {
          this.error = error.message;
          this.loading = false;
        }
      );
  }
}

Somebody could explain me what i misunderstand ?



Solution 1:[1]

As explain in the comment by @angularQuestions trainingUser in authorisation.guard is never null as the authentication.service return an object if the user does not exist in the LocalStorage :

this.trainingUserSubject = new BehaviorSubject<any>(JSON.parse(localStorage.getItem('trainingUser') || '{}'));

One solution is just to test a property of the object rather than testing the object himself :

if (trainingUser.username) {
  // authorized so return true
  return true;
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Patrick Pierra