mauriciovigolo / keycloak-angular

Easy Keycloak setup for Angular applications.
MIT License
729 stars 280 forks source link

Support for Pure Function-Based Guards in Angular #526

Open nephi5 opened 12 months ago

nephi5 commented 12 months ago

Bug Report or Feature Request (mark with an x)

Currently, 'keycloak-angular' is functioning as expected with Angular versions up to v.17. However, the reliance on Class-based Guards is a concern due to Angular's evolving standards


- [x] feature request

Versions.

{
"keycloak-angular": "^14.0.0",
"@angular/core": "^17.0.0",
"keycloak-js": "^18.0.0"
}

Repro steps.

Desired functionality.

mauriciovigolo commented 12 months ago

Hi @nephi5, I agree with the change request. The new release for Angular v.17 will not have this change yet, but I will schedule it to change it soon.

ThoSap commented 9 months ago

I'm using the function-based guard for a half year now, you could do it like follows:

import { CanMatchFn, Router, UrlTree } from '@angular/router';
import { inject } from '@angular/core';

// Services
import { KeycloakService } from 'keycloak-angular';

export const authGuard: CanMatchFn = async (route, segments): Promise<boolean | UrlTree> => {
  const router = inject(Router);
  const keycloakService = inject(KeycloakService);

  const authenticated: boolean = await keycloakService.isLoggedIn();

  if (!authenticated) {
    await keycloakService.login({
      redirectUri: window.location.origin,
    });
  }

  // Get the user Keycloak roles and the required from the route
  const roles: string[] = keycloakService.getUserRoles(true);
  const requiredRoles = route.data?.['roles'];

  // Allow the user to proceed if no additional roles are required to access the route
  if (!Array.isArray(requiredRoles) || requiredRoles.length === 0) {
    return true;
  }

  // Allow the user to proceed if ALL of the required roles are present
  const authorized = requiredRoles.every((role) => roles.includes(role));
  // Allow the user to proceed if ONE of the required roles is present
  //const authorized = requiredRoles.some((role) => roles.includes(role));

  if (authorized) {
    return true;
  }

  // Display my custom HTTP 403 access denied page
  return router.createUrlTree(['/access']);
};