GithubHelp home page GithubHelp logo

witcom-gmbh / keycloak-authz-angular Goto Github PK

View Code? Open in Web Editor NEW
6.0 5.0 8.0 1.67 MB

Setup Keycloak Authorization for Angular applications

License: GNU General Public License v3.0

JavaScript 4.47% TypeScript 95.53%

keycloak-authz-angular's Introduction

Keycloak Authorization for Angular

npm version npm Known Vulnerabilities

Keycloak Authorization Addon for keycloak-angular



About

This library adds Keycloak fine-grained authorization policies (https://www.keycloak.org/docs/6.0/authorization_services/) to the great keycloak-angular library by Mauricio Vigolo (https://github.com/mauriciovigolo/keycloak-angular) It provides the following features

  • A Keycloak Authorization Service which uses the Keycloak API to get entitlements/permissions for the logged-in user
  • Generic AuthGuard implementation, so you can customize your own AuthGuard logic inheriting the authentication logic and the permissions/authorizations load
  • An Angular directive wheich enables/disables HTML-Elements bases on permission
  • Documentation that helps to configure this library with your angular app

Install

Versions

This library depends on keycloak-angular, and therefore on angular and keycloak-js. Following combinations have been tested

keycloak-angular-authz keycloak-angular Angular Keycloak SSO-RH
1.x.x 6.x.x 7 4 -

Steps to install using NPM or YARN

Please, again, be aware to choose the correct version, as stated above. Installing this package without a version will make it compatible with the latest angular and keycloak versions.

In your angular application directory:

With npm:

npm install --save keycloak-authz-angular@<choosen-version-from-table-above>

With yarn:

yarn add keycloak-authz-angular@<choosen-version-from-table-above>

Setup

Angular

Please follow the documentation here https://github.com/mauriciovigolo/keycloak-angular

Angular Authorization

It makes sense to initialize keycloak-authorization directly after having initialized keycloak-angular. When initializing the authorization you can provide an default resource-server-id for which all entitlements are loaded. So if you are using APP_INITIALIZER you could do it like this

import { KeycloakService } from 'keycloak-angular';
import {KeycloakAuthorizationService} from 'keycloak-authz-angular';
import { environment } from '../../environments/environment';

export function kcInitializer(keycloak: KeycloakService,authService:KeycloakAuthorizationService): () => Promise<any> {
  return (): Promise<any> => {
    return new Promise(async (resolve, reject) => {
      try {
        await keycloak.init({
          config: environment.keycloakConfig,
          initOptions: {
            onLoad: 'login-required',
            checkLoginIframe: false
          }, 
          bearerPrefix: 'Bearer',
          bearerExcludedUrls: []
        });
        await authService.init({
          config: environment.keycloakConfig,
          initOptions: {
            defaultResourceServerId: 'my-resource-id'
          }}); 
        resolve();
      } catch (error) {
        reject(error);
      }
    });
  };
}

Basically you wait for the initialization of both keycloak & keycloak authorization to finish before proceeding.In that case all permissions are available for further use immediately after initialization. It is possible to retrieve entitlements for multiple resource-servers - just call the getAuthorizations(NAME-OF-RESOURCE-SERVER) Method multiple times. You can use the same keycloak-configuration object for keycloak-angular and keycloak-authz-angular.

AuthGuard

The generic AuthGuard "KeycloakAuthzAuthGuard" that is provided by the library follows the sames principles as the AuthGuard which is included in the keycloak-angular library - except that it uses permissions instead of roles. In your implementation you just need to implement the desired security logic.

import { Injectable } from '@angular/core';
import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { KeycloakService, KeycloakAuthGuard } from 'keycloak-angular';
import {KeycloakAuthorizationService,KeycloakAuthzAuthGuard} from 'keycloak-authz-angular';

@Injectable()
export class AppAuthGuard extends KeycloakAuthzAuthGuard {
  constructor(protected router: Router, protected keycloakAngular: KeycloakService, protected keycloakAuth: KeycloakAuthorizationService) {
    super(router, keycloakAngular,keycloakAuth);
  }
  isAccessAllowed(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
    return new Promise(async (resolve, reject) => {
      if (!this.authenticated) {
        this.keycloakAngular.login();
        return;
      }
      const requiredRoles = route.data.roles;
      const requiredPermissions = route.data.permissions;
      if (!requiredPermissions || requiredPermissions.length === 0) {
        return resolve(true);
      } else {
        if (!this.permissions || this.permissions.length === 0) {
          resolve(false);
        }
        let granted: boolean = false;
        for (const requiredPermission of requiredPermissions) {
            if (this.keycloakAuth.checkAuthorization(requiredPermission)){
                granted = true;
                break;
            }
        }
        resolve(granted);
      }
    });
  }
}

The routing-module itself can be configured like this. In that case the link is available to users that either

  • have read scope on resource resource1 or
  • access to resource resource2 regardless of the scope
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HomeComponent} from './home/home.component';
import { AuthztestComponent } from './authztest/authztest.component';
import { AppAuthGuard } from './app-authguard';

