13

I have successfully implement AuthGuardService which restrict access to a protected route if user is not logged in.

What I am trying to achieve is, if user is already logged in and accessed the Login Route, I want it to redirect to another route like homepage.

Daniel Chikaka
  • 1,432
  • 4
  • 17
  • 28
  • 1
    I would make another canActivate guard for the login route which redirects to the homepage if the user is already logged in. – LLai Nov 02 '17 at 14:26
  • It's essentially what Boulboulouboule posted. if they are logged in, navigate to home page (& return false) else return true so they navigate to login page. Then attach this guard to the login route in your route definitions – LLai Nov 02 '17 at 15:41
  • I tried, it does't work. Blank page is shown instead @LLai – Daniel Chikaka Nov 02 '17 at 16:00
  • Is the guard redirecting correctly? So you are navigated to `/home`? (or whatever your home route is) – LLai Nov 02 '17 at 16:02
  • Here is what i did https://pastebin.com/5G9yiBi3 and in app.module.ts file i wrote something like this: { path: '', component: LoginComponent, pathMatch: 'full', canActivate: [AnonymousGuard] }, @LLai – Daniel Chikaka Nov 02 '17 at 16:09
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/158087/discussion-between-llai-and-daniel-chikaka). – LLai Nov 02 '17 at 16:10

4 Answers4

20

You can create two CanActivate guard:
- For restricting routes for already loggedIn users (eg: /login, /register etc)
- For restricting routes for not loggedIn users (eg: /dashboard)

Auth Service

loggedIn() {
    //return boolean for loggedIn user logic
}

Guard for not loggedIn Users

import { Injectable } from "@angular/core";
import { CanActivate, Router } from "@angular/router";
import { AuthService } from './auth.service';

@Injectable()
export class AuthGuard implements CanActivate {

    constructor(private _authService: AuthService, private _router: Router) { }

    canActivate(): boolean {
        if (this._authService.loggedIn()) {
            return true;
        } else {
            this._router.navigate(['/login'])
            return false
        }
    }
}

Guard for loggedIn Users

import { Injectable } from "@angular/core";
import { CanActivate, Router } from "@angular/router";
import { AuthService } from './auth.service';

@Injectable()
export class LoggedInAuthGuard implements CanActivate {

    constructor(private _authService: AuthService, private _router: Router) { }

    canActivate(): boolean {
        if (this._authService.loggedIn()) {
            this._router.navigate(['/dashboard'])
            return false
        } else {
            return true
        }
    }
}

Register AuthGuard in App Module

...
providers:[AuthGuard,LoggedInAuthGuard]
...

Add AuthGuard in Routing Module

const routes: Route[] = [
  { path: "/login", component: LoginComponent, canActivate:[LoggedInAuthGuard] },
  { path: "/dashboard, component: DashboardComponent, canActivate: [AuthGuard]}
]
Priyanka Jalan
  • 301
  • 2
  • 3
  • AuthService has the authentication api calls such as `http.post('/login')` `loggedIn()` method for AuthService will check the if user is authenticated. It will return boolean true or false based on the state of user. – Priyanka Jalan Mar 21 '20 at 20:27
7

You could perform a simple check in the ngOnInit of the login component like so and redirect to another page of your choice if they are authenticated already:

ngOnInit() {
   if (this._authService.isLoggedIn) {
      this._router.navigate(['/apps']);
   }
}

That worked for me!

Daniel Chikaka
  • 1,432
  • 4
  • 17
  • 28
  • 2
    I did the same but the problem is login component loads for like a second or so. So it shows the login html for the same time then it redirects me to intended route. – prit.patel Aug 20 '18 at 09:24
  • Make it log the user out when the login component is loaded. Only load the login component from a route guard (implementing CanActivate), with a return url in the query parameters. – Dico Apr 03 '19 at 04:25
  • As @prit.patel mentioned already, I believe this is not a proper solution, as it first allows user to load and see the login component, before the redirect. It's better to use CanActivate in this case. – Server Khalilov Jul 08 '19 at 12:35
  • Another way to make this work is to have an else block that flips a boolean to true, then in your html, wrap the whole page in an ngIf. You'll no longer have it visible for a second, and you wont have to write a nearly duplicative guard. – Jeff Oct 21 '20 at 18:40
3

You can use the CanActivate guard on paths that requires user to be logged in:

const ROUTER: Routes = [
  {path: 'restricted', component: MyRestrictedCOmponent, canActivate:[LoginActivate]},
  {path: 'home', component: HomeComponent},
];

And the guard that redirect unlogged users on the homepage :

@Injectable()
export class LoginActivate implements CanActivate {
  constructor(private authService: AuthService, private router: Router) {}
  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<boolean>|Promise<boolean>|boolean {
    if (!authService.isLoggedIn()) {
      this.router.navigate(['home']);
    }
    return true;
  }
}
Boulboulouboule
  • 4,087
  • 1
  • 13
  • 29
  • It doesn't work. All i get is a blank page for "home" route when the user is logged in – Daniel Chikaka Nov 02 '17 at 15:34
  • Did you have some errors ? Here's the official documentation about guards, take a look to understand how to implement it in your project : https://angular.io/guide/router#guards – Boulboulouboule Nov 03 '17 at 06:56
  • 1
    No errors, it just refuse to work. Never mind, i have found another way to do it will post as answer to help others with similar problem @Boulboulouboule – Daniel Chikaka Nov 03 '17 at 08:02
  • 1
    Don't forget to register your guard as a provider in your app configuration, i.e.: @NgModule({ declarations: [ ], imports: [ ], providers: [ LoginActivate] }) – Lucaci Andrei Jan 23 '18 at 14:03
2

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 !

Reinier Garcia
  • 1,002
  • 1
  • 11
  • 19