Search code examples
javascriptangularamazon-web-servicesamazon-cognitoaws-amplify

What authentication check using the Auth API should we perform in our Angular Authentication Guards?


In this article the author uses Auth.currentAuthenticatedUser() in the AuthGuard like this:

  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    return Auth.currentAuthenticatedUser().then(() => { return true; })
      .catch(() => {
        this.router.navigate(['signin']);
        return false;
      });
  }

However as noted in this issue this can throw even though the uses is authenticated and has been redirected, resulting in two redirects. The first from Cognito and the second from the auth guard, since it does not "Think" that the user has been signed in yet.

So what should we call on Auth that will guarantee not to throw when the user is signed in via Federated Identities?

I think Auth.currentSession() will work but wanted to double check.

Update

I tried Auth.currentSession() but it also not not provide a session right after redirect.

This is what I tried in AppComponent:

    Auth.currentSession().
      then((s) => console.log(`The current session is ${JSON.stringify(s)}`)).
      catch((e) => console.log(`There is no current session ${e}`))



After redirect when the application loads this is what is logged:

There is no current session No current user

If I manually refreshed then it logs a session as we would expect.


Solution

  • It seems in general we probably not assume that the authenticated user will be available immediately after the Cognito redirect.

    Since we can't do that we also cannot redirect to a protected resource, since the guard deny access.

    Therefore we have to redirect to a non guarded page, and then wait for Amplify to tell us that authentication has taken place.

    We can then use a Subject to notify other parts of the app.

    Seems like the right way to listen (Until Angular 9 is support by AngularService) is to run both Auth.currentAuthenticatedUser() and Hub.listen in parallel.

    Right after the redirect Auth.currentAuthenticatedUser() will most likely throw, but Hub.listen will eventually emit the signin event which we can next on our Subject.

    So try Auth.currentAuthenticatedUser() and if it throws, then try to listen for a sigin event with Hub in the catch block. Also listen for signout events in this case.

    After that until the user signs out or the session times out, Auth.currentAuthenticatedUser() should always return a user, and we can next that on the Subject that we are using to observe authenticated state.

    So that's how I plan to approach it.