const routes: Routes = [
    {
      path: 'home', component: HomeComponent      
    },
    {
      path: 'authztest', component: AuthztestComponent,canActivate: [AppAuthGuard] ,data:{permissions:[{
          rsname:"resource1",
          scope:"view" 
      },
      {
          rsname:"resource2"
      }
      ]}
    },
    {
        path: '', redirectTo: '/home', pathMatch: 'full' 
    }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
providers: [AppAuthGuard]
})
export class AppRoutingModule { }

Directive for enabling HTML-Elements based on authorization

A very basic directive that enables HTML-Elements only if the user has the required entitlement for a resource/resource and scope. It is included in the library and has to imported as an Angular-Module, e.g. in app.module

import { KeycloakAuthzAngularModule } from 'keycloak-authz-angular';
@NgModule({
  declarations: [
  ...
  ],
  imports: [
    ...
    KeycloakAuthzAngularModule,
    ...
  ],
  ...
})

The name of the directive is enableForKeycloakAuthorization and it takes a single required permission as a parameter. The permission can either be a resource, or a combination of resource and scope separated by a # It should be easy to adopt it for your own directives

This example only enables the button if the user has an entitlement for reource resource and resource-scope create

<button enableForKeycloakAuthorization="resource#create" type="button" class="ui-button-raised"></button>

keycloak-authz-angular's People

Contributors

iceman91176 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

keycloak-authz-angular's Issues

AuthGuard Problem - Services not initialized yet when checking permission

Hi, I tried to follow the documentation and created my own Guard to check the user has permissions to see one specific page, this is what I have in my routing module:

const routes: Routes = [
  {
    path: '',
    component: PurchaseComponent,
    canActivate: [KeycloakPermissionGuard],
    data: {
      permissions: [
        {
          rsname: 'purchase',
          scope: 'action'
        }
      ]
    }
  }
];

And this is my code in the KeycloakPermissionGuard guard:

   if (!this.authenticated) {
      this.keycloakService.login();
      return;
    }

    const requiredRoles = route.data.roles;
    const requiredPermissions = route.data.permissions;

    if (!(requiredPermissions && requiredPermissions.length > 0) && !(requiredRoles && requiredRoles.length > 0)) {
      return resolve(true);
    }

    let granted: boolean = true;

    if (requiredRoles) {
      let roles = this.keycloakService.getUserRoles();
      granted = requiredRoles.every(r => roles.indexOf(r) > -1);
    }

    if (granted && requiredPermissions) {
      for (const requiredPermission of requiredPermissions) {
        if (!this.keycloakAuthorization.checkAuthorization(requiredPermission)){
            granted = false;
            break;
        }
      }
    }

    resolve(granted);

While debugging in the browser, I noticed the guard got activated a lot of times, and every time it went it the 'not authenticated' if, and tried to log in into keycloak again and again. When the guard got activated, the keycloakService and keycloakAuthorizationService, where not initialized yet, and the token was not loaded, thus no permissions, at the end, after waiting for long, those services got initialized, and finally evaluated the permissions. So I wonder if I'm doing something wrong, or this is a bug in the library? How could I get rid of that problem? I would like to use permissions to check if user should see one specific page or get forbidden display shown.

Angular 14

Hey everyone!

I would like to know if this package is still actively maintained? If yes, is it possible to use the latest Angular version (14) for the peer dependencies?

Greets

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.