简体   繁体   中英

Angular 4: Authguard(canActivate) in not blocking the url as it should, breaks the app blocking everything, no errors are thrown in console/browser

I created this authguard but is not blocking the route when the user is logged out. The app auth workflow works fine, I use traditional sessions on the db in the backend, I verified the state of the auth service with chrome using Augury extension and the auth states set ok with the different operations so the problem is not there.

This is my auth-guard.service.ts service:

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
import { AuthService } from './auth.service';
import { Observable } from 'rxjs/Observable';


@Injectable()
export class AuthGuard implements CanActivate {

  constructor(private authService: AuthService, private router: Router){}

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
     return this.authService.isAuthenticated().map(isAuth => {
       console.log('is authenticated',isAuth);
            if (isAuth) {
                return true;
            }else{
                this.router.navigate(['/signin']);
                return Observable.of(false);
            }
        }).catch(() => {
            return Observable.of(false);
        });;
  }
}

This is my authService with the method isAuthenticated() which is used by the auth guard to block or not the route(I'm using publishReplay to catch the authState for 5 times to save some requests to the db since I didn't find a better way to check for user auth state, I just do a query and If the auth guard of the backend don't reject it then it means the session is still active returning a code 200:

import { Injectable, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { Http, Response, RequestOptions, Headers } from '@angular/http';
import 'rxjs/add/operator/map';
import {Observable, Subject} from "rxjs/Rx";

@Injectable()
export class AuthService implements OnInit {
 userIsAuthenticated = new Subject();
  constructor(private router: Router, private http: Http) { }

  private getHeaders(){
      let headers = new Headers();
      headers.append('Content-Type', 'application/json');
      headers.append('Accept', 'application/json');
      headers.append('Authorization','Bearer');
      return headers;
  }

  ngOnInit(){
    //it starts as false to check with the server if the user is authenticated
    this.userIsAuthenticated.next(false);
  }

  isAuthenticated(): Observable<boolean> {

      let options = new RequestOptions({ headers: this.getHeaders(), withCredentials: true });
      return this.http.get('http://someurl/api/sponsor/check/login',options)
        .map(response => {
          let res = response.json();
          console.log("response");
          if (res.code == 200) {
            this.userIsAuthenticated.next(true);
            return true;
          } else {
            this.userIsAuthenticated.next(false);
            return false;
          }
        }
      ).publishReplay(5);

  }



}

This is my routing file :

import { NgModule }  from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { AuthGuard } from './auth/auth-guard.service';
import { SigninComponent } from './auth/signin/signin.component';
import { SignupComponent } from './auth/signup/signup.component';
import { MainComponent } from './main/main.component';
import { DashboardComponent } from './dashboard/dashboard.component';
import { ActiveCampaignsComponent } from './dashboard/active-campaigns/active-campaigns.component';
import { HistoryCampaignsComponent } from './dashboard/history-campaigns/history-campaigns.component';
import { OverviewCampaignsComponent } from './dashboard/overview-campaigns/overview-campaigns.component';
const appRoutes: Routes = [
  { path: '', redirectTo: '/', pathMatch:'full'},
  { path: '', component: MainComponent },
  { path: 'signin', component:SigninComponent},
  { path: 'signup', component: SignupComponent},
  { path: 'dashboard', canActivate:[AuthGuard],component: DashboardComponent,
    children: [
      { path: '', redirectTo:'dashboard/overview', pathMatch: 'full'},
      { path: 'overview', component: OverviewCampaignsComponent },
      { path: 'active', component: ActiveCampaignsComponent},
      { path: 'history', component: HistoryCampaignsComponent}
    ] }

]

@NgModule({
  imports: [RouterModule.forRoot(appRoutes)],
  exports: [RouterModule]
})

export class AppRoutingModule{

}

As you can see the dashboard route is implementing this auth guard but it's not blocking anything and it has an unexpected behaviour blocking all the links if I add that line(this happens I logout and try to access by url to the dashboard route, it breaks the app yet no errors are seen in console, links don't respond after doing that). What seems to be the problem? Thanks!!

i think you forgot to add your guard to Providers array in AppModule , also add canActivate after component name, you should have something similar code above.(This may be the problem why your Guard doesn't protect dashboard routes.

@NgModule({
  imports: [
    RouterModule.forRoot([
      {
        path: 'dashboard', 
        component: DashboardComponent,
        canActivate:[AuthGuard],
      }
    ])
  ],
  providers: [AuthGuard]
})
class AppModule {}

The problem was that I was not catching the error on authService on the isAuthenticated method after map, so the authGuard code didn't get executed due to the error being thrown before giving it a chance to execute, I removed the publishReplay(for caching) as well cause code didn't get executed after entering a guarded url, but I prefer to open another topic with the problem. Thanks :

authService :

isAuthenticated(): Observable<boolean> {

      let options = new RequestOptions({ headers: this.getHeaders(), withCredentials: true });
      return this.http.get('http://localhost:3000/api/check/login',options)
        .map(response => {
          let res = response.json();
          console.log("response");
          if (res.code == 200) {
            this.userIsAuthenticated.next(true);
            return true;
          }
        }
      ).catch((err)=>{
        //maybe add in the future if the code is 403 then send him to login otherwise send him elsewhere
        return Observable.of(false);
      });

  }

authGuard, I changed the return value in case of not being logged in from Observable.of(false) to just false(I don't think this was mandatory since the signature lets you return an Observable, but I just prefer to give full details of the solution to the problem:

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
     return this.authService.isAuthenticated().map(isAuth => {
       console.log('is authenticated',isAuth);
            if (isAuth) {
                return true;
            }else{
                this.router.navigate(['/signin']);
                return false;
            }
        });
  }

There were no changes on routes file compared to what I previously posted.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM