1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.
  2. Anuncie Aqui
    Anuncie aqui você Também: fdantas@4each.com.br

What is the best practice for an authGuard (CanActivateFn) with Angular Signals and...

Discussão em 'Angular' iniciado por erwan, Junho 2, 2025.

  1. erwan

    erwan Guest

    I'm using Angular (v20) with Signals and httpResource from @angular/common/http to manage my user's state and authentication. My AuthService exposes a currentUserResource that loads user information and a computed isLoggedin signal.

    The challenge is implementing an authGuard (CanActivateFn) that needs to wait for the initial authentication status to be known (i.e., for currentUserResource to complete its initial load) before allowing or denying access to a route. I'm looking for the cleanest, most idiomatic "Angular Signals" way to do this, ideally avoiding RxJS.

    Here's a simplified version of my AuthService:

    import { Injectable, computed, inject } from '@angular/core';
    import { httpResource, HttpResourceRef } from '@angular/common/http';
    import { User } from '../interfaces/user.interface';
    import { Router } from '@angular/router';

    const API_URL = '/api/auth';

    @Injectable({ providedIn: 'root' })
    export class AuthService {
    private router = inject(Router);

    currentUserResource: HttpResourceRef<User | null> = httpResource<User | null>(() => ({
    url: `${API_URL}/current`, // Endpoint to get the current user
    defaultValue: null,
    }));

    public readonly isLoggedIn = computed(() => !!this.currentUserResource());
    public readonly currentUser = computed(() => this.currentUserResource());

    }


    And here's the skeleton of my functional authGuard:

    import { inject } from '@angular/core';
    import { CanActivateFn, Router } from '@angular/router';
    import { AuthService } from '../services/auth.service';

    export const authGuard: CanActivateFn = async (route, state) => {
    const authService = inject(AuthService);
    const router = inject(Router);
    if (authService.currentUserResource.isLoading()) {
    // What's the best approach here to wait?
    // 1. Use toObservable + firstValueFrom (RxJS)?
    // 2. Manually create a Promise with an Angular `effect` and resolve it?
    // 3. Is there another Angular-recommended method or built-in utility?
    await /* ??? */; // This is where I need the solution
    }

    // Once loading is complete (isLoading is false):
    if (!authService.isLoggedIn()) {
    router.navigate(['/login']);
    return false;
    }
    return true;
    };


    Right now I have this approach, but I'm not satisfied with the usage of an effect:

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

    if (authService.currentUserResource.isLoading()) {
    await new Promise<void>((resolve) => {
    const effectRef = effect(() => {
    if (!authService.currentUserResource.isLoading()) {
    resolve();
    effectRef.destroy();
    }
    });
    });
    }

    const isLoggedIn = authService.isLoggedin();

    if (!isLoggedIn) {
    router.navigateByUrl('/signin');
    return false;
    }

    return true;
    };


    I'm really looking for the new "signals-way", most modern and recommended approach to this issue.

    Continue reading...

Compartilhe esta Página