Here is my AuthGuard, which works perfectly with logged or not logged users. I'm using this single guard for all my routes. I'm using Angular 13.
STEP # 1: Create a Guard
Example # 1: Using NgRx instead of services.
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree } from '@angular/router';
import { Observable, take } from 'rxjs';
import { User } from "../../models/user/user.model";
import { map } from "rxjs/operators";
import { Store } from "@ngrx/store";
import * as fromAppReducer from "../../../../store/app.reducer";
import * as fromAuthReducer from "src/app/modules/auth/store/auth.reducer";
@Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(private store: Store<fromAppReducer.AppState>, private router: Router ) {
}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
return this.store.select('auth').pipe(
take(1),
map((authState: fromAuthReducer.AuthState) => authState.currentUser),
map((currentUser: User) => {
if (currentUser) {
return (state.url === '/auth' ? this.router.createUrlTree(['recipes']) : true);
} else {
return (state.url === '/auth' ? true : this.router.createUrlTree(['auth']));
}
})
);
}
}
Example # 2: Using services.
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree } from '@angular/router';
import { Observable, take } from 'rxjs';
import { AuthService } from "../../services/auth/auth.service";
import { User } from "../../models/user/user.model";
import { map } from "rxjs/operators";
import { Store } from "@ngrx/store";
import * as fromAppReducer from "../../../../store/app.reducer";
@Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {
}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
return this.authService.currentUserSubject.pipe(
take(1),
map((user: User) => {
if (user) {
return (state.url === '/auth' ? this.router.createUrlTree(['recipes']) : true);
} else {
return (state.url === '/auth' ? true : this.router.createUrlTree(['auth']));
}
})
);
}
}
The idea is basically the same in both cases. What only changes is just where is gonna be stored the related data (the logged user, which is a User object), either on a single instance service or in the NgRx store.
But in both cases this is the logic:
A) If the user is logged in:
If the request route is '/auth' (login in your case), then we will redirect into recipes (the homepage in your case). We don't want an already logged user to visit the auth page. But if the requested routes is not '/auth', then we will allow to proceed with the route navigation, because there is not harm in doing so.
B) If the user is not logged in:
It's more or less the opposite than A.
If the request route is '/auth' (login in your case), then we will allow to proceed with the route navigation into the AuthComponent. That way the user has a chance to sign in or log in, whenever he/she decides to do so. But if the requested route is not '/auth', then we will redirect into 'auth'.
Note:
Here in B, we have to do it this way, instead of simply allowing it always or redirecting always. Because if we always allow then the user will have access to the whole app, and if we always redirect then we will have a never ending cycle, because when always redirecting the guard will catch it again and redirect again, and then redirect again,... without end. And as a consequence, you will never see your login page.
STEP # 2: Register your Guard on all the necessary route's routing module files.
On the auth route (AuthComponent):
import { RouterModule, Routes } from "@angular/router";
import { NgModule } from "@angular/core";
import { AuthComponent } from "./components/auth/auth.component";
import { AuthGuard } from "./guards/auth/auth.guard";
const authRoutesConfig: Routes = [
{path: '', canActivate: [AuthGuard], component: AuthComponent},
];
@NgModule({
declarations: [],
imports: [
RouterModule.forChild(authRoutesConfig),
],
exports: [
RouterModule
],
})
export class AuthRoutingModule {
}
For the rest of the routes (example: recipes)
import { RouterModule, Routes } from "@angular/router";
import { AuthGuard } from "../auth/guards/auth/auth.guard";
import { RecipesComponent } from "./components/recipes/recipes.component";
import { NoRecipeInfoTextComponent } from "./components/recipes/no-recipe-info-text/no-recipe-info-text.component";
import { NewRecipeComponent } from "./components/recipes/new-recipe/new-recipe.component";
import { RecipeDetailComponent } from "./components/recipes/recipe-detail/recipe-detail.component";
import { RecipesResolverService } from "./services/recipes-resolver/recipes-resolver.service";
import { NgModule } from "@angular/core";
const recipesRoutesConfig: Routes = [
{
path: '', canActivate: [AuthGuard], component: RecipesComponent, children: [
{path: '', component: NoRecipeInfoTextComponent},
{path: 'new', component: NewRecipeComponent},
{path: ':id', component: RecipeDetailComponent, resolve: [RecipesResolverService]},
{path: ':id/edit', component: NewRecipeComponent, resolve: [RecipesResolverService]},
]
},
];
@NgModule({
declarations: [],
imports: [
RouterModule.forChild(recipesRoutesConfig),
],
exports: [
RouterModule
],
})
export class RecipesRoutingModule {
}
ANGULAR ROCKS !