@procempa/ngx-keycloak

Keycloak module for Angular with Guard, Interceptor, Observable Service and Directive to filter content based on user's roles

Usage no npm install needed!

<script type="module">
  import procempaNgxKeycloak from 'https://cdn.skypack.dev/@procempa/ngx-keycloak';
</script>

README

Keycloak Module AngularX

Package and Keycloak Versions

Package Version | Keycloak Version | Branch -|-|- 1.0.x | ~1.6.0 | kc-1.6.0 2.x.x | ^3.0.0 --> ^6.0.0 | v2 3.x.x | ^3.0.0 --> ^6.0.0 | master

Instalation

 npm i @procempa/ngx-keycloak
 yarn add @procempa/ngx-keycloak

Import Module

At AppModule of your project import the class NgxKeycloakModule

import { NgxKeycloakModule } from '@procempa/ngx-keycloak';

@NgModule({
  declarations: [],
  imports: [    
    NgxKeycloakModule.forRoot()    
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Initialization

You must create a function to use as a factory to initialize the keycloak with the application on the providers array.

example

AppModule:

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    NgxKeycloakModule.forRoot()
  ],
  providers: [
    { provide: APP_INITIALIZER, useFactory: initKeycloak, multi: true, deps: [KeycloakService] }
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

Factory Function

export function initKeycloak(keycloak: KeycloakService) {
  const env = {
    url: environment.KEYCLOAK_URL,
    realm: environment.KEYCLOAK_REALM,
    clientId: environment.KEYCLOAK_CLIENT
  };

  return () => keycloak.init(env, { onLoad: 'login-required' });
}

Interceptor

The NgxKeycloak's interceptor sends an Authorization header with a Bearer token to all requests but sometimes is necessary to not send it.

To ignore some URLs create an array with the addresses as a string or Regex and pass it on initialization.

Another way to ignore the interceptor is to add IGNORE_INTERCEPTOR to header for the request.

example

export function initKeycloak(keycloak: KeycloakService) {
  const env = {
    url: environment.KEYCLOAK_URL,
    realm: environment.KEYCLOAK_REALM,
    clientId: environment.KEYCLOAK_CLIENT
  };

  keycloak.urlsToIgnore = ['ignore.url.com', 'another.com.uk'];
  return () => keycloak.init(env, { onLoad: 'login-required' });
}

Routes Guard

The routes guard can accept an array of roles to protect it from navigation.

example

import { KeycloakAuthGuard } from '../keycloak/guards/auth.guard';

const routes: Routes = [
  {
    path: 'myPath',
    canActivate: [KeycloakAuthGuard],
    component: AppShellComponent,
    children: [
      {
        path: '/route/administration',
        loadChildren: 'app/admin.module#AdminModule',
        canLoad: [KeycloakAuthGuard],
        data: {
          roles: ['admin', 'employee', 'manager']
        }
      },
      {
        path: '/upload',
        component: UploadComponent,
        canActivate: [KeycloakAuthGuard],
        data: {
          roles: ['employee']
        }
      }
    ]
  }
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class MyRoutingModule { }

BaseKeycloakGuard

This class can be extended to customize the validation logic. Just implement the method isAllowed

Directives

*keycloakRole

The directive verify if the user has the roles for the specified client ou current client and remove or insert at DOM the elements.

example

<nav class="navbar navbar-default">
  <div class="container">
    <ul class="nav navbar-nav nav-tabs">
      <!-- allways visualized -->
      <li routerLinkActive="active"><a routerLink="./equip">Equipament</a></li>
      <!-- only rendered for a user with the ADMIN ROLE -->
      <li routerLinkActive="active" *keycloakRole="'admin'" ><a routerLink="./empresa">Empresa</a></li>
      <!-- only rendered for a user with the EMPLOYEE or ADMIN roles -->
      <li routerLinkActive="active" *keycloakRole="['employee', 'admin']"><a routerLink="./upload">Upload</a></li>
      <!-- only rendered for a user with the ADMIN role IN myapp client -->
      <li routerLinkActive="active" *keycloakRole="{myapp: ['admin']}"><a routerLink="./upload">Upload</a></li>
    </ul>
  </div>
</nav>

*keycloakAuthenticated

This directive is quite useful if you use onLoad type of check-sso instead of login-required.

You can also pass boolean parameter to directive to determine if it should be shown or hidden for authenticated users.

// Authenticated user
<div *keycloakAuthenticated="true">Shows</div>
<div *keycloakAuthenticated="false">Hides</div>

// Unauthenticated user
<div *keycloakAuthenticated="true">Hides</div>
<div *keycloakAuthenticated="false">Shows</div>