angularpromiseobservableangular-router-guardscanactivate

Return boolean observable on angular guard


I would like to block URL access from unauthorized users. I'm currently using canActivate with route Guard from angular to block users to access /my/specific_url_route.

Indeed, I want to block some users (not listed in a list on my database) from accessing the resource by typing the matching URL (like: mydomain/legalForm/123ytfdhs653HG). To do that, first of all, I already block the access from my user view (OK) but the unauthorized users can still access the resource (page) by typing the correct URL directly (the subject).

My code is correct (return false and true as expected on my service) but my guard

canActivate()

is executed and returns a value before my function has been completed. I know that's an issue relative to promise/observable. I need to edit my 2 functions canActivate() and isAuthorizedToAccessForms0(), but I don't know how to expect the correct behavior.

I know that pipe, map or then should be added after my called to function in order to wait for the result in my guard, here are related subject 1 subject 2 subject 3

My service:

  public isAuthorizedToAccessForms0(id): boolean{ 
    var isFound = false;
    
    //query
    this.fire.collection('solutions').where(firebase.firestore.FieldPath.documentId(), '==', id).get().then(val => {
      val.forEach(v => {
        this.emailsAuthorizedVeolia = v.data().emailVeoliaAuthorized; //get each user
      })
    }).then(res => {
      //Check if a user is present in the list (whenever his position is in the list)
      for (let i = 0; i <= this.emailsAuthorizedVeolia.length-1 ; i++) {
        if(this.authService.currentUserEmail === this.emailsAuthorizedVeolia[i]) {
            isFound = true
          } else {
            //isFound = false by default
          }
      }
    }).then(final => {
      if (isFound === false) { //no occurrence finded
        console.log("DENIED!")
        isFound = false
        return false
      } else { //user finded
        console.log("GRANTED!")
        isFound = true
        return true
      }
    });
    
    return isFound;
  }

My guard:

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
    const myId = route.paramMap.get('id')
    
    try{
        const authorized = this.formAllServ.isAuthorizedToAccessForms(myId) //HERE I need to add .pipe ? .then ? .map ?
        
        if(authorized === false) {
            console.log("FALSE")
            alert('You are not allowed to view this page');
            this.router.navigate(['/homeUser']);
            return false
        }
        else {
            console.log("TRUE")
            alert('Access granted !');
            return true
        }
    } catch(err) {
        return false;
    }  
    
}

Solution

  • The problem lies in the isAuthorizedToAccessForms0 method. You return the isFound variable without waiting for end of the promise. Accordingly, the result of this function will always be false.

    You can update code of isAuthorizedToAccessForms0 like that:

     public isAuthorizedToAccessForms0(id): Observable<boolean> { 
        
        return new Observable<boolean>(observer => {
          this.fire.collection('solutions').where(firebase.firestore.FieldPath.documentId(), '==', id).get().then(val => {
            val.forEach(v => {
              this.emailsAuthorizedVeolia = v.data().emailVeoliaAuthorized; //get each user
            })
          }).then(res => {
            //Check if user is present in the list (whenever his position in the list)
            for (let i = 0; i <= this.emailsAuthorizedVeolia.length-1 ; i++) {
              if(this.authService.currentUserEmail === this.emailsAuthorizedVeolia[i]) {
                  console.log('GRANTED!');
                  observer.next(true);
                  observer.complete();
                  break;
                } else {
                  observer.next(false);
                  observer.complete();
                  console.log('DENIED!');
                  break;
                  //isFound = false by default
                }
            }
          })
        })
      }
    

    After that you should update the guard

      canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
        const myId = route.paramMap.get('id')
        
        try{
           return this.formAllServ.isAuthorizedToAccessForms(myId).pipe(
            tap((authorized) => {
     
              if(authorized === false) {
                console.log("FALSE")
                alert('You are not allowed to view this page');
                this.router.navigate(['/homeUser']);
            }
            else {
                console.log("TRUE")
                alert('Access granted !');
            }
            })
           )
           
        } catch(err) {
            return of(false);
        }  
        
    }
    

    I hope this helps you