Angular 4 Неявный обратный вызов и защита маршрутизатора

Каков наилучший способ обработки неявного потока Callback в Angular 4? Я хочу, чтобы Guard подождал, пока пользователь будет перенаправлен обратно с токеном, и он будет сохранен до того, как Guard вернет true или false. Я получаю маршрут Access Denied в течение нескольких секунд, прежде чем перенаправлять обратно, чтобы проверить токен. Есть ли лучший способ обработать AuthGuard, чем то, что я делаю, поэтому я не получаю отказ в доступе до завершения проверки подлинности?

Как заставить сторожа маршрутизатора ждать перенаправления?

AppComponent

    ngOnInit() {    

         //if there is a hash then the user is being redirected from the AuthServer with url params
         if (window.location.hash && !this.authService.isUserLoggedIn()) {     

          //check the url hash
          this.authService.authorizeCallback();

        }
        else if (!this.authService.isUserLoggedIn()) {         

          //try to authorize user if they aren't login
          this.authService.tryAuthorize();       

  }    
}

AuthSerivce

tryAuthorize() {
       //redirect to open id connect /authorize endpoint
        window.location.href = this.authConfigService.getSignInEndpoint();
    }

    authorizeCallback() {       

        let hash = window.location.hash.substr(1);

        let result: any = hash.split('&').reduce(function (result: any, item: string) {
            let parts = item.split('=');
            result[parts[0]] = parts[1];
            return result;
        }, {});


        if (result.error && result.error == 'access_denied') {
            this.navigationService.AccessDenied();
        }
        else {

            this.validateToken(result);
        }
    }


    isUserLoggedIn(): boolean {       
        let token = this.getAccessToken();

        //check if there is a token      
        if(token === undefined || token === null || token.trim() === '' )
        {
            //no token or token is expired;
            return false;
        }

        return true;
    }


    getAccessToken(): string {              

        let token = <string>this.storageService.get(this.accessTokenKey);


        if(token === undefined || token === null || token.trim() === '' )
        {
            return '';
        }

        return token;
    }

    resetAuthToken() {
        this.storageService.store(this.accessTokenKey, '');
    }

    validateToken(tokenResults: any) {        

        //TODO: add other validations         

        //reset the token
        this.resetAuthToken();

        if (tokenResults && tokenResults.access_token) {

            //store the token
            this.storageService.store(this.accessTokenKey, tokenResults.access_token);

            //navigate to clear the query string parameters
            this.navigationService.Home();

        }
        else {
            //navigate to Access Denied
            this.navigationService.AccessDenied();
        }

    }
}

AuthGuard

 canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot){

    var hasAccess = this.authService.isUserLoggedIn();        

    if(!hasAccess)
    {
        this.naviationService.AccessDenied();
        return false;
    }
     return true;    
  }

Ответ 1

Если вы хотите, чтобы ваш охранник подождал асинхронную задачу, вам нужно изменить свой AuthService, чтобы вернуть наблюдаемое и испускающее значение, которое вам нужно в асинхронной задаче, которую вы хотите подождать, в вашем случае ее reduce(). После этого сделайте подписку в охраннике. Таким образом, вы можете заставить своего охранника ждать какой-либо асинхронной задачи.

AuthGuard

canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot){

  this.authService.isUserLoggedIn().map(logged => {
        if (logged) {
            return true;
        } else {
            this.naviationService.AccessDenied();
            return false;
        }
    }).catch(() => {
        this.naviationService.AccessDenied();
        return Observable.of(false);
    });
  }
}

часть AuthService

 isUserLoggedIn(): Observable<boolean> {
    return new Observable((observer) => {
      let hash = window.location.hash.substr(1);

      let result: any = hash.split('&').reduce(function (result: any, item: string) {
        let parts = item.split('=');
        result[parts[0]] = parts[1];

        if (result.error && result.error == 'access_denied') {
          observer.next(false);
          observer.complete();
        }
        else {
          this.validateToken(result);
        }
        let token = this.getAccessToken();

        //check if there is a token      
        if(token === undefined || token === null || token.trim() === '' )
        {
          //no token or token is expired;
          observer.next(false);
          observer.complete();
        }

        observer.next(true);
        observer.complete();
      }, {});
    });
} 

Ответ 2

Метод CanActivate может вернуть Observable, Promise или Boolean, а Angular будет знать, чтобы развернуть его и обработать все async. Вы можете изменить свой код, чтобы проверить необходимые данные, прежде чем возвращать завершенный/неудачный Observable или разрешенный/отклоненный Promise к маршрутизатору Angular и вызывая this.naviationService.AccessDenied() в результате этой асинхронной функции.