keycloak-angular
keycloak-angular copied to clipboard
Support for Pure Function-Based Guards in Angular
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.
- As of Angular 15.2.0 they have introduced the deprecation of Class and InjectionToken guards and resolvers. Guards should now be written as plain JavaScript function. Link to deprecation commit
- Currently when upgrading from Angular v.15 to v.16 the
ng updateautomatically wraps the Class based Guards intomapToCanActivatefunction provided under@angular/router.
Desired functionality.
- To ensure the longevity and compatibility of 'keycloak-angular' with future Angular releases, it would be beneficial to transition to a pure function-based solution for Guards. This change is not about fixing a current issue but about aligning with the evolving Angular ecosystem and avoiding potential future incompatibilities.
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.
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']);
};