Angular routing, redirecting from page-not-found, to login page

79 views Asked by At

my router is not working properly, when I go to the pageNotFound page and want to go back to previous steps, it redirects me to the login page, I would be grateful for any tips on how to fix it. Debuilding shows that the method in authenticationguard - canActive() returns false when returning from this page, unfortunately I cannot find out why the user is not read correctly

export const routes: Routes = [
      {
        path: 'forbidden',
        component: ForbiddenPageComponent,
      },
      {
        path: 'auth',
        loadChildren: () => import('./modules/auth/auth.module').then(mod => mod.AuthModule),
        component: AuthComponent,
      },
     
      {
        path: '',
        canActivate: [AuthenticationGuard],
        canActivateChild: [AuthorizationGuard],
        children: [
          {
            path: 'task',
            loadChildren: () => import('./modules/task/task.module').then(mod => mod.TaskModule),
          },
          {
            path: 'order',
            loadChildren: () => import('./modules/order/order.module').then(mod => mod.OrderModule),
          },
          {
            path: '',
            pathMatch: 'full',
            redirectTo: 'task',
          }
        ],
      },
      {
        path: '**',
        pathMatch: 'full',
        component: PageNotFoundComponent
      }
    ];

@Injectable({
  providedIn: 'root',
})
export class AuthenticationGuard {
  currentUser: OPLUser | null;
 
  constructor(
    private authService: AuthService,
    private router: Router,
    @Inject(APP_CONFIG) private appConfig: AppEnvConfiguration
  ) {
    this.authService.currentUser$.subscribe(user => {
      this.currentUser = user;
    });
  }
 
  canActivate(): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    if (this.appConfig.skipLogin || (this.currentUser && this.currentUser.isAuthenticated())) {
      return true;
    }
 
    this.router.navigate(['/auth'], { replaceUrl: true });
 
    return false;
  }
}

@Injectable({
  providedIn: 'root',
})
export class AuthorizationGuard {
  constructor(
    private authService: AuthService,
    private router: Router,
    @Inject(APP_CONFIG) private appConfig: AppEnvConfiguration
  ) {}
 
  /**
   * Checks if user is authorized to activate this route.
   * If not, user is redirected to 'forbidden' page.
   *
   * @param route
   */
  canActivateChild(
    route: ActivatedRouteSnapshot
  ): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    const currentUser = this.authService.currentUser$.getValue();
    if (currentUser && currentUser.authenticatedAndAuthorized) {
      if (this.appConfig.skipLogin) return true;
 
      const routeRoleList: RouteRule = route && route.data ? route.data['roleList'] : null;
 
      if (!routeRoleList || this.matchesRole(routeRoleList, currentUser)) {
        return true;
      } else {
        this.router.navigate(['forbidden'], { skipLocationChange: true });
        return false;
      }
    } else {
      this.router.navigate(['forbidden'], { skipLocationChange: true });
      return false;
    }
  }
 
  /**
   * Checks if user roles matches route role configuration.
   * OneOf - checks any role.
   * AllOf - checks all roles.
   *
   * @param routeRoleList - provided route role configuration.
   * @param user - current user.
   */
  matchesRole(routeRoleList: RouteRule, user: OPLUser | null): boolean {
    let match = true;
 
    if (routeRoleList.oneOf && routeRoleList.oneOf.length > 0) {
      match = routeRoleList.oneOf.some(role => user?.hasRole(role));
    }
 
    if (routeRoleList.allOf && routeRoleList.allOf.length > 0) {
      match = routeRoleList.oneOf.every(role => user?.hasRole(role));
    }
 
    return match;
  }
}
 
1

There are 1 answers

0
Kyler Johnson On

This is happening due to the order in which you have configured your routes. The Angular router will go in order evaluating the path you define in your route config object against the current url. What's happening here is the router is encountering your empty path with the children and that matches the url (because you don't have pathMatch: full on that one). It then tries to route you to /task based on the redirect there, which is protected by the AuthenticationGuard, so you get redirected to login.

You can fix this by adding pathMatch: full to the empty path like this:

     {
        path: '',
        pathMatch: 'full',
        canActivate: [AuthenticationGuard],
        canActivateChild: [AuthorizationGuard],
        children: [
          {
            path: 'task',
            loadChildren: () => import('./modules/task/task.module').then(mod => mod.TaskModule),
          },
          {
            path: 'order',
            loadChildren: () => import('./modules/order/order.module').then(mod => mod.OrderModule),
          },
          {
            path: '',
            pathMatch: 'full',
            redirectTo: 'task',
          }
        ],
      },

If you want to debug the router yourself, you can do that by enabling router tracing. It will log the processes the router goes through to the console. You can do that by adding the option in your root level app-routing.module.ts like this:

@NgModule({
  imports: [
    RouterModule.forRoot(routes, {
      enableTracing: true
    }),
  ],

If you're using a standalone angular app (which bootstraps your Angular app with the bootstrapApplication method, you can enable router tracing by including the withDebugTracing from the @angular/router like this:

    provideRouter(APP_ROUTES, 
      withDebugTracing(),
    ),