How to Secure Angular4 With Keycloak Role or Group
This article will be an extension of an existing one: http://czetsuya-tech.blogspot.com/2017/10/how-to-secure-angular-app-using.html . No...
https://www.czetsuyatech.com/2017/11/angular-security-with-keycloak-role.html
This article will be an extension of an existing one: http://czetsuya-tech.blogspot.com/2017/10/how-to-secure-angular-app-using.html.
Normally on the front-end side (Angular), we are only concern whether a user is authenticated or not. Authorization, whether a user has enough access to a given REST resource, is configured in the API side. But what if we want to secure the front-end URLs nonetheless? One of the many solutions is to create a guard and either add an authorization code in canLoad or canActivate. In this article, we will deal with canLoad, as we are lazy-loading our modules (please refer to Angular documentation for more information).
In this particular example, we are checking for the group claim tied to a user. The role would also do, but I normally used that on the REST API side.
Here are the steps:
Normally on the front-end side (Angular), we are only concern whether a user is authenticated or not. Authorization, whether a user has enough access to a given REST resource, is configured in the API side. But what if we want to secure the front-end URLs nonetheless? One of the many solutions is to create a guard and either add an authorization code in canLoad or canActivate. In this article, we will deal with canLoad, as we are lazy-loading our modules (please refer to Angular documentation for more information).
In this particular example, we are checking for the group claim tied to a user. The role would also do, but I normally used that on the REST API side.
Here are the steps:
- Create a permission-guard model that we will use when defining the route
export interface PermissionGuard { Only?: Array string>, Except?: Array string>, RedirectTo?: string | Function }
- Add the route in your app-routing.module.ts file, and define the Permission model in the data attribute.
{ path: 'dashboard/employee', canLoad: [AuthGuard], loadChildren: 'app/module/dashboard/employee/employee.module#EmployeeModule', data: { Permission: { Only: ['employee'], RedirectTo: '403' } as PermissionGuard } }
- In our keycloak.service, add the following methods. It checks if a user is a member of a group specified in the PermissionGuard.
static hasGroup( groupName: string ): boolean { return KeycloakService.auth.authz != null && KeycloakService.auth.authz.authenticated && KeycloakService.auth.authz.idTokenParsed.groups.indexOf( "/" + groupName ) !== -1 ? true : false; } static hasGroups( groupNames: Array
): boolean { return groupNames.some( v => { if ( typeof v === "string" ) return KeycloakService.hasGroup( v ); } ); } - Going back to the auth-guard.service, let's modify the canLoad method to process the authorization. Here we use the previously defined method to check if the current logged user is a member of a specific group.
canLoad( route: Route ): boolean { if ( KeycloakService.auth.loggedIn && KeycloakService.auth.authz.authenticated ) { this.logger.info( "user has been successfully authenticated" ); } else { KeycloakService.login(); return false; } this.logger.info( "checking authorization" ); let data = route.data["Permission"] as PermissionGuard; if ( Array.isArray( data.Only ) && Array.isArray( data.Except ) ) { throw "Can't use both 'Only' and 'Except' in route data."; } if ( Array.isArray( data.Only ) ) { let hasDefined = KeycloakService.hasGroups( data.Only ) console.log("hasDefined="+hasDefined); if ( hasDefined ) return true; if ( data.RedirectTo && data.RedirectTo !== undefined ) this.router.navigate( [data.RedirectTo] ); return false; } if ( Array.isArray( data.Except ) ) { let hasDefined = KeycloakService.hasGroups( data.Except ) if ( !hasDefined ) return true; if ( data.RedirectTo && data.RedirectTo !== undefined ) this.router.navigate( [data.RedirectTo] ); return false; } }
- An additional bonus is having a directive that we can use in the UI side to hide/show an element when a user is either a member of a group or not
import { Directive, OnInit, ElementRef, Input } from '@angular/core'; import { KeycloakService } from 'app/core/auth/keycloak.service'; @Directive( { selector: '[hasGroup]' } ) export class HasGroupDirective implements OnInit { @Input( 'hasGroup' ) group: string; @Input( 'hasGroups' ) groups: string; constructor( private element: ElementRef, ) { } ngOnInit() { if ( KeycloakService.hasGroup( this.group ) ) { this.element.nativeElement.style.display = ''; } else { this.element.nativeElement.style.display = 'none'; } } }
- What is 403? It's an additional route to redirect when a user is not authorized.
{ path: '403', component: ForbiddenComponent },
Post a Comment