Sodiki
Sodiki

Reputation: 161

Infinity loop with using Ngrx dispatcher in canActivate

I have authGuard in my angular 17 project. I'm trying to dispatch my new token value to store but i got infinity loop. When i use this ligne in my canActivate code(i get infinity loop) :

this.store.dispatch(myAction.setAuthTokenValue({authToken: response.authToken,}));

here is my guard code:

canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<boolean | UrlTree> {
    return this.authService.jwtToken$.pipe(
      take(1),
      switchMap(isAuthenticated => {
        if (!isAuthenticated.isAuthenticated) {
          return this.authService.getIfIsInAProdEnv().pipe(
            map(isUseSAML => {
              const isProdEnv = isUseSAML;
              return this.redirectToLoginPage(isProdEnv);
            })
          );
        } else if (isAuthenticated.token) {
          // if (!this.isRenewToken) {
            return this.authService.renewToken(isAuthenticated.token).pipe(
              tap(response => {
                if (response && response.authToken) {
                  this.isRenewToken = true;
                  console.log("tap")
                  this.store.dispatch(
                    MyActions.setAuthTokenValue({
                      authToken: response.authToken,
                    })
                  );
                }
              }),
              switchMap(response => {
                console.log("swith")
                if (response && response.authToken) {
                  this.isRenewToken = false;
                  return this.checkAccess(route);
                } else {
                  this.store.dispatch(MyActions.logout());
                  return of(this.redirectToLoginPage(false));
                }
              }),
              catchError(() => {
                this.store.dispatch(MyActions.logout());
                return of(this.redirectToLoginPage(false));
              })
            );
          } else {
            return this.checkAccess(route);
          }
        // } else {
        //   return of(this.redirectToLoginPage(false));
        // }
      }),
      catchError(() => {
        this.store.dispatch(MyActions.logout());
        return of(this.redirectToLoginPage(false));
      })
    );
  }

  redirectToLoginPage(isProdEnv: boolean) {
    if (isProdEnv) {
      return this.router.createUrlTree(['login/with-saml']);
    } else {
      return this.router.createUrlTree(['/login/with-email-id']);
    }
  }

Do have an idea to resolve this.

1 - I had to try to use this flag (i had commented in canActivate code you can see this // ) :

if (!this.isRenewToken) {
    ....
} else {
    return of(this.redirectToLoginPage(false));
}

but this solution execute my renewToken request only once while i need to make renew request whenever user go to other path.

2 - I also tried : create tokenService:

canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<boolean | UrlTree> {
    return this.authService.jwtToken$.pipe(
      take(1),
      switchMap(isAuthenticated => {
        if (!isAuthenticated.isAuthenticated) {
          return this.authService.getIfIsInAProdEnv().pipe(
            map(isUseSAML => {
              const isProdEnv = isUseSAML;
              return this.redirectToLoginPage(isProdEnv);
            })
          );
        } else if (isAuthenticated.token) {
           return this.tokenService.isRenewingToken$.pipe(
             take(1),
             switchMap(isRenewing => {
               if (!isRenewing) {
                 const token:string = isAuthenticated.token as string;
                 return this.tokenService.renewToken(token).pipe(
                   switchMap(response => {
                     if (response && response.authToken) {
                       return this.checkAccess(route);
                    } else {
                       this.store.dispatch(MyActions.logout());
                      return of(this.redirectToLoginPage(false));
                     }
                   }),
                   catchError(() => {
                     this.store.dispatch(MyActions.logout());
                     return of(this.redirectToLoginPage(false));
                   })
                 );
              } else {
                 return this.checkAccess(route);
               }
             })
           );

@Injectable({
  providedIn: 'root',
})
export class TokenService {
  private isRenewingTokenSubject = new BehaviorSubject<boolean>(false);
  isRenewingToken$ = this.isRenewingTokenSubject.asObservable();

  constructor(
    private authService: AuthService,
    private store: Store<MyState>
  ) {}

  renewToken(token: string): Observable<any> {
    this.isRenewingTokenSubject.next(true);
    return this.authService.renewToken(token).pipe(
      tap(response => {
        if (response && response.authToken) {
          this.store.dispatch(
            MyAction.setAuthTokenValue({ authToken: response.authToken })
          );
        }
        this.isRenewingTokenSubject.next(false);
      }),
      catchError(error => {
        this.isRenewingTokenSubject.next(false);
        throw error;
      })
    );
  }
}

But it execute only once my request

Upvotes: 1

Views: 47

Answers (0)

Related Questions