Route Guards

Summary

Guard Location Applied to

AuthGuard

core/auth/_guards/auth.guard.ts

The authenticated shell (BaseComponent)

DashboardStatusGuard

core/auth/_guards/auth.guard.ts

Available for dashboard-only routes

LogOutGuard

core/auth/_guards/log-out.guard.ts

/auth/login, /auth/register, /auth/reset-password

NewPasswordTokenGuard

core/auth/_guards/new-password-token.guard.ts

/auth/new-password

ModuleGuard

core/auth/_guards/module.guard.ts

Module-level permission gating

DocuSignGuard

core/pyb/guards/docu-sign.guard.ts

/dashboard, /product-offerings

HelpCenterRedirectGuard

core/pyb/guards/help-center-redirect.guard.ts

/help-center

TwoFactorAuthenticationGuard

core/pyb/guards/two-factor-authentication.guard.ts

/manage-account

TwoFactorInfoLoadedGuard

core/pyb/guards/two-factor-info-loaded.guard.ts

2FA sub-routes

NgxPermissionsGuard

ngx-permissions

/daily-summary-report

SellerCardListGuard

views/pages/seller-card/guards/

/seller-card

ActiveUserGuard

views/pages/seller-card/guards/

Card operations requiring an active supplier

AuthGuard

Selects isLoggedIn from the store. When false it navigates to auth/login?returnUrl=<current url> and the observable emits false.

canActivate(route, state): Observable<boolean> {
    return this.store.pipe(
        select(isLoggedIn),
        tap(loggedIn => {
            if (!loggedIn) {
                this.router.navigateByUrl(
                    `${URLS_ROUTER.AUTH.LOGIN}?returnUrl=${state.url}`,
                ).then();
            }
        }),
    );
}

The guard reads store state, not localStorage. The auth slice is rehydrated from localStorage by the persistent meta-reducer before the router runs, so a page refresh keeps the session. See State Persistence.

DashboardStatusGuard

Declared alongside AuthGuard. It checks whether user.dashboardStatus.status !== SupplierDashboardStatusType.Onboarding and, if the supplier is onboarding, navigates to /dashboard.

The guard returns true unconditionally — the redirect is a side effect, not a block. It nudges rather than prevents.

LogOutGuard

The inverse of AuthGuard. Returns true when the user is logged out, otherwise returns a UrlTree for /auth/login, keeping an authenticated user off the login and registration screens.

NewPasswordTokenGuard

Guards /auth/new-password. The reset code arrives as the r query parameter.

  1. If a session is already active, dispatch logout() and allow navigation.

  2. Otherwise call AuthService.confirmPasswordReset(verificationCode).

  3. On error, show a BannerTypes.WARNING banner with the translated message and return a UrlTree for /auth/login.

  4. On success, return true.

ModuleGuard

Reads route.data.moduleName, registers it with NgxPermissionsService and then checks it. Without a moduleName in route data it returns false. Provided by AppRoutingModule and ReportsRoutingModule.

DocuSignGuard

Despite the name, this guard does not check whether a contract is signed. It is the DocuSign return handler: it detects a redirect back from DocuSign and runs the corresponding business flow before the page renders.

Applied to /dashboard and /product-offerings — the two pages DocuSign is configured to return to.

canActivate(route, state): Observable<UrlTree | boolean> {
    return this.store.select(selectCurrentUserId).pipe(
        filter<string>(Boolean),
        exhaustMap(supplierKey => {
            if (this.docusignFlowInitialized(route.queryParams)) {   (1)
                return this.runDocuSignVerificationFlow({
                    flowType: route.queryParams.flowType,
                    params: {
                        data: route.queryParams,
                        event: route.queryParams.event,
                        supplierKey,
                    },
                }).pipe(
                    catchError(() => of(false)),
                    map(() => this.getUrlTree(route.queryParams?.mode)),  (2)
                    take(1),
                );
            }
            return of(true);                                          (3)
        }),
        take(1),
    );
}
1 "Initialized" here means both event and flowType query parameters are present — the signature of a DocuSign redirect.
2 After the flow completes, redirect to a clean URL so the query parameters cannot be replayed. mode === ProductOfferingsList → /product-offerings, anything else → /default.
3 Ordinary navigation with no DocuSign parameters passes straight through.

The flow itself is dispatched by DocusignVerificationService.initializeFlow(), which applies the second, localStorage-based replay guard.

HelpCenterRedirectGuard

A navigation-only guard. It tracks RouterNavigationEvents.HelpCenterLinkClicked with the current user id, opens https://help.payability.com/hc/en-us in a new tab, and returns false so Angular never completes the navigation.

static helpCenterUrl = 'https://help.payability.com/hc/en-us';

canActivate(): boolean {
    this.eventLoggerService.track({
        additionalProperties: { userId: this.userService.getCurrentUserId() },
        eventMessage: RouterNavigationEvents.HelpCenterLinkClicked,
    });
    window.open(HelpCenterRedirectGuard.helpCenterUrl, '_blank');
    return false;
}

TwoFactorAuthenticationGuard

Implements CanActivate, CanActivateChild and CanLoad. Delegates to TwoFactorAuthenticationService.checkTwoFactorAuthenticationState(force), where force comes from route.data.force2FAConfirmation.

  • A skipTwoFactorCheck query parameter bypasses the check entirely.

  • If the check fails and RouteUrlHelperService.prevUrl is empty — i.e. this was a direct entry rather than in-app navigation — it returns a UrlTree for /dashboard rather than leaving the user on a dead route.

ManageAccountModule applies it with force2FAConfirmation: true, so account settings always demand a confirmed second factor.

TwoFactorInfoLoadedGuard

A data-readiness guard. It dispatches fetch2FAInfo() and waits for the loaded2FAInfo action before allowing activation, so 2FA screens never render against an empty slice.

NgxPermissionsGuard

From ngx-permissions. Used on the top-level /daily-summary-report route:

{
    path: 'daily-summary-report',
    loadChildren: () => import('.../daily-summary-page.module').then(m => m.DailySummaryModule),
    canActivate: [NgxPermissionsGuard],
    data: { permissions: { only: ['REPORTS.DAILY_SUMMARY'] } },
}

The dotted string is the hierarchical permission name built by permissions.const.ts and loaded into NgxPermissionsService by GlobalSubscriptionsService. See Permissions System.

SellerCardListGuard

Ensures the Seller Card list has something to show. It dispatches loadPaymentMethods() and loadAirwallexCards(), waits until both slices report loaded and not loading, then:

  • if the supplier has p-card payment methods or any Airwallex card → true;

  • otherwise → UrlTree for newcard, the card-ordering funnel.

ActiveUserGuard

Selects selectIsSupplierActive, waits for a boolean, and redirects to /newcard when the supplier is not Active.