4

I have the following Angular AuthGuard:

@Injectable({
    providedIn: 'root',
})
export class AuthGuard implements CanActivate, CanLoad {
    constructor(private authService: AuthService, private router: Router) {}

    canActivate(
        next: ActivatedRouteSnapshot,
        state: RouterStateSnapshot,
    ): | Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
        return this.isAuthenticated();
    }

    canLoad(
        route: Route,
        segments: UrlSegment[],
    ): | boolean | UrlTree | Observable<boolean | UrlTree> | Promise<boolean | UrlTree> {
        return this.isAuthenticated();
    }

    isAuthenticated(): | boolean | UrlTree | Observable<boolean | UrlTree> | Promise<boolean | UrlTree> {
        return this.authService.isAuthenticated$.pipe(
            take(1),
            tap((isAuthenticated: boolean) => {
                if (!isAuthenticated) {
                    this.router.navigate(['/account/login']);
                }
            }),
        );
    }
}

I updated to Angular 15:

Angular CLI: 15.2.0
Node: 16.19.1
Package Manager: npm 8.19.3
OS: darwin x64

Angular: 15.2.0

Package                         Version
---------------------------------------------------------
@angular-devkit/core            15.2.0
@angular-devkit/schematics      15.2.0
@schematics/angular             15.2.0

And now I'm getting the following TS depreciation note: enter image description here And a similar depreciation message for the CanLoad interface:

@deprecated — Use CanMatchFn instead
'CanLoad' is deprecated.

I tried to look for how to implement my AuthGuard with the recommended CanActivateFn and CanMatchFn, but I couldn't find a good resource on you to implement this with the ability to redirect to a certain route when the user is not authenticated, as it appears in the old CanActivate and CanLoad implementation above:

this.router.navigate(['/account/login']);

How to correctly implement a simple Angular AuthGuard that uses the new CanMatchFn and CanActivateFn?

Lorraine R.
  • 1,545
  • 1
  • 14
  • 39

1 Answers1

7

The simpliest way is to use inject(AuthGuard).canActivate as CanActivateFn.

If you want to refactor to drop the class it's similar :

const isAuthenticated = (): | boolean | UrlTree | Observable<boolean | UrlTree> | Promise<boolean | UrlTree> => {
    const authService = inject(AuthService);
    const router = inject(Router);
    return authService.isAuthenticated$.pipe(
        take(1),
        tap((isAuthenticated: boolean) => {
            if (!isAuthenticated) {
                this.router.navigate(['/account/login']);
            }
        }),
    );
}

const canActivate:CanActivateFn = isAuthenticated;
const canMatch:CanMatchFn = isAuthenticated;

For the depreciation of CanLoad just pass canMatch: [canMatch] in your router.


Edit: For futur references, this PR adds new helper functions to allow smooth migration from Class guard to functionnal guards.

  • mapToCanActivate([MyClassGuard])
  • mapToCanActivateChild([MyClassGuard])
  • mapToCanMatch([MyClassGuard])

etc. This will probably land in 15.3 or 16.0.

Matthieu Riegler
  • 31,918
  • 20
  • 95
  • 134