0

I'm trying to work with Auth guards in angular. I have a httpcall that sets a true/false value based on a response back from an HTTP call. The problems are: 1) httpClients return an observable 2) the httpClient subscription needs to happened before the Authorized method gets called, that way the hasSessionFlag is already set.

DUAL SERVICE .TS

hasSession() {
    return this.http.get<{}>('API CALL', {
      withCredentials: true,
      observe: 'response'
    }).subscribe((res=>{
      if(res.status === 200) {
        this.hasSessionFlag = true;
      } else {
        this.hasSessionFlag = false
      }
    }));
  }

//Check if all the logical conditions of the user sessions pass*
  isAuthorized(): boolean {
      if (this.hasSessionFlag) {
        this.authService.login();
      } else {
        this.dualLoginRedirect();
      }
  }

  canActivate(): boolean {
    return this.isAuthorized();
  }

ROUTER.TS

   {
      canActivate: [DualLogonService],
      path: 'test',
      component: TestPageComponent
    }
Patricio Vargas
  • 5,236
  • 11
  • 49
  • 100
  • You are doing a lot of imperative style of coding, you can quite easily achieve by applying a reactive style of coding. There could be a better way to achieve the same if you could share "this.isUserLoggedIn()" and "this.isInternal()" code or give an idea what it does. BTW, canActivate return type could be an Observable, so you can also compose your httpClient observable and project it to return boolean and finally return that observable from canActivate. – user2216584 Jun 24 '19 at 22:04
  • @user2216584 I updated my questions. I simplyfied it, but then how can the auth guard will subcribe to the canActivate? – Patricio Vargas Jun 24 '19 at 22:21
  • 1
    please see my answer. Notice, my assumptions. If my assumption is not correct let me know the correct assumption. I will update the code accordingly. – user2216584 Jun 24 '19 at 22:47

2 Answers2

1

Instead of doing a subscribe on your http call, you can do a tap and set the value for hasSessionFlag....so do this..

hasSession() {
    return this.http.get<{}>('API CALL', {
      withCredentials: true,
      observe: 'response'
    }).pipe(
      tap(res=>{
      if(res.status === 200) {
        this.hasSessionFlag = true;
      } else {
        this.hasSessionFlag = false
      }
    }))
  .switchMap(res=>{
      if(this.hasSessionFlag)return this.authService.login();
      else return this.dualLoginRedirect();
}));
  }

isAuthorized(): boolean {
      if (this.hasSessionFlag) {
        this.authService.login();
      } else {
        this.dualLoginRedirect();
      }
  }

assuming your authService.login() and dualLoginRedirect() are observables

Ajay Reddy
  • 1,475
  • 1
  • 16
  • 20
1

As per your latest code, I think this is what you want to do:

If API returns status is 200 then you want to call "this.authService.login();" otherwise "this.dualLoginRedirect();"

[As per your code at the time of writing this answer - Your isAuthorized() method does not return boolean instead it returns undefined. It must return a boolean or an observable of boolean or promise. I am assuming that in case of status is 200 then it will return true otherwise it will return false]. With this assumption, you can have only canActive() method like this:

canActive(): Observable<boolean> {
    return this.http.get<{}>('API CALL', {
              withCredentials: true,
              observe: 'response'
            })
            .pipe(
              map(res => {
                if(res.status === 200) {
                  //OR DO here whaterevr you want to do
                  this.authService.login();
                  //return true will render the associated component
                  //i.e. pass the guard
                  return true;
                } else {
                  this.dualLoginRedirect();
                  //return false will fail the guard.
                  return false;
                }
              })
            );
  }

Your guard service will automatically subscribe to the returned observable of canActivate() method.

Also, notice that in this approach there is no class members are declared. Although if your guard needs it just declare them and use them as per your requirements.

user2216584
  • 5,387
  • 3
  • 22
  • 29
  • very cool, question....so what I understood is that my auth guard service is smart enough to subscribe to my observable when i got to "x" route that has the canActive? – Patricio Vargas Jun 24 '19 at 22:55
  • @PatricioVargas Yes! You are absolutely correct, angular guard's canActiavte() should return Observable | Promise | boolean | UrlTree inorder to pass/fail the guard. If it returns an Observable then it automatically subscribes to unwrap the boolean. As a developer, you need not do anything. Refer: https://angular.io/api/router/CanActivate – user2216584 Jun 24 '19 at 23:00
  • this works, but I have a questions. When activate returns false, it takes me to the page B, then takes me back to the page A, where I was. I shouldn't take me to page B if it's false. Is it because it's waiting on the observable to complete? – Patricio Vargas Jun 24 '19 at 23:53
  • @PatricioVargas - I am not aware of what `this.authService.login()` and `this.dualLoginRedirect();` does. But guard has only one **RESPONSIBILITY** - Can user route to the requested url? If your guard logic returns false it means you must route the user somewhere [i.e. A page which informs the user that you are not allowed to access this page OR Login Page]. If true then your component will be rendered in the respective router-outlet. From your comment, it appears that `this.dualLoginRedirect();` the method is having logic which navigates the app to page B. continue in next comment.... – user2216584 Jun 25 '19 at 00:03
  • Returning false means request path cannot be accessed. It is **NOT** related to if it was waiting on the observable to complete. It is related to what `this.dualLoginRedirect();` method does. – user2216584 Jun 25 '19 at 00:05
  • I removed ```this.dualLoginRedirect();``` because i thought that was the issue and actually don't need it anymore, but keeps giving me the issue. Let me check if commenting ```this.authService.login() ``` works – Patricio Vargas Jun 25 '19 at 00:06