0

I have an AuthGuard which I want to check my authentication status in my App.

Simplified it should work like this:

  • if there is an access_cookie, return true
  • if there is no access_cookie and no refresh_cookie, return false
  • if there is no access_cookie, but a refresh_cookie, request a new access_cookie and on success, return true

However, returning plain true or false works of course, but the request for a new access_cookie is an Observable<boolean>.

My AuthGuard:

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
    ...
    const result = this.auth.checkCredentials(scopes);
    console.log(result);
    ....
}

The checkCredentials() (simplified):

checkCredentials(): boolean{
    if (!this.cookieService.check('access_token')) {
        if(this._getRefreshToken(){
            try {
              this.refresh().subscribe(
                result=> {
                  return result;
                },
                error => {
                  return false;
                });
            } catch (e) {
              return false;
            }
       }
    }

    return true;
 }

The refresh Obeservable

refresh(): Observable<boolean> {

    if (this._getRefreshToken() && !this.cookieService.check('access_token')) {
      const oauth2_token_endpoint = TOKEN_URL;
      const headers = new HttpHeaders({
        'Content-Type': 'application/x-www-form-urlencoded',
        'Authorization': 'Basic ' + btoa(CLIENT_ID + ':' + CLIENT_SECRET),
      });
      const body = 'grant_type=' + GrantType.REFRESH + '&refresh_token=' + this._getRefreshToken();

      return this.http.post<UserData>(oauth2_token_endpoint, body, {headers})
        .pipe(map(user => {
          if (user && user.access_token) {
            this._saveToken(user);
            return true;
          }
          return false;
        }));
    }

    return throwError('Refresh Token Error');
  }

The checkCredentials() gets executed, but it does not wait for the refresh() but instead returns undefined immediately.

Where am I missing something?

I am using Angular 8.2.14 and rxjs 6.4.0

PrimuS
  • 2,505
  • 6
  • 33
  • 66
  • 1
    Just return `this.refresh()` or use `map` operator to turn it into `Boolean` – martin Dec 04 '19 at 14:09
  • Does this answer your question? [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – jonrsharpe Dec 04 '19 at 14:11
  • 1
    In the `canActivate` method, you can return a `boolean` as you said, but you can also return an `Observable`, which makes things really easy. Just return `refresh()` and map it so it that that observable returns a boolean. – Celsiuss Dec 04 '19 at 14:13

1 Answers1

2

Your checkCredentials is not returning a result inside the nested if block, it also subscribes to the observable instead of returning it. Use map instead of subscribe and return the Obserable. You should also change the signature on the method for the return type so that it returns either boolean or Observable<boolean>.

checkCredentials(): boolean | Observable<boolean> {
    if (!this.cookieService.check('access_token') && this._getRefreshToken()) {
      return this.refresh().pipe(map(
        result=> {
          return result;
        }, error => {
          return false;
        }));
    }
    return true;
}

Not of interest: I combined your 2 if blocks into 1.

Igor
  • 60,821
  • 10
  • 100
  • 175