@ngrxs/permission-based-authorization
TypeScript icon, indicating that this package has built-in type declarations

1.0.0 • Public • Published

@ngrxs/permission-based-authorization

@ngrxs/permission-based-authorization library that provide a common authentication based on permissions and not roles.

Installation

  1. Install the library:

    npm install --save @ngrxs/permission-based-authorization
  2. Use providePermissionBasedAuthorization, AuthFlowService and AuthPermissionsService in app.config.ts:

    import { ApplicationConfig } from '@angular/core';
    import { providePermissionBasedAuthorization, AuthFlowService, AuthPermissionsService, AuthDebugService } from '@ngrxs/permission-based-authorization';
    
    enum PagePermissions {
      None = 0,
      ViewDashboard = 1,
      ViewUsers = 2,
    }
    
    export const appConfig: ApplicationConfig = {
      providers: [
        { provide: AuthFlowService, useClass: AuthDebugService },
        { provide: AuthPermissionsService, useClass: AuthDebugService },
        providePermissionBasedAuthorization({
          routePermissions: {
            '/': null,
            '/dashboard': PagePermission.ViewDashboard,
            '/users': PagePermission.ViewUsers,
          },
          signInUrl: '/',
        })
      ]
    };
  3. Use in app.routes.ts

    import { AuthFlowService, AuthRouteCheckService } from '@ngrxs/permission-based-authorization';
    
    const authLoginRedirect: CanActivateFn = (route: ActivatedRouteSnapshot): UrlTree | boolean => {
      const isLoggedIn = inject(AuthFlowService).isLoggedIn;
      return isLoggedIn ? createUrlTreeFromSnapshot(route, ['home']) : true;
    };
    
    const canActivateRoute: CanActivateFn = (_, state: RouterStateSnapshot) =>
      inject(AuthRouteCheckService).checkRoutePermission(state.url);
    
    const canActivateChildRoute: CanActivateChildFn = (_, state: RouterStateSnapshot) =>
      inject(AuthRouteCheckService).checkRoutePermission(state.url);
    
    export const routes: Routes = [
       /** routes */
    ];

Usage

Provides auth based on permissions.

Real world auth service

import { AuthFlowService, AuthPermissionsService } from '@ngrxs/permission-based-authorization';

import { OAuthService } from 'angular-oauth2-oidc';
import { JwksValidationHandler } from 'angular-oauth2-oidc-jwks';

@Injectable()
export class AuthGoogleService implements AuthFlowService {
  #user: { given_name: string } | null = null;
  #oauth: OAuthService;

  constructor(settings: AppSettings, oauthService: OAuthService) {
    const oauthConfigs = getAuthConfig(settings);
    oauthService.configure({
      issuer: 'https://accounts.google.com',
      redirectUri: settings.webBasePath + 'oauth-callback',
      clientId: settings.googleClientId,
      scope: 'openid profile email',
      sessionChecksEnabled: true,
      strictDiscoveryDocumentValidation: false,
    });
    oauthService.tokenValidationHandler = new JwksValidationHandler();

    this.#oauth = oauthService;
  }

  get isLoggedIn(): boolean {
    return this.#oauth.hasValidAccessToken();
  }

  get name(): string {
    return this.#user === null ? '' : this.#user.given_name;
  }

  getBearerToken(): string {
    return this.#oauth.getIdToken();
  }

  signOut(): void {
    this.#oauth.logOut();
  }

  startAuthentication(): void {
    void this.#oauth.loadDiscoveryDocumentAndLogin();
  }

  async completeAuthentication(): Promise<boolean> {
    const successLogin = await this.#oauth.loadDiscoveryDocumentAndTryLogin({ });
    if (!successLogin) return false;

    this.#user = await this.#oauth
      .loadUserProfile()
      .then(it => it as GoogleUserInfo)
      .catch(error => {
        console.error(error);
        return null;
      });

    return true;
  }
}

@Injectable()
export class AuthApiPermissionsService implements AuthPermissionsService {
  #permissionsLoaded$ = new BehaviorSubject(false);
  #permissions$ = new BehaviorSubject<number | null>(null);
  #http = inject(HttpClient);

  get permissions$(): Observable<number | null> {
    return this.#permissions$;
  }

  get permissionsLoaded$(): Observable<boolean> {
    return this.#permissionsLoaded$;
  }

  startPermissionsLoading(): void {
    this.#http.get<{ permissions: number }>('api/v1/profile').subscribe({
      next: ({ permissions }) => this.#permissions$.next(permissions),
      error: () => this.#permissions$.next(0),
      finally: () => this.#permissionsLoaded$.next(true),
    })
  }
}

OpenId Connect Callback

import { ChangeDetectionStrategy, Component } from '@angular/core';
import { AuthCallbackService } from '@ngrxs/permission-based-authorization';

@Component({
  selector: 'app-auth-callback',
  standalone: true,
  template: '<p>Please wait while we redirect you back</p>',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class AuthCallbackComponent {
  constructor(auth: AuthCallbackService) {
    auth.completeAuthentication();
  }
}

Interfaces

interface AuthFeatureOptions {
  routePermissions: { [route: string]: number } // all route access permissions
  homeUrl: string; // home page url
  signInUrl: string; // login page
  signInCallbackUrl: string; // openid-connect callback url
  noAccessUrl: string; // no access url
}

Readme

Keywords

Package Sidebar

Install

npm i @ngrxs/permission-based-authorization

Weekly Downloads

2

Version

1.0.0

License

MIT

Unpacked Size

65.4 kB

Total Files

25

Last publish

Collaborators

  • rosenkolev