Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 17 AuthGuard with Observable

Trying to get the following AuthGuard to properly redirect if a user is not logged in.

export const authGuard: CanActivateFn = (route, state) => {
  const authService = inject(AuthService);
  const router = inject(Router);

  return authService.currentUser$.pipe(map((auth: User | null) => {
    console.log(auth);
    if(auth !== null){
      console.log(auth);
      return true;
    }
    return router.createUrlTree(['/login']);
  }));
};

and in my AuthService file I utilize the following

private currentUserSource = new ReplaySubject<User | null>(1);
currentUser$ = this.currentUserSource.asObservable();

In my app.routes.ts file my Test component is being lazy loaded.

{path: 'test', loadComponent: () => import('./test/test.component').then(mod => mod.TestComponent), canActivate: [authGuard]},

Instead of redirecting the user to a login page, it just shows a blank page.

Thanks

I have re-written it several times utilizing different methods, but they all failed at redirecting.

like image 355
WorldDrknss Avatar asked Jul 23 '26 12:07

WorldDrknss


2 Answers

What I think the problem may be is using a ReplaySubject for your currentUserSource variable, I would advice replacing it for a BehaviourSubject :

private currentUserSource = new BehaviourSubject<User | null>(null);
currentUser$ = this.currentUserSource.asObservable();

As to why I believe this might be the source of the problem, a guard that causes a lock on an empty page usually means that its waiting for an Observable to emit a value.

A ReplaySubject holds no default value meaning that if no value was emitted before the guard is called, your authGuard will subscribe to an observable that will will never emit any value, which causes it to wait indefinitely.

Changing it to a BehaviourSubject will ensure that there is always a value, so the guard may always be able to complete.

like image 82
Tibère B. Avatar answered Jul 26 '26 06:07

Tibère B.


I tried to replicate your issue on stackblitz, and when using functional guards, I was running into the "no provider for authguard", in which would return a black page.

So I turned the functional authguard into a class guard, and provided in root

See here:

https://stackblitz.com/edit/angular-standalone-route-fhto46?file=src%2Froutes%2Froutes.ts


@Injectable({ providedIn: 'root' })
class AuthGuardClass implements CanActivate {
  private router = inject(Router);
  canActivate() {
    return of({ user: 'mock' }).pipe( // Replace mock
      map((auth: { user: string } | null) => {
        console.log(auth);
        if (auth !== null) {
          console.log(auth);
          return true;
        }
        return this.router.createUrlTree(['/login']);
      })
    );
  }
}

export const routes: Routes = [
  {
    path: '',
    canActivate: [AuthGuardClass],
    children: [
      {
        path: '',
        pathMatch: 'full',
        redirectTo: 'home',
      },
      {
        path: 'home',
        loadComponent: () =>
          import('./home.component').then((mod) => mod.HomeComponent),
      },
    ],
  },
  {
    path: 'login',
    loadComponent: () =>
      import('./login.component').then((mod) => mod.LoginComponent),
  },
];
like image 41
Caio Oliveira Avatar answered Jul 26 '26 07:07

Caio Oliveira