I´m trying to implement a guard to prevent user navigation to the login page once they authenticate themselves. I want to use this guard for every page component in my app except the login page itself.
Here goes the code I´m trying:
import { Injectable } from '@angular/core';
import { Router, ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot, UrlTree, CanDeactivate } from '@angular/router';
import { Observable } from 'rxjs';
import { AuthenticationService } from '../services/authentication.service';
@Injectable({ providedIn: 'root' })
export class AuthGuard<T> implements CanActivate, CanDeactivate<T> {
constructor(private router: Router, private auth: AuthenticationService) {
}
canActivate(
_route: ActivatedRouteSnapshot,
_state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
return this.auth.isAuthenticated() || this.router.navigate(['/login']);
}
canDeactivate(
_component: T,
_currentRoute: ActivatedRouteSnapshot,
_currentState: RouterStateSnapshot,
nextState: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
if (nextState && nextState.url.includes('/login')) {
return !this.auth.isAuthenticated();
}
return true;
}
}
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AuthGuard } from './guards/auth.guard';
import { HomeComponent } from './pages/home/home.component';
import { LoginComponent } from './pages/login/login.component';
const routes: Routes = [
{
path: 'login',
component: LoginComponent
},
{
path: 'home',
component: HomeComponent,
canActivate: [AuthGuard],
canDeactivate: [AuthGuard]
},
{
path: '**',
redirectTo: 'home',
}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
providers: [AuthGuard]
})
export class AppRoutingModule { }
I´ve also tried implementing a new canActive guard just for the login component, which works, but I don´t like this approach as much.
I´ve seen some examples like this one https://www.concretepage.com/angular-2/angular-candeactivate-guard-example. Using an interface, in this case, doesn´t seem right because typescript doesn´t support default method implementation. I would rather not repeat the validation on every component.
Why doesn´t my implementation get called? How should I go about doing this?
Any suggestions will be appreciated.
Aditional info: Angular v14.1.3
Sample: https://stackblitz.com/edit/angular-ivy-hvz4mc
CodePudding user response:
You have two injectable
decorators in auth guard, please find below the stackblitz working.
import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, CanDeactivate, Router, UrlTree } from '@angular/router';
import { Observable } from 'rxjs';
import { AuthenticationService } from '../services/authentication.service';
@Injectable()
// @Injectable({ providedIn: 'root' }) // <-- mistake here
export class AuthGuard implements CanActivate, CanDeactivate<any> {
constructor(private router: Router, private auth: AuthenticationService) {
}