How to access RouterStateSnapshot within a functional guard? (Angular 15.1.2)
Asked Answered
O

1

6

I'm writing a guard that prevents a user to access a target url whenever he is not authenticated. I'm writing a functional guard to achieve this, however when I try to access the (target) url via the RouterStateSnapshot interface, I receive an empty string back.

In following stackblitz you'll find a minimal reproducible of the issue described above. (Angular 15.1.2) https://stackblitz.com/edit/angular-ft2rvr

Implementation of the functional guard:

import { inject } from '@angular/core';
import { Router } from '@angular/router';
import { OAuthService } from 'angular-oauth2-oidc';

export const authGuard = () => {
  const router = inject(Router);
  const oAuthService = inject(OAuthService);
  const hasAccessToken = oAuthService.hasValidAccessToken();
  const hasIdToken = oAuthService.hasValidIdToken();

  console.log('targeUrl', router.routerState.snapshot.url); // router.routerState.snapshot.url returns empty string

  if (!hasAccessToken || !hasIdToken) {
    router.navigate(['authenticate'], {
      state: {
        targetUrl: router.routerState.snapshot.url,
      },
    });
  }
  return hasAccessToken;
};

Whenever I replace the functional guard with a guard service, I am able to access the target url via the RouterStateSnapshot. (See code block below for the implementation)

import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, CanActivateChild, Router, RouterStateSnapshot } from '@angular/router';
import { OAuthService } from 'angular-oauth2-oidc';

@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate, CanActivateChild {

    constructor(private router: Router, private oAuthService: OAuthService) { }

    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
        return this.isUserLoggedIn(state.url);
    }

    canActivateChild(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
        return this.isUserLoggedIn(state.url);
    }

    private isUserLoggedIn(targetUrl: string): boolean {
        const hasAccessToken = this.oAuthService.hasValidAccessToken();
        if (!hasAccessToken) {
            this.router.navigate(['authenticate'], { state: {
                targetUrl: targetUrl
            }});
        }
        return hasAccessToken;
    }
}

Can you tell me how I need to access the target url within a functional guard?

Oxime answered 15/2, 2023 at 15:25 Comment(0)
A
11

Try changing the params going into your authGuard. The routerStateSnapshot should be passed through as parameters automatically.

export const authGuard = (
  route: ActivatedRouteSnapshot,
  state: RouterStateSnapshot,
) => {}
Albata answered 23/2, 2023 at 23:2 Comment(5)
With this, the state.url always comes empty []Etude
Actually this solution is right, it's working on my side and the parameters in the stackblitz example are wrong : ts export const authGuard = (route: RouterStateSnapshot) => { ... The first argument is missing: route: ActivatedRouteSnapshotWisecrack
Thanks for your answer. Indeed with both parameters it's working.Oxime
@Albata what if you want to pass an argument to the guard and you also need to use the state, for example? How would you do it?Overtly
Maybe using a higher-order function that returns the actual guard function?Overtly

© 2022 - 2024 — McMap. All rights reserved.