0

I have getNewToken and hasTheUserTokenOrPermission methods. The first one returns token and set the user data after that the second one checks after WE SET THAT user data - if he has permission to see something in the guards.

So i need to wait for the first result and after that to check if there is permission. I need to return either true or false from the canActivateGuard.

So this can be solved on this way with the promise pattern

import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot, Router } from '@angular/router';
import { AuthStoreService } from '@core/services/auth-store.service';

import { Observable, of, Subject } from 'rxjs';
import { catchError, map, tap } from 'rxjs/operators';
import { LoginService } from 'src/api/controllers/Login';
import { OAuthService } from 'angular-oauth2-oidc';




/**
 * HasPermissionGuard - Used for checking the user permission for the core routing
 * in order to check if the user has permission to see the page
 */
@Injectable()
export class HasPermissionGuard implements CanActivate {

    constructor(
        private loginService: LoginService,
        private oauthService: OAuthService,
        public authStore: AuthStoreService,
        private router: Router
    ) { }

    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<any> | Observable<any> {
        return this.getNewToken().toPromise().then(data => {
            return this.hasTheUserTokenOrPermission(route);
        }).then((shouldWeProvideAccess: boolean) => {
            console.log('shouldWeProvideAccess', shouldWeProvideAccess);
            if (shouldWeProvideAccess) {
                return shouldWeProvideAccess;
            } else {
                this.router.navigate(['/403']);
                return shouldWeProvideAccess;
            }
        })
    }

    getNewToken(): Observable<any> {
        return this.loginService.token({ token: this.oauthService.getAccessToken() }).pipe(
            tap(response => {
                this.authStore.setData(response);
            },
            ), catchError(err => {
                this.router.navigate(['/405']);
                return Observable.throw(err);
            }));
    }

    hasTheUserTokenOrPermission(route): boolean {
        if (!this.authStore.hasUserInfo()) {
            return true;
        }
        if (this.authStore.hasPermission(route.data.permission)) {
            return true;
        }
        return false;
    }
}

but when i try to solve this on observable pattern i have problems

return this.getNewToken().subscribe(data => {
            let shouldWeProvideAccess = this.hasTheUserTokenOrPermission(route);
            console.log('shouldWeProvideAccess', shouldWeProvideAccess);
            if (shouldWeProvideAccess) {
                return shouldWeProvideAccess;
            } else {
                this.router.navigate(['/403']);
                return shouldWeProvideAccess;
            }
        })

i get

Type 'Subscription' is not assignable to type 'Promise<any> | Observable<any>'. error.

How can i return result from here ?

sdsd
  • 447
  • 3
  • 20

2 Answers2

0

You don't return values inside a subscription. So modify the following

hasTheUserTokenOrPermission(route): boolean {
        let shouldWeProvideAccess = false;
        if (!this.authStore.hasUserInfo()) {
            shouldWeProvideAccess = true;
        }
        if (this.authStore.hasPermission(route.data.permission)) {
            shouldWeProvideAccess = true;
        }
        console.log('shouldWeProvideAccess', shouldWeProvideAccess);
        if (shouldWeProvideAccess === false) {
          this.router.navigate(['/403']);
        }
        return shouldWeProvideAccess;
    }

And then

return this.getNewToken().subscribe(data => {
            this.hasTheUserTokenOrPermission(route);
        })
Panagiotis Bougioukos
  • 15,955
  • 2
  • 30
  • 47
0

When you call .subscribe(), you return a subscription. As the error suggests, you need to return Observable, not Subscription.

So, do not subscribe :-)

You can use the map operator to transform your getNewToken() call to an Observable<boolean>:

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
    return this.getNewToken().pipe(
        map(() => {
            const allow = this.hasTheUserTokenOrPermission(route);
        
            if (!allow) {
                this.router.navigate(['/403']);
            }

            return allow;
        })
    )
};
BizzyBob
  • 12,309
  • 4
  • 27
  • 51
  • I know that i can return directly - i asked how can i return result from subscribe – sdsd Apr 14 '21 at 11:16
  • For route guards, it is not appropriate to subscribe. When using observable pattern, you simply return observable that emits a Boolean. The framework internally subscribes when the route guard is used. The reason for your error is that the guard must return observable or promise, but by calling subscribe, you return a subscription. – BizzyBob Apr 14 '21 at 11:47