ERROR Error: Uncaught (in promise): Error: Invalid CanActivate guard Error: Invalid CanActivate guard

654 views Asked by At

so i want to implement auth guard to redirect user to login page if he is not logged in yet.

For example , when am in login page , when i type "/dashboard" in my url , i want the user to be redirected to login in case he is not authenticated , but it's not working , it just stays at the login page no matter what , and even if i'm authenticated it doesn't go to dashboard page anymore.

I'm really stuck and it's frustrating because whatever i did it just doesn't work, there must be something wrong that i haven't noticed.

I really need help to solve this , thanks in advance !

This is my token storage service :

        logout_user() : void{
        localStorage.clear();
        this.isAuthenticate = false;
        this.router.navigate(['login']);
      }
    
      public saveToken(token : string) : void {
        localStorage.removeItem(TOKEN_KEY);
        localStorage.setItem(TOKEN_KEY,token);
      }
    
      public getToken() : string | null {
        return localStorage.getItem(TOKEN_KEY);
      }
    
      public saveUser(user:any) : void {
        localStorage.removeItem(USER_KEY);
        localStorage.setItem(USER_KEY,JSON.stringify(user));
      }
    
      public getUser() : any {
        const user = localStorage.getItem(USER_KEY);
        if(user){
          this.isAuthenticate = true;
          return JSON.parse(user);
        }
        return this.isAuthenticate;
      }

This is my auth guard page

        canActivate(
        next: ActivatedRouteSnapshot,
        state: RouterStateSnapshot
      ): Observable<boolean> | Promise<boolean> | boolean {
        const isLoggedin = this.tokenService.getToken();
        if(!isLoggedin){
          this.router.navigate(['/login']);
          console.log(isLoggedin);
          alert("You are not logged in yet");
          return false;
        }
        return true;
    }

This is my login component

        ngOnInit(): void {
         if(this.tokenStorage.getToken()){
           this.isLoggedIn = true;
           this.router.navigate(['dashboard']);
           this.roles = this.tokenStorage.getUser().roles;
           console.log(this.tokenStorage.getToken());
           console.log(this.tokenStorage.getUser());
          }
        
      }
    
      onSubmit() : void {
        const {username , password} = this.form;
        this.authService.login_user(username,password).subscribe(
          data_user => {
            this.tokenStorage.saveToken(data_user.accessToken);
            this.tokenStorage.saveUser(data_user);
            this.isLoggedIn = true;
            this.roles = this.tokenStorage.getUser().roles;
            this.router.navigate(['dashboard']);
          },
          error_login => {
            this.errorMessage = error_login.error.message;
            this.isLoginFailed = true;
            console.log(this.errorMessage);
            return this.isLoggedIn;
          }
        );
      }

These are my routes

        export const AdminLayoutRoutes: Routes = [
        { path: 'dashboard',          component: DashboardComponent,  canActivate : [AuthGuard]},
        { path: 'user-profile',       component: UserProfileComponent},
        { path: 'table-list',         component: TableListComponent },
        { path: 'typography',         component: TypographyComponent },
        { path: 'icons',              component: IconsComponent },
        { path: 'maps',               component: MapsComponent },
        { path: 'notifications',      component: NotificationsComponent },
        { path: 'upgrade',            component: UpgradeComponent },
        { path: 'jobDetail',          component: JobDetailComponent },
        { path: 'candidateProfile',   component: CandidateProfileComponent },
        { path: 'register',           component: RegisterComponent },
        { path: 'my-profile',         component: MyprofileComponent },
        { path: 'userDetail/:id',     component: UserDetailsComponent }
    ];

These are my app module routes

     const routes: Routes =[
      {
        path: '',
        component : LoginComponent
      },
       {
        path: '',
        component: AdminLayoutComponent,
        children: [{
          path: '',
          loadChildren: () => import('./layouts/admin-layout/admin-layout.module').then(m => m.AdminLayoutModule)
        }]
      },
      {
        path : 'login',
        component: LoginComponent
      }
    ];

This is my auth Service :

   import { HttpClient,HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { User } from 'app/models/user.model';
import { Router } from '@angular/router';
import { TokenRegistrationService } from './token-registration.service';

const AUTH_API = 'http://localhost:8080/api/auth/';
const httpOptions = {
  headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};

export interface SignUpData {
  username: string;
  first_name : string;
  last_name : string;
  profession : string;
  email: string;
  password: string;
  roles: string[];
}




@Injectable({
  providedIn: 'root'
})
export class AuthenticationService {
  
  private userSubject: BehaviorSubject<User>;
  public user: Observable<any>;
  isAuthenticate = false;


  
  constructor(private http : HttpClient,private router : Router,private tokenService : TokenRegistrationService) {
       
   }

 
  

  //Login service for user authentication , sends data inside observable through the AUTH_API/signin and if it matches existing user , authenticates
  login_user(username: string, password: string): Observable<any> {
    return this.http.post(AUTH_API + 'signin', {
      username,
      password,
    }, httpOptions);
    
  }


  //Signup service for user account creation , sends data inside observable throught the AUTH_API/signup and if data is valid , creates account
  register_user(user : SignUpData) : Observable<any>{
    return this.http.post(AUTH_API + 'signup',user,httpOptions);
  }



}

successful and unseccessful handling :

     <div
                    class="alert alert-danger"
                    role="alert"
                    *ngIf="f.submitted && isLoginFailed"
                    >
                    Login failed: {{ errorMessage }}
                    </div>
                <button [disabled]="loading" class="btn btn-primary">
                    <span *ngIf="loading" class="spinner-border spinner-border-sm mr-1"></span>
                    Login
                </button>
                <div *ngIf="errorMessage" class="alert alert-danger mt-3 mb-0">{{errorMessage}}</div>
                </form>
    </div>
</div>
<div class="alert alert-success" *ngIf="isLoggedIn">
    Logged in as {{ roles }}.
  </div>

This shows alert-success if i loging and error message if not

Ths is my data after login successful :

{id: 46, username: 'azizos', first_name: 'azizoo', last_name: 'azz', profession: 'sezer', …}
accessToken: "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJheml6b3MiLCJpYXQiOjE2NDk4ODc5NzksImV4cCI6MTY0OTk3NDM3OX0.2GivL_V7ZnRzF4otPX-KZ20qe9pS-W1BOJmFmeQoNnhN7o_82PUvKmDnJEeumRu0dRWmSE46uGfxmqvo7feTxQ"
email: "azizos.gmail@com"
first_name: "azizoo"
id: 46
last_name: "azz"
profession: "sezer"
roles: ['ROLE_HR_MANAGER']
tokenType: "Bearer"
username: "azizos"
1

There are 1 answers

12
Chris Hamilton On

Ah yes the classic pathMatch: 'full' oversight.

const routes: Routes =[
      {
        path: '',
        pathMatch: 'full'
        component : LoginComponent,
      },
      ...

Without that, all routes will always match the empty string, because the default match strategy is 'prefix' and an empty path is a prefix of all other URLs.

From the docs: https://angular.io/api/router/Route

The path-match strategy 'full' matches against the entire URL. It is important to do this when redirecting empty-path routes. Otherwise, because an empty path is a prefix of any URL, the router would apply the redirect even when navigating to the redirect destination, creating an endless loop.