# Role Base Authentication

1. The main routing file defines route paths and lazy-loaded modules for various components based on the user's role.

{% code title="src/app-routing.module.ts" %}

```typescript
...
{
    path: '',
    component: AdminLayout,
    canActivateChild: [AuthGuardChild],
    children: [
      {
        path: '',
        loadChildren: () => import('./demo/pages/dashboard/dashboard.module').then((m) => m.DashboardModule),
        data: { roles: [Role.Admin, Role.User] }
      },
      {
        path: 'dashboard',
        loadChildren: () => import('./demo/pages/dashboard/dashboard.module').then((m) => m.DashboardModule),
        data: { roles: [Role.Admin, Role.User] }
      },
      {
        path: 'widget',
        loadChildren: () => import('./demo/pages/widget/widget.module').then((m) => m.WidgetModule),
        data: { roles: [Role.Admin, Role.User] }
      },
      ...
    ]
}
...
```

{% endcode %}

2. **Child Routing Module:** Defines the child routes and applies role-based access.

{% code title="src/app/demo/pages/dashboard/dashboard-routing.module.ts" %}

```typescript

...
const routes: Routes = [
  {
    path: '',
    children: [
      {
        path: 'default',
        loadComponent: () => import('./default/default.component').then((c) => c.DefaultComponent),
        data: { roles: [Role.Admin, Role.User] }
      },
      {
        path: 'analytics',
        loadComponent: () => import('./analytics/analytics.component').then((c) => c.AnalyticsComponent),
        data: { roles: [Role.Admin] }
      },
      {
        path: 'finance',
        loadComponent: () => import('./finance/finance.component').then((c) => c.FinanceComponent),
        data: { roles: [Role.Admin] }
      }
    ]
  }
];
...

```

{% endcode %}

3. Role Management

{% code title="src/app/%40theme/types/role.ts" %}

```typescript
export enum Role {
  User = 'User',
  Admin = 'Admin'
}
```

{% endcode %}

4. **Authentication Guard**: This `AuthGuardChild` ensures that users can only access authorized routes.

{% code title="src/app/%40theme/helpers/auth.guard.ts" %}

```typescript
import { Injectable, inject } from '@angular/core';
import { Router, ActivatedRouteSnapshot, RouterStateSnapshot, CanActivateChild } from '@angular/router';
import { Observable, of } from 'rxjs';
import { map, catchError } from 'rxjs/operators';

import { AuthenticationService } from '../services/authentication.service';
import { User } from '../types/user';

@Injectable({ providedIn: 'root' })
export class AuthGuardChild implements CanActivateChild {
  private router = inject(Router);
  private authenticationService = inject(AuthenticationService);

  /**
   * Determines whether a child route can be activated based on user authentication and authorization.
   *
   * @param route - The activated route snapshot that contains the route configuration and parameters.
   * @param state - The router state snapshot that contains the current router state.
   * @returns A boolean or Observable<boolean> indicating whether the route can be activated. Redirects to an appropriate page if not.
   *
   * If the user is logged in and their role is authorized for the route, returns true.
   * If the user is logged in but not authorized, redirects to the unauthorized page and returns false.
   * If the user is not logged in, redirects to the login page with the return URL and returns false.
   * If user data is being loaded, waits for it to complete before making a decision.
   */
  canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean | Observable<boolean> {
    const currentUser = this.authenticationService.currentUserValue;
    const hasToken = !!this.authenticationService.getToken();

    // If we have a token but no user data, fetch it first
    if (hasToken && !currentUser && !this.authenticationService.isLoading) {
      return this.authenticationService.fetchCurrentUser().pipe(
        map((user) => {
          this.authenticationService.isLogin = true;
          return this.checkAuthorization(route, state, user);
        }),
        catchError(() => {
          // If fetch fails, redirect to login
          this.router.navigate(['/login'], { queryParams: { returnUrl: state.url } });
          return of(false);
        })
      );
    }

    // If user data is currently loading, wait for it to complete
    if (this.authenticationService.isLoading) {
      // Return an observable that waits for loading to complete
      return new Observable<boolean>((observer) => {
        let attempts = 0;
        const maxAttempts = 50; // 5 seconds max (50 * 100ms)

        const checkInterval = setInterval(() => {
          attempts++;
          if (!this.authenticationService.isLoading || attempts >= maxAttempts) {
            clearInterval(checkInterval);
            const user = this.authenticationService.currentUserValue;
            if (user && this.authenticationService.isLoggedIn()) {
              observer.next(this.checkAuthorization(route, state, user));
            } else {
              this.router.navigate(['/login'], { queryParams: { returnUrl: state.url } });
              observer.next(false);
            }
            observer.complete();
          }
        }, 100);
      });
    }

    // If we have user data, check authorization
    if (currentUser && this.authenticationService.isLoggedIn()) {
      return this.checkAuthorization(route, state, currentUser);
    }

    // User not logged in, redirect to login page
    this.router.navigate(['/login'], { queryParams: { returnUrl: state.url } });
    return false;
  }

  /**
   * Checks if the user is authorized for the route based on their role
   */
  private checkAuthorization(route: ActivatedRouteSnapshot, state: RouterStateSnapshot, currentUser: User): boolean {
    const { roles } = route.data;
    if (roles && !roles.includes(currentUser.user.role)) {
      // User not authorized, redirect to unauthorized page
      this.router.navigate(['/unauthorized']);
      return false;
    }
    // User is logged in and authorized for child routes
    return true;
  }
}
```

{% endcode %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://phoenixcoded.gitbook.io/able-pro/angular/how-to/role-base-authentication.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
