keycloak-angular 16.0.1 → 19.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +245 -151
  2. package/fesm2022/keycloak-angular.mjs +1324 -51
  3. package/fesm2022/keycloak-angular.mjs.map +1 -1
  4. package/index.d.ts +3 -0
  5. package/lib/directives/has-roles.directive.d.ts +95 -0
  6. package/lib/features/keycloak.feature.d.ts +43 -0
  7. package/lib/features/with-refresh-token.feature.d.ts +66 -0
  8. package/lib/guards/auth.guard.d.ts +75 -0
  9. package/lib/interceptors/custom-bearer-token.interceptor.d.ts +97 -0
  10. package/lib/interceptors/include-bearer-token.interceptor.d.ts +111 -0
  11. package/lib/interceptors/keycloak.interceptor.d.ts +71 -0
  12. package/lib/{core → legacy/core}/core.module.d.ts +5 -0
  13. package/lib/legacy/core/interceptors/keycloak-bearer.interceptor.d.ts +53 -0
  14. package/lib/legacy/core/interfaces/keycloak-event.d.ts +74 -0
  15. package/lib/legacy/core/interfaces/keycloak-options.d.ts +146 -0
  16. package/lib/legacy/core/services/keycloak-auth-guard.d.ts +50 -0
  17. package/lib/legacy/core/services/keycloak.service.d.ts +316 -0
  18. package/lib/{keycloak-angular.module.d.ts → legacy/keycloak-angular.module.d.ts} +5 -0
  19. package/lib/legacy/public_api.d.ts +14 -0
  20. package/lib/provide-keycloak.d.ts +74 -0
  21. package/lib/services/auto-refresh-token.service.d.ts +47 -0
  22. package/lib/services/user-activity.service.d.ts +66 -0
  23. package/lib/signals/keycloak-events-signal.d.ts +118 -0
  24. package/package.json +5 -7
  25. package/public_api.d.ts +19 -7
  26. package/esm2022/keycloak-angular.mjs +0 -2
  27. package/esm2022/lib/core/core.module.mjs +0 -33
  28. package/esm2022/lib/core/interceptors/keycloak-bearer.interceptor.mjs +0 -51
  29. package/esm2022/lib/core/interfaces/keycloak-event.mjs +0 -12
  30. package/esm2022/lib/core/interfaces/keycloak-options.mjs +0 -2
  31. package/esm2022/lib/core/services/keycloak-auth-guard.mjs +0 -17
  32. package/esm2022/lib/core/services/keycloak.service.mjs +0 -204
  33. package/esm2022/lib/keycloak-angular.module.mjs +0 -15
  34. package/esm2022/public_api.mjs +0 -7
  35. package/lib/core/interceptors/keycloak-bearer.interceptor.d.ts +0 -14
  36. package/lib/core/interfaces/keycloak-event.d.ts +0 -14
  37. package/lib/core/interfaces/keycloak-options.d.ts +0 -22
  38. package/lib/core/services/keycloak-auth-guard.d.ts +0 -11
  39. package/lib/core/services/keycloak.service.d.ts +0 -42
package/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
  [![npm][npm-badge]][npm]
9
9
  <!-- prettier-ignore-end -->
10
10
 
11
- > Easy Keycloak setup for Angular applications.
11
+ > Easy Keycloak integration for Angular applications.
12
12
 
13
13
  ---
14
14
 
