keycloak-angular

repository·main·Indexed 21 days ago

https://github.com/mauriciovigolo/keycloak-angular

A library for integrating the official Keycloak JavaScript adapter into Angular applications. Version 22.0.0 supports modern Angular features including Signals, functional route guards via createAuthGuard, and a standalone approach using provideKeycloak. It provides automatic token refreshing with withAutoRefreshToken, a bearer token interceptor, and role-based rendering through the *kaHasRoles structural directive.

Tokens
21.8K
Snippets
54
Records
78
Agent score
73%

What's inside keycloak-angular

  1. Key Features of Keycloak Angular Standalone

    main

    The standalone example demonstrates modern Angular integration patterns for Keycloak, including:

    • Keycloak Initialization: Using the provideKeycloak function for standalone configuration.
    • Automatic Token Refresh: Enabling automatic token refreshing via the withAutoRefreshToken feature.
    • Keycloak Signal Integration: Using Signals to simplify handling Keycloak events.
    • Bearer Token Interceptor: Including the interceptor using includeBearerTokenInterceptor to automatically attach tokens to HTTP requests.
    • Auth Guard Factory: Creating functional route guards with the createAuthGuard function.
    • Role-Based Rendering: Using the *kaHasRoles directive to conditionally render UI elements based on resource or realm roles.
  2. Compare Bearer Token vs Custom Bearer Token Interceptors

    main

    Choose the appropriate interceptor based on your authorization requirements:

    FeatureBearer Token InterceptorCustom Bearer Token Interceptor
    Best Use CaseSimple, declarative rules based on URL and Method.Advanced, dynamic logic based on request properties or Keycloak state.
    ConfigurationUses urlPattern (RegExp) and httpMethods.Uses a shouldAddToken callback function.
    ComplexityLowHigh
    Injection TokenINCLUDE_BEARER_TOKEN_INTERCEPTOR_CONFIGCUSTOM_BEARER_TOKEN_INTERCEPTOR_CONFIG
  3. How `withAutoRefreshToken` works

    main

    The withAutoRefreshToken feature operates through a specific workflow involving activity tracking and token lifecycle events:

    1. Activity Tracking: The UserActivityService monitors user interactions such as mouse movement, key presses, touch starts, and clicks.
    2. Token Expiration Event: When a KeycloakEventType.TokenExpired event occurs, the system evaluates the user's state.
    3. Decision Logic:
      • Active User: If the user has been active within the sessionTimeout window, the token is refreshed using Keycloak.updateToken.
      • Inactive User: If the user's inactivity duration exceeds the sessionTimeout, the action defined in onInactivityTimeout ('login', 'logout', or 'none') is executed.
  4. Deprecation notice for NgModule approach in Keycloak-Angular v19+

    main

    Starting from Keycloak-Angular v19, the NgModule-based approach is deprecated for use with Angular v19. The following components are deprecated in favor of a standalone approach:

    • KeycloakService
    • KeycloakAngularModule
    • Class-based interceptors
    • Class-based guards

    If you are starting a new project with Angular v19, you should use the standalone method instead of the NgModule approach demonstrated in this example.

  5. How to perform custom Keycloak initialization

    main
    By default, provideKeycloak attempts to initialize Keycloak automatically. If you need to control exactly when keycloak.init() is called (for example, to perform logic before authentication starts), omit the initOptions parameter from the provideKeycloak configuration. This skips the automatic initialization process, giving you full manual control.
  6. Migrate from KeycloakEvents RxJS Subject to Angular Signals

    main

    In version 19, Keycloak events transitioned from an RxJS Subject to Angular Signals. This provides a more declarative way to handle client events without manual subscription management.

    Key Changes

    • Angular Signals: Events are now exposed as Signals.
    • Early Initialization: Events are instantiated at the start of the application lifecycle, even before the Keycloak client is initialized.
    • Injection Token: Use the KEYCLOAK_EVENT_SIGNAL injection token to access events.

    Implementation Steps

    1. Inject KEYCLOAK_EVENT_SIGNAL using Angular's inject() function.
    2. Use an effect() to react to changes in the signal.
    3. Use the typeEventArgs<T>() utility to provide strong typing for event arguments.

    Example

    import { Component, effect, inject } from '@angular/core';
    import { KEYCLOAK_EVENT_SIGNAL, KeycloakEventType, typeEventArgs, type ReadyArgs } from 'keycloak-angular';
    
    @Component({
      selector: 'app-menu',
      templateUrl: './menu.component.html',
      styleUrls: ['./menu.component.css']
    })
    export class MenuComponent {
      authenticated = false;
      private readonly keycloakSignal = inject(KEYCLOAK_EVENT_SIGNAL);
    
      constructor() {
        effect(() => {
          const keycloakEvent = this.keycloakSignal();
    
          if (keycloakEvent.type === KeycloakEventType.Ready) {
            // Use typeEventArgs to cast arguments to the correct type
            this.authenticated = typeEventArgs<ReadyArgs>(keycloakEvent.args);
          }
    
          if (keycloakEvent.type === KeycloakEventType.AuthLogout) {
            this.authenticated = false;
          }
        });
      }
    }
    import { Component, effect, inject } from '@angular/core';
    import { KEYCLOAK_EVENT_SIGNAL, KeycloakEventType, typeEventArgs, type ReadyArgs } from 'keycloak-angular';
    
    @Component({
      selector: 'app-menu',
      templateUrl: './menu.component.html',
      styleUrls: ['./menu.component.css']
    })
    export class MenuComponent {
      authenticated = false;
      private readonly keycloakSignal = inject(KEYCLOAK_EVENT_SIGNAL);
    
      constructor() {
        effect(() => {
          const keycloakEvent = this.keycloakSignal();
    
          if (keycloakEvent.type === KeycloakEventType.Ready) {
            this.authenticated = typeEventArgs<ReadyArgs>(keycloakEvent.args);
          }
    
          if (keycloakEvent.type === KeycloakEventType.AuthLogout) {
            this.authenticated = false;
          }
        });
      }
    }
  7. Configure automatic token refresh and interceptors

    main

    To ensure a seamless user experience, you can enable automatic token refreshing and secure outgoing HTTP requests using the following features:

    • Automatic Token Refresh: Include the withAutoRefreshToken feature to automatically refresh expired tokens based on user activity.
    • Bearer Token Interceptor: Use the includeBearerTokenInterceptor function to automatically attach the Keycloak bearer token to outgoing HttpClient requests.
  8. Setup Keycloak Angular in an NgModule Application

    main

    To integrate Keycloak into an Angular application using NgModule, you must initialize the KeycloakService during the application bootstrap process using an APP_INITIALIZER provider. This ensures Keycloak is ready before the app starts.

    Note: This implementation is deprecated. It is recommended to migrate to the provideKeycloak function for modern Angular applications. However, for existing NgModule-based apps, follow this pattern:

    1. Import KeycloakAngularModule in your AppModule.
    2. Create an initialization factory function that calls keycloak.init().
    3. Provide the factory using APP_INITIALIZER with KeycloakService as a dependency.

    To enable silent SSO (which avoids full page redirects by using a hidden iframe), you must also serve a static HTML file at the path specified in silentCheckSsoRedirectUri.

    import { APP_INITIALIZER, NgModule } from '@angular/core';
    import { BrowserModule } from '@angular/platform-browser';
    import { KeycloakAngularModule, KeycloakService } from 'keycloak-angular';
    import { AppRoutingModule } from './app-routing.module';
    import { AppComponent } from './app.component';
    
    function initializeKeycloak(keycloak: KeycloakService) {
      return () =>
        keycloak.init({
          config: {
            url: 'http://localhost:8080',
            realm: 'your-realm',
            clientId: 'your-client-id'
          },
          initOptions: {
            onLoad: 'check-sso',
            silentCheckSsoRedirectUri: window.location.origin + '/assets/silent-check-sso.html'
          }
        });
    }
    
    @NgModule({
      declarations: [AppComponent],
      imports: [AppRoutingModule, BrowserModule, KeycloakAngularModule],
      providers: [
        {
          provide: APP_INITIALIZER,
          useFactory: initializeKeycloak,
          multi: true,
          deps: [KeycloakService]
        }
      ],
      bootstrap: [AppComponent]
    })
    export class AppModule {}