'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 |