@@ -16,9 +16,11 @@
16
16
  - [Installation](#installation)
17
17
  - [Setup](#setup)
18
18
  - [Example project](#example-project)
19
- - [AuthGuard](#authguard)
20
- - [HttpClient Interceptor](#httpclient-interceptor)
21
- - [Keycloak-js Events](#keycloak-js-events)
19
+ - [Keycloak Angular Features](#Keycloak-angular-features)
20
+ - [Guards](#guards)
21
+ - [HttpClient Interceptors](#httpclient-interceptors)
22
+ - [Directives](#directives)
23
+ - [Keycloak Events Signal](#keycloak-events-signal)
22
24
  - [Contributors](#contributors)
23
25
  - [License](#license)
24
26
 
@@ -26,16 +28,15 @@
26
28
 
27
29
  ## About
28
30
 
29
- This library helps you to use [keycloak-js](https://www.keycloak.org/docs/latest/securing_apps/index.html#_javascript_adapter) in Angular applications providing the following features:
31
+ `Keycloak-Angular` is a library that makes it easier to use [keycloak-js](https://www.keycloak.org/docs/latest/securing_apps/index.html#_javascript_adapter) in Angular applications. It provides the following features:
30
32
 
31
- - A **Keycloak Service** which wraps the `keycloak-js` methods to be used in Angular, giving extra
32
- functionalities to the original functions and adding new methods to make it easier to be consumed by
33
- Angular applications.
34
- - Generic **AuthGuard implementation**, so you can customize your own AuthGuard logic inheriting the authentication logic and the roles load.
35
- - A **HttpClient interceptor** that adds the authorization header to all HttpClient requests.
36
- It is also possible to disable this interceptor or exclude routes from having the authorization header.
37
- - This documentation also assists you to configure the keycloak in your Angular applications and with
38
- the client setup in the admin console of your keycloak installation.
33
+ - **Easy Initialization**: Use the `provideKeycloak` function to set up and initialize a Keycloak instance with `provideAppInitializer`. This simplifies the setup process.
34
+ - **Angular DI Support**: The Keycloak client instance can be injected directly into Angular components, services, and other parts of your app. There’s no need to create a custom service to wrap the client.
35
+ - **HTTP Interceptors**: Add the Bearer token to the `Authorization` header with built-in interceptors. These are modular and easy to configure using injection tokens. You can also create your own interceptors with provided helper functions.
36
+ - **Keycloak Events as Signals**: Access Keycloak events easily using Angular Signals, provided through the `KEYCLOAK_EVENT_SIGNAL` injection token.
37
+ - **Automatic Token Refresh**: Use the `withAutoRefreshToken` feature to automatically refresh tokens when they expire, considering the user activity and a sessionTimeout.
38
+ - **Custom Route Guards**: Use the `createAuthGuard` factory to build guards for `CanActivate` and `CanActivateChild`, helping you secure routes with custom logic.
39
+ - **Role-Based Rendering**: The `*kaHasRoles` directive lets you show or hide parts of the DOM based on the user’s resource or realm roles.
39
40
 
40
41
  ## Installation
41
42
 
@@ -45,20 +46,20 @@ Run the following command to install both Keycloak Angular and the official Keyc
45
46
  npm install keycloak-angular keycloak-js
46
47
  ```
47
48
 
48
- Note that `keycloak-js` is a peer dependency of Keycloak Angular. This change allows greater flexibility of choosing the right version of the Keycloak client version for your project.
49
+ Note that `keycloak-js` is a peer dependency of `keycloak-angular`. This allows greater flexibility of choosing the right version of the Keycloak client for your project.
49
50
 
50
51
  ### Versions
51
52
 
52
53
  | Angular | keycloak-js | keycloak-angular | Support |
53
- | :-----: |:-----------:| :--------------: | :-----------------: |
54
- | 18.x | 18 - 25 | 16.x.x | New Features / Bugs |
55
- | 17.x | 18 - 25 | 15.x.x | Bugs |
54
+ | :-----: | :---------: | :--------------: | :-----------------: |
55
+ | 19.x | 18 - 26 | 19.x.x | New Features / Bugs |
56
+ | 18.x | 18 - 26 | 16.x.x | Bugs |
57
+ | 17.x | 18 - 25 | 15.x.x | - |
56
58
  | 16.x | 18 - 25 | 14.x.x | - |
57
59
  | 15.x | 18 - 21 | 13.x.x | - |
58
- | 14.x | 18 - 19 | 12.x.x | - |
59
- | 14.x | 10 - 17 | 11.x.x | - |
60
60
 
61
- Only the latest version of Angular in the table above is actively supported. This is due to the fact that compilation of Angular libraries might be [incompatible between major versions](https://angular.io/guide/creating-libraries#ensuring-library-version-compatibility).
61
+ > 1. Only the latest version of Angular listed in the table above is actively supported. This is because the compilation of Angular libraries can be [incompatible between major versions](https://angular.io/guide/creating-libraries#ensuring-library-version-compatibility).
62
+ > 2. Starting with Angular v19, Keycloak-Angular will adopt the same **major** version numbering as Angular, making it easier to identify the correct version to use.
62
63
 
63
64
  #### Choosing the right keycloak-js version
64
65
 
@@ -66,54 +67,47 @@ The Keycloak client documentation recommends to use the same version of your Key
66
67
 
67
68
  ## Setup
68
69
 
69
- In order to make sure Keycloak is initialized when your application is bootstrapped you will have to add an `APP_INITIALIZER` provider to your `AppModule`. This provider will call the `initializeKeycloak` factory function shown below which will set up the Keycloak service so that it can be used in your application.
70
+ To initialize Keycloak, add `provideKeycloak` to the `providers` array in the `ApplicationConfig` object (e.g., in `app.config.ts`). If `initOptions` is included, `keycloak-angular` will automatically use `provideAppInitializer` to handle initialization, following the recommended approach for starting dependencies.
70
71
 
71
- Use the code provided below as an example and implement it's functionality in your application. In this process ensure that the configuration you are providing matches that of your client as configured in Keycloak.
72
+ Use the example code below as a guide for your application.
72
73
 
73
74
  ```ts
74
- import { APP_INITIALIZER, NgModule } from '@angular/core';
75
- import { BrowserModule } from '@angular/platform-browser';
76
- import { KeycloakAngularModule, KeycloakService } from 'keycloak-angular';
77
- import { AppRoutingModule } from './app-routing.module';
78
- import { AppComponent } from './app.component';
79
-
80
- function initializeKeycloak(keycloak: KeycloakService) {
81
- return () =>
82
- keycloak.init({
75
+ import { provideRouter } from '@angular/router';
76
+ import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
77
+ import { provideKeycloak } from 'keycloak-angular';
78
+
79
+ import { routes } from './app.routes';
80
+
81
+ export const appConfig: ApplicationConfig = {
82
+ providers: [
83
+ provideKeycloak({
83
84
  config: {
84
- url: 'http://localhost:8080',
85
- realm: 'your-realm',
86
- clientId: 'your-client-id'
85
+ url: 'keycloak-server-url',
86
+ realm: 'realm-id',
87
+ clientId: 'client-id'
87
88
  },
88
89
  initOptions: {
89
90
  onLoad: 'check-sso',
90
- silentCheckSsoRedirectUri:
91
- window.location.origin + '/assets/silent-check-sso.html'
91
+ silentCheckSsoRedirectUri: window.location.origin + '/silent-check-sso.html'
92
92
  }
93
- });
94
- }
95
-
96
- @NgModule({
97
- declarations: [AppComponent],
98
- imports: [AppRoutingModule, BrowserModule, KeycloakAngularModule],
99
- providers: [
100
- {
101
- provide: APP_INITIALIZER,
102
- useFactory: initializeKeycloak,
103
- multi: true,
104
- deps: [KeycloakService]
105
- }
106
- ],
107
- bootstrap: [AppComponent]
108
- })
109
- export class AppModule {}
93
+ }),
94
+ provideZoneChangeDetection({ eventCoalescing: true }),
95
+ provideRouter(routes)
96
+ ]
97
+ };
110
98
  ```
111
99
 
112
- In the example we have set up Keycloak to use a silent `check-sso`. With this feature enabled, your browser will not do a full redirect to the Keycloak server and back to your application, instead this action will be performed in a hidden iframe, so your application resources only need to be loaded and parsed once by the browser when the app is initialized and not again after the redirect back from Keycloak to your app.
100
+ > **Important:**
101
+ >
102
+ > 1. You do not need to call `provideAppInitializer` if `initOptions` is provided. The library handles this automatically.
103
+ > 2. If you need to control when `keycloak.init` is called, do not pass the `initOptions` to the `provideKeycloak` function. In this case, you are responsible for calling `keycloak.init` manually.
104
+ > 3. For reference, Angular's `APP_INITIALIZER` is deprecated. The recommended approach is to use `provideAppInitializer`.
105
+
106
+ In the example, Keycloak is configured to use a silent `check-sso`. With this feature enabled, your browser avoids a full redirect to the Keycloak server and back to your application. Instead, this action is performed in a hidden iframe, allowing your application resources to be loaded and parsed only once during initialization, rather than reloading after a redirect.
113
107
 
114
- To ensure that Keycloak can communicate through the iframe you will have to serve a static HTML asset from your application at the location provided in `silentCheckSsoRedirectUri`.
108
+ To enable communication through the iframe, you need to serve a static HTML file from your application at the location specified in `silentCheckSsoRedirectUri`.
115
109
 
116
- Create a file called `silent-check-sso.html` in the `assets` directory of your application and paste in the contents as seen below.
110
+ Create a file named `silent-check-sso.html` in the `public` or `assets` directory of your application and paste in the content shown below.
117
111
 
118
112
  ```html
119
113
  <html>
@@ -127,130 +121,230 @@ Create a file called `silent-check-sso.html` in the `assets` directory of your a
127
121
 
128
122
  If you want to know more about these options and various other capabilities of the Keycloak client is recommended to read the [JavaScript Adapter documentation](https://www.keycloak.org/docs/latest/securing_apps/#_javascript_adapter).
129
123
 
124
+ > **Note About NgModules:**
125
+ > Since Keycloak-Angular v19, the KeycloakAngularModule, KeycloakService, KeycloakBearerInterceptor and KeycloakAuthGuard are deprecated.
126
+ > If your application relies on NgModules, the library still has support for it. See more information on how to configure a [NgModule the application](https://github.com/mauriciovigolo/keycloak-angular/docs/ngmodule.md).
127
+
128
+ **Additional Resources**
129
+ For more details, refer to the [provideKeycloak](https://github.com/mauriciovigolo/keycloak-angular/docs/provide.md) documentation.
130
+
130
131
  ## Example project
131
132
 
132
- If you want to see a complete overview a pre-configured client together with a working Keycloak server make sure to check out the [example project](projects/example/README.md) in this repository.
133
+ If you want to see a complete overview a pre-configured client together with a working Keycloak server make sure to check out the [standalone example project](projects/examples/standalone/README.md) in this repository.
133
134
 
134
- ## AuthGuard
135
+ ## Keycloak Angular Features
135
136
 
136
- A generic AuthGuard, `KeycloakAuthGuard` is provided to help you protect authenticated routes in your application. This guard provides you with information to see if the user is logged in and a list of roles from that belong to the user. In your implementation you just need to implement the desired logic to protect your routes.
137
+ Keycloak Angular Features enhance the library's capabilities and make it more modular and composable. These features are executed during application initialization and have access to Angular's Dependency Injection (DI) context. While there is currently only one feature available, more features will be introduced over time to address new scenarios and expand functionality.
137
138
 
138
- To write your own implementation extend the `KeycloakAuthGuard` class and implement the `isAccessAllowed` method. For example the code provided below checks if the user is authenticated and if not the user is requested to sign in. It also checks if the user has the correct roles which could be provided by passing the `roles` field into the data of the route.
139
+ ### Usage
140
+
141
+ The example below adds the `withAutoRefreshToken` feature to Keycloak Angular. Note that this feature depends on two services provided by Keycloak Angular, which are: `AutoRefreshTokenService` and `UserActivityService`.
139
142
 
140
143
  ```ts
141
- import { Injectable } from '@angular/core';
142
- import {
143
- ActivatedRouteSnapshot,
144
- Router,
145
- RouterStateSnapshot
146
- } from '@angular/router';
147
- import { KeycloakAuthGuard, KeycloakService } from 'keycloak-angular';
148
-
149
- @Injectable({
150
- providedIn: 'root'
151
- })
152
- export class AuthGuard extends KeycloakAuthGuard {
153
- constructor(
154
- protected readonly router: Router,
155
- protected readonly keycloak: KeycloakService
156
- ) {
157
- super(router, keycloak);
144
+ import { provideKeycloak, withAutoRefreshToken, AutoRefreshTokenService, UserActivityService } from 'keycloak-angular';
145
+
146
+ export const provideKeycloakAngular = () =>
147
+ provideKeycloak({
148
+ config: {
149
+ url: 'keycloak-server-url',
150
+ realm: 'realm-id',
151
+ clientId: 'client-id'
152
+ },
153
+ initOptions: {
154
+ onLoad: 'check-sso',
155
+ silentCheckSsoRedirectUri: window.location.origin + '/silent-check-sso.html'
156
+ },
157
+ features: [
158
+ withAutoRefreshToken({
159
+ onInactivityTimeout: 'logout',
160
+ sessionTimeout: 60000
161
+ })
162
+ ],
163
+ providers: [AutoRefreshTokenService, UserActivityService]
164
+ });
165
+
166
+ export const appConfig: ApplicationConfig = {
167
+ providers: [provideKeycloakAngular(), provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes)]
168
+ };
169
+ ```
170
+
171
+ ## Guards
172
+
173
+ The `createAuthGuard` function is a higher-order function that simplifies the creation of guards relying on Keycloak data. It can be used to create `CanActivateFn` or `CanActivateChildFn` guards to protect the routes in your application.
174
+
175
+ ### Usage:
176
+
177
+ ```ts
178
+ import { ActivatedRouteSnapshot, CanActivateFn, Router, RouterStateSnapshot, UrlTree } from '@angular/router';
179
+ import { inject } from '@angular/core';
180
+ import { AuthGuardData, createAuthGuard } from 'keycloak-angular';
181
+
182
+ const isAccessAllowed = async (
183
+ route: ActivatedRouteSnapshot,
184
+ _: RouterStateSnapshot,
185
+ authData: AuthGuardData
186
+ ): Promise<boolean | UrlTree> => {
187
+ const { authenticated, grantedRoles } = authData;
188
+
189
+ const requiredRole = route.data['role'];
190
+ if (!requiredRole) {
191
+ return false;
158
192
  }
159
193
 
160
- public async isAccessAllowed(
161
- route: ActivatedRouteSnapshot,
162
- state: RouterStateSnapshot
163
- ) {
164
- // Force the user to log in if currently unauthenticated.
165
- if (!this.authenticated) {
166
- await this.keycloak.login({
167
- redirectUri: window.location.origin + state.url
168
- });
169
- }
170
-
171
- // Get the roles required from the route.
172
- const requiredRoles = route.data.roles;
173
-
174
- // Allow the user to proceed if no additional roles are required to access the route.
175
- if (!Array.isArray(requiredRoles) || requiredRoles.length === 0) {
176
- return true;
177
- }
178
-
179
- // Allow the user to proceed if all the required roles are present.
180
- return requiredRoles.every((role) => this.roles.includes(role));
194
+ const hasRequiredRole = (role: string): boolean =>
195
+ Object.values(grantedRoles.resourceRoles).some((roles) => roles.includes(role));
196
+
197
+ if (authenticated && hasRequiredRole(requiredRole)) {
198
+ return true;
181
199
  }
182
- }
183
- ```
184
200
 
185
- ## HttpClient Interceptor
201
+ const router = inject(Router);
202
+ return router.parseUrl('/forbidden');
203
+ };
186
204
 
187
- By default, all HttpClient requests will add the Authorization header in the format of: `Authorization: Bearer **_TOKEN_**`.
205
+ export const canActivateAuthRole = createAuthGuard<CanActivateFn>(isAccessAllowed);
206
+ ```
188
207
 
189
- There is also the possibility to exclude requests that should not have the authorization header. This is accomplished by implementing the `shouldAddToken` method in the keycloak initialization. For example, the configuration below will not add the token to `GET` requests that match the paths `/assets` or `/clients/public`:
208
+ The `canActivateAuthRole` can be used in the route configuration to protect specific routes.
190
209
 
191
210
  ```ts
192
- await keycloak.init({
193
- config: {
194
- url: 'http://localhost:8080',
195
- realm: 'your-realm',
196
- clientId: 'your-client-id'
197
- },
198
- initOptions: {
199
- onLoad: 'check-sso',
200
- silentCheckSsoRedirectUri:
201
- window.location.origin + '/assets/silent-check-sso.html'
202
- },
203
- shouldAddToken: (request) => {
204
- const { method, url } = request;
205
-
206
- const isGetRequest = 'GET' === method.toUpperCase();
207
- const acceptablePaths = ['/assets', '/clients/public'];
208
- const isAcceptablePathMatch = acceptablePaths.some((path) =>
209
- url.includes(path)
210
- );
211
-
212
- return !(isGetRequest && isAcceptablePathMatch);
211
+ import { Routes } from '@angular/router';
212
+
213
+ import { HomeComponent } from './components/home/home.component';
214
+ import { BooksComponent } from './components/books/books.component';
215
+ import { canActivateAuthRole } from './guards/auth-role.guard';
216
+
217
+ export const routes: Routes = [
218
+ { path: '', component: HomeComponent },
219
+ {
220
+ path: 'books',
221
+ component: BooksComponent,
222
+ canActivate: [canActivateAuthRole],
223
+ data: { role: 'view-books' }
213
224
  }
214
- });
225
+ ];
215
226
  ```
216
227
 
217
- In the case where your application frequently polls an authenticated endpoint, you will find that users will not be logged out automatically over time. If that functionality is not desirable, you can add an http header to the polling requests then configure the `shouldUpdateToken` option in the keycloak initialization.
228
+ ## HttpClient Interceptors
229
+
230
+ Keycloak Angular provides `HttpClient` interceptors for managing the Bearer Token in the HTTP request header.
231
+
232
+ > By default, the library **does not** add the Bearer token to requests. It is the application's responsibility to configure and use the appropriate interceptors.
233
+ >
234
+ > **Important:** This behavior is a significant change from previous versions of the library. The new approach is more declarative, secure, extendable and configurable. There are no services directly interacting with interceptors anymore. All configurations are handled within the interceptor itself.
218
235
 
219
- In the example below, any http requests with the header `token-update: false` will not trigger the user's keycloak token to be updated.
236
+ ### Usage:
220
237
 
221
238
  ```ts
222
- await keycloak.init({
223
- config: {
224
- url: 'http://localhost:8080',
225
- realm: 'your-realm',
226
- clientId: 'your-client-id'
227
- },
228
- initOptions: {
229
- onLoad: 'check-sso',
230
- silentCheckSsoRedirectUri:
231
- window.location.origin + '/assets/silent-check-sso.html'
232
- },
233
- bearerExcludedUrls: ['/assets', '/clients/public'],
234
- shouldUpdateToken(request) {
235
- return !request.headers.get('token-update') === 'false';
236
- }
239
+ import { provideRouter } from '@angular/router';
240
+ import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
241
+ import { provideHttpClient, withInterceptors } from '@angular/common/http';
242
+ import {
243
+ provideKeycloak,
244
+ createInterceptorCondition,
245
+ IncludeBearerTokenCondition,
246
+ INCLUDE_BEARER_TOKEN_INTERCEPTOR_CONFIG
247
+ } from 'keycloak-angular';
248
+
249
+ import { routes } from './app.routes';
250
+
251
+ const urlCondition = createInterceptorCondition<IncludeBearerTokenCondition>({
252
+ urlPattern: /^(http:\/\/localhost:8181)(\/.*)?$/i,
253
+ bearerPrefix: 'Bearer'
237
254
  });
255
+
256
+ export const appConfig: ApplicationConfig = {
257
+ providers: [
258
+ provideKeycloak({
259
+ config: {
260
+ url: 'keycloak-server-url',
261
+ realm: 'realm-id',
262
+ clientId: 'client-id'
263
+ },
264
+ initOptions: {
265
+ onLoad: 'check-sso',
266
+ silentCheckSsoRedirectUri: window.location.origin + '/silent-check-sso.html'
267
+ }
268
+ }),
269
+ {
270
+ provide: INCLUDE_BEARER_TOKEN_INTERCEPTOR_CONFIG,
271
+ useValue: [urlCondition] // <-- Note that multiple conditions might be added.
272
+ },
273
+ provideZoneChangeDetection({ eventCoalescing: true }),
274
+ provideRouter(routes),
275
+ provideHttpClient(withInterceptors([includeBearerTokenInterceptor]))
276
+ ]
277
+ };
278
+ ```
279
+
280
+ **Additional Resources**
281
+ For more details on the available interceptors and their configurations, refer to the [Keycloak HttpClient Interceptors](https://github.com/mauriciovigolo/keycloak-angular/docs/interceptors.md) documentation.
282
+
283
+ ## Directives
284
+
285
+ The library offers the `*kaHasRoles` structural directive to evaluate if the user has the required Realm or Resource Roles to render or not to render a DOM.
286
+
287
+ ### Usage
288
+
289
+ In the component, add the `HasRolesDirective` in the imports list.
290
+
291
+ ```ts
292
+ @Component({
293
+ selector: 'app-menu',
294
+ imports: [RouterModule, HasRolesDirective],
295
+ templateUrl: './menu.component.html',
296
+ styleUrls: ['./menu.component.css']
297
+ })
298
+ export class MenuComponent {}
299
+ ```
300
+
301
+ ```html
302
+ <nav class="menu">
303
+ <div class="developer-status"><strong>Keycloak Events:</strong> {{ keycloakStatus }}</div>
304
+
305
+ <div class="actions">
306
+ <a routerLink="/" class="action-item">Home</a>
307
+ <a routerLink="/books" class="action-item" *kaHasRoles="['view-books']">My Books</a>
308
+ </div>
309
+ </nav>
238
310
  ```
239
311
 
240
- ## Keycloak-js Events
312
+ > **Note:**
313
+ > If no resource is specified in `*kaHasRoles="['view-books']"`, the default resource used will be the Keycloak application client ID.
241
314
 
242
- The callback events from [keycloak-js](https://www.keycloak.org/docs/latest/securing_apps/index.html#javascript-adapter-reference) are available through a RxJS subject which is defined by `keycloakEvents$`.
315
+ ## Keycloak Events Signal
243
316
 
244
- For example you make keycloak-angular auto refreshing your access token when expired:
317
+ Keycloak Angular provides a Signal that allows Angular applications to easily react to `keycloak-js` authorization and session events.
318
+
319
+ This Signal is created during the library's initialization and is available without any additional configuration. It can be accessed by injecting the `KEYCLOAK_EVENT_SIGNAL` injection token.
320
+
321
+ ### Usage
245
322
 
246
323
  ```ts
247
- keycloakService.keycloakEvents$.subscribe({
248
- next(event) {
249
- if (event.type == KeycloakEventType.OnTokenExpired) {
250
- keycloakService.updateToken(20);
251
- }
324
+ @Component({
325
+ selector: 'app-menu',
326
+ templateUrl: './menu.component.html',
327
+ styleUrls: ['./menu.component.css']
328
+ })
329
+ export class MenuComponent {
330
+ authenticated = false;
331
+
332
+ constructor(private readonly keycloak: Keycloak) {
333
+ const keycloakSignal = inject(KEYCLOAK_EVENT_SIGNAL);
334
+
335
+ effect(() => {
336
+ const keycloakEvent = keycloakSignal();
337
+
338
+ if (keycloakEvent.type === KeycloakEventType.Ready) {
339
+ this.authenticated = typeEventArgs<ReadyArgs>(keycloakEvent.args);
340
+ }
341
+
342
+ if (keycloakEvent.type === KeycloakEventType.AuthLogout) {
343
+ this.authenticated = false;
344
+ }
345
+ });
252
346
  }
253
- });
347
+ }
254
348
  ```
255
349
 
256
350
  ## Contributors