@odx/auth 3.3.0 → 3.5.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 (42) hide show
  1. package/README.md +15 -2
  2. package/esm2020/index.mjs +2 -2
  3. package/esm2020/lib/auth.config.mjs +7 -1
  4. package/esm2020/lib/auth.providers.mjs +14 -15
  5. package/esm2020/lib/components/auth-loading-screen/auth-loading-screen.component.mjs +41 -0
  6. package/esm2020/lib/components/index.mjs +2 -0
  7. package/esm2020/lib/helpers/handle-auth-error.mjs +21 -0
  8. package/esm2020/lib/helpers/index.mjs +2 -1
  9. package/esm2020/lib/models/auth-http-cache.mjs +15 -6
  10. package/esm2020/lib/models/index.mjs +2 -1
  11. package/esm2020/lib/models/offline-auth-error-handler.mjs +7 -0
  12. package/esm2020/lib/plugins/index.mjs +2 -1
  13. package/esm2020/lib/plugins/loading-screen-plugin.mjs +16 -0
  14. package/esm2020/plugins/service-connect/lib/helpers/service-connect-plugin-factory.mjs +5 -17
  15. package/esm2020/plugins/service-connect/lib/service-connect-rights.plugin.mjs +1 -2
  16. package/esm2020/plugins/service-connect/lib/service-connect.config.mjs +4 -3
  17. package/fesm2015/odx-auth-plugins-service-connect.mjs +7 -21
  18. package/fesm2015/odx-auth-plugins-service-connect.mjs.map +1 -1
  19. package/fesm2015/odx-auth.mjs +119 -74
  20. package/fesm2015/odx-auth.mjs.map +1 -1
  21. package/fesm2020/odx-auth-plugins-service-connect.mjs +7 -19
  22. package/fesm2020/odx-auth-plugins-service-connect.mjs.map +1 -1
  23. package/fesm2020/odx-auth.mjs +116 -72
  24. package/fesm2020/odx-auth.mjs.map +1 -1
  25. package/index.d.ts +1 -1
  26. package/lib/auth.config.d.ts +4 -0
  27. package/lib/auth.providers.d.ts +1 -1
  28. package/lib/components/auth-loading-screen/auth-loading-screen.component.d.ts +11 -0
  29. package/lib/components/index.d.ts +1 -0
  30. package/lib/helpers/handle-auth-error.d.ts +3 -0
  31. package/lib/helpers/index.d.ts +1 -0
  32. package/lib/models/auth-http-cache.d.ts +2 -1
  33. package/lib/models/index.d.ts +1 -0
  34. package/lib/models/offline-auth-error-handler.d.ts +2 -0
  35. package/lib/plugins/index.d.ts +1 -0
  36. package/lib/plugins/loading-screen-plugin.d.ts +2 -0
  37. package/package.json +1 -1
  38. package/plugins/service-connect/lib/helpers/service-connect-plugin-factory.d.ts +3 -2
  39. package/plugins/service-connect/lib/service-connect-rights.plugin.d.ts +1 -1
  40. package/plugins/service-connect/lib/service-connect.config.d.ts +3 -0
  41. package/esm2020/lib/auth-overlay.component.mjs +0 -46
  42. package/lib/auth-overlay.component.d.ts +0 -9
@@ -1,34 +1,34 @@
1
- import { trigger, transition, useAnimation } from '@angular/animations';
2
- import * as i1 from '@angular/common';
3
- import { CommonModule, NgIf } from '@angular/common';
4
1
  import * as i0 from '@angular/core';
5
- import { InjectionToken, inject, Injectable, Component, ChangeDetectionStrategy, ViewEncapsulation, Directive, EventEmitter, Output, HostListener, Input, NgModule, EnvironmentInjector, makeEnvironmentProviders, ENVIRONMENT_INITIALIZER, APP_INITIALIZER } from '@angular/core';
6
- import { fadeOut } from '@odx/angular/animations';
7
- import { DynamicViewService } from '@odx/angular/cdk/dynamic-view';
8
- import * as i6 from '@odx/angular/components/button';
9
- import { ButtonComponent } from '@odx/angular/components/button';
10
- import { CircularProgressComponent } from '@odx/angular/components/circular-progress';
11
- import * as i7 from '@odx/angular/components/icon';
12
- import { IconComponent } from '@odx/angular/components/icon';
13
- import { LogoDirective } from '@odx/angular/components/logo';
14
- import { BehaviorSubject, filter, map, distinctUntilChanged, share, of, switchMap, combineLatest, tap, take, fromEvent, startWith, merge, shareReplay, EMPTY } from 'rxjs';
15
- import { isString, createConfigTokens, untilDestroyed, Position, buildUrl } from '@odx/angular/utils';
16
- import { HttpErrorResponse } from '@angular/common/http';
17
- import { Router } from '@angular/router';
2
+ import { inject, EnvironmentInjector, InjectionToken, Injectable, Directive, EventEmitter, Output, HostListener, Component, ChangeDetectionStrategy, ViewEncapsulation, Input, NgModule, makeEnvironmentProviders, ENVIRONMENT_INITIALIZER, APP_INITIALIZER } from '@angular/core';
18
3
  import { WindowRef, CoreModule } from '@odx/angular';
19
- import { OAuthService, OAuthErrorEvent, provideOAuthClient, OAuthModuleConfig, OAuthStorage } from 'angular-oauth2-oidc';
20
- import { deepmerge } from 'deepmerge-ts';
21
- import jwtDecode from 'jwt-decode';
22
4
  import * as i3 from '@odx/angular/components/area-header';
23
5
  import { AreaHeaderModule } from '@odx/angular/components/area-header';
24
6
  import * as i8 from '@odx/angular/components/dropdown';
25
7
  import { DropdownModule } from '@odx/angular/components/dropdown';
26
8
  import { HeaderModule } from '@odx/angular/components/header';
27
- import * as i1$1 from '@odx/angular/components/loading-spinner';
9
+ import * as i1 from '@odx/angular/components/loading-spinner';
28
10
  import { LoadingSpinnerDirective, LoadingSpinnerModule } from '@odx/angular/components/loading-spinner';
11
+ import { LogoDirective } from '@odx/angular/components/logo';
12
+ import { isString, createConfigTokens, untilDestroyed, Position, buildUrl } from '@odx/angular/utils';
13
+ import { OAuthErrorEvent, OAuthService, provideOAuthClient, OAuthModuleConfig, OAuthStorage } from 'angular-oauth2-oidc';
14
+ import { HttpErrorResponse } from '@angular/common/http';
15
+ import { Router } from '@angular/router';
16
+ import { deepmerge } from 'deepmerge-ts';
17
+ import jwtDecode from 'jwt-decode';
18
+ import { BehaviorSubject, filter, map, distinctUntilChanged, share, of, switchMap, combineLatest, tap, take, fromEvent, startWith, merge, shareReplay, EMPTY } from 'rxjs';
19
+ import * as i1$1 from '@angular/common';
20
+ import { NgIf, CommonModule } from '@angular/common';
29
21
  import * as i2 from '@ngrx/component';
30
22
  import * as i4 from '@odx/angular/components/avatar';
31
23
  import * as i5 from '@odx/angular/components/action-group';
24
+ import * as i6 from '@odx/angular/components/button';
25
+ import { ButtonComponent } from '@odx/angular/components/button';
26
+ import * as i7 from '@odx/angular/components/icon';
27
+ import { IconComponent } from '@odx/angular/components/icon';
28
+ import { DynamicViewDirective, DynamicViewService } from '@odx/angular/cdk/dynamic-view';
29
+ import { trigger, transition, useAnimation } from '@angular/animations';
30
+ import { fadeOut } from '@odx/angular/animations';
31
+ import { CircularProgressComponent } from '@odx/angular/components/circular-progress';
32
32
 
33
33
  function createInitials(value) {
34
34
  if (!value)
@@ -45,6 +45,25 @@ function createInitials(value) {
45
45
  }, '');
46
46
  }
47
47
 
48
+ function handleAuthError(handlers) {
49
+ const injector = inject(EnvironmentInjector);
50
+ return (error) => {
51
+ injector.runInContext(() => {
52
+ for (const handler of handlers) {
53
+ try {
54
+ handler(error);
55
+ break;
56
+ }
57
+ catch (unhandledError) {
58
+ if (!(unhandledError instanceof OAuthErrorEvent))
59
+ throw error;
60
+ continue;
61
+ }
62
+ }
63
+ });
64
+ };
65
+ }
66
+
48
67
  function resolveEmail(claims) {
49
68
  if (isString(claims['email'])) {
50
69
  return claims['email'];
@@ -84,6 +103,10 @@ const ODX_AUTH_CORE_PLUGINS = new InjectionToken('@odx/auth::CorePlugins', {
84
103
  providedIn: 'root',
85
104
  factory: () => [],
86
105
  });
106
+ const ODX_AUTH_ERROR_HANDLERS = new InjectionToken('@odx/auth::ErrorHandlers', {
107
+ providedIn: 'root',
108
+ factory: () => [],
109
+ });
87
110
  const ODX_AUTH_PLUGINS = new InjectionToken('@odx/auth::Plugins', {
88
111
  providedIn: 'root',
89
112
  factory: () => {
@@ -108,6 +131,8 @@ const { AuthDefaultConfig, AuthConfig, injectAuthConfig, provideAuthConfig } = c
108
131
  showRedirectOverlay: false,
109
132
  plugins: [],
110
133
  defaultAuthorizedHandler: null,
134
+ enableLoadingScreen: false,
135
+ loadingScreenMessage: null,
111
136
  });
112
137
 
113
138
  const ODX_AUTH_HTTP_CACHE_STORAGE = new InjectionToken('@odx/auth::AuthHttpCacheStorage', {
@@ -117,18 +142,26 @@ const ODX_AUTH_HTTP_CACHE_STORAGE = new InjectionToken('@odx/auth::AuthHttpCache
117
142
  class AuthHttpCache {
118
143
  constructor() {
119
144
  this.cacheStorage = inject(ODX_AUTH_HTTP_CACHE_STORAGE);
145
+ this.oauthService = inject(OAuthService);
120
146
  }
121
147
  async delete(request) {
122
148
  const cache = await this.cacheStorage.open(AuthHttpCache.CACHE_KEY);
123
149
  return cache.delete(request);
124
150
  }
125
- async request(request) {
151
+ async request(requestInfo, authentication = false) {
152
+ const request = new Request(requestInfo);
153
+ const authorizationHeader = authentication ? this.oauthService.authorizationHeader() : null;
154
+ if (authorizationHeader) {
155
+ request.headers.set('Authorization', authorizationHeader);
156
+ }
126
157
  const cache = await this.cacheStorage.open(AuthHttpCache.CACHE_KEY);
127
- try {
128
- await cache.add(request);
158
+ const response = await fetch(request).catch(() => null);
159
+ if (response?.ok) {
160
+ await cache.put(request, response);
129
161
  }
130
- catch {
131
- // ignore request errors
162
+ if (authorizationHeader && response?.status === 401) {
163
+ await cache.delete(request);
164
+ await this.oauthService.refreshToken().catch(() => null);
132
165
  }
133
166
  return cache.match(request).then((res) => res?.json() ?? null);
134
167
  }
@@ -165,6 +198,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImpor
165
198
  args: [{ providedIn: 'root' }]
166
199
  }] });
167
200
 
201
+ const offlineAuthErrorHandler = (error) => {
202
+ if (error.type === 'discovery_document_load_error' && error.reason?.status === 504) {
203
+ return;
204
+ }
205
+ throw error;
206
+ };
207
+
168
208
  class AuthService {
169
209
  constructor() {
170
210
  this.authConfig = injectAuthConfig();
@@ -294,38 +334,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImpor
294
334
  args: [{ providedIn: 'root' }]
295
335
  }], ctorParameters: function () { return []; } });
296
336
 
297
- class AuthOverlayComponent {
298
- constructor() {
299
- this.icon$ = inject(AuthService).isRedirecting$.pipe(startWith(false), distinctUntilChanged(), map((isRedirecting) => (isRedirecting ? 'link-external' : 'user')));
300
- }
301
- static initialize() {
302
- const dynamicViewService = inject(DynamicViewService);
303
- inject(AuthService).isLoading$.subscribe((isLoading) => {
304
- if (isLoading) {
305
- AuthOverlayComponent.instance ?? (AuthOverlayComponent.instance = dynamicViewService.createView(AuthOverlayComponent));
306
- }
307
- else {
308
- AuthOverlayComponent.instance?.destroy();
309
- AuthOverlayComponent.instance = null;
310
- }
311
- });
312
- }
313
- }
314
- AuthOverlayComponent.instance = null;
315
- AuthOverlayComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: AuthOverlayComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
316
- AuthOverlayComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", type: AuthOverlayComponent, isStandalone: true, selector: "div.odx-auth-overlay", host: { properties: { "@hostAnimation": "true" } }, ngImport: i0, template: "<div class=\"odx-auth-overlay__content\" odxLayout=\"grid 12 horizontal-center vertical-center gap-small\">\n <odx-logo size=\"large\"></odx-logo>\n <odx-circular-progress class=\"odx-auth-overlay-spinner\" value=\"-1\" size=\"medium\" stroke=\"3\">\n <odx-icon [name]=\"icon$ | async\" iconSet=\"core\"></odx-icon>\n </odx-circular-progress>\n</div>\n", styles: ["@keyframes odx-auth-overlay-animation{0%{opacity:0;transform:translate(-50%,-50%) scale(.9)}to{opacity:1;transform:translate(-50%,-50%)}}.odx-auth-overlay{--odx-c-highlight: var(--odx-c-primary);background-color:var(--odx-c-background-content);position:fixed;z-index:var(--odx-v-layer-5);inset:0}.odx-auth-overlay__content{top:50%;left:50%;transform:translate(-50%,-50%);position:absolute;animation:odx-auth-overlay-animation .75s ease}.odx-auth-overlay-spinner{position:relative}.odx-auth-overlay-spinner .odx-icon{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "component", type: IconComponent, selector: "odx-icon", inputs: ["inline", "size", "name", "iconSet"] }, { kind: "directive", type: LogoDirective, selector: "odx-logo", inputs: ["size", "variant"] }, { kind: "component", type: CircularProgressComponent, selector: "odx-circular-progress", inputs: ["stroke", "size", "value"] }], animations: [trigger('hostAnimation', [transition(':leave', useAnimation(fadeOut()))])], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
317
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: AuthOverlayComponent, decorators: [{
318
- type: Component,
319
- args: [{ standalone: true, selector: 'div.odx-auth-overlay', imports: [CommonModule, ButtonComponent, IconComponent, LogoDirective, CircularProgressComponent], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
320
- '[@hostAnimation]': 'true',
321
- }, animations: [trigger('hostAnimation', [transition(':leave', useAnimation(fadeOut()))])], template: "<div class=\"odx-auth-overlay__content\" odxLayout=\"grid 12 horizontal-center vertical-center gap-small\">\n <odx-logo size=\"large\"></odx-logo>\n <odx-circular-progress class=\"odx-auth-overlay-spinner\" value=\"-1\" size=\"medium\" stroke=\"3\">\n <odx-icon [name]=\"icon$ | async\" iconSet=\"core\"></odx-icon>\n </odx-circular-progress>\n</div>\n", styles: ["@keyframes odx-auth-overlay-animation{0%{opacity:0;transform:translate(-50%,-50%) scale(.9)}to{opacity:1;transform:translate(-50%,-50%)}}.odx-auth-overlay{--odx-c-highlight: var(--odx-c-primary);background-color:var(--odx-c-background-content);position:fixed;z-index:var(--odx-v-layer-5);inset:0}.odx-auth-overlay__content{top:50%;left:50%;transform:translate(-50%,-50%);position:absolute;animation:odx-auth-overlay-animation .75s ease}.odx-auth-overlay-spinner{position:relative}.odx-auth-overlay-spinner .odx-icon{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}\n"] }]
322
- }] });
323
- function initalizeAuthOverlay() {
324
- if (!injectAuthConfig().showRedirectOverlay)
325
- return;
326
- AuthOverlayComponent.initialize();
327
- }
328
-
329
337
  class AuthActionDirective {
330
338
  constructor() {
331
339
  this.takeUntilDestroyed = untilDestroyed();
@@ -357,7 +365,7 @@ class SignInDirective extends AuthActionDirective {
357
365
  }
358
366
  }
359
367
  SignInDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: SignInDirective, deps: null, target: i0.ɵɵFactoryTarget.Directive });
360
- SignInDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.9", type: SignInDirective, isStandalone: true, selector: "[odxButton][odxAuthSignIn]", outputs: { afterSignIn: "odxAuthSignIn" }, host: { listeners: { "click": "handleClick()" } }, usesInheritance: true, hostDirectives: [{ directive: i1$1.LoadingSpinnerDirective }], ngImport: i0 });
368
+ SignInDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.9", type: SignInDirective, isStandalone: true, selector: "[odxButton][odxAuthSignIn]", outputs: { afterSignIn: "odxAuthSignIn" }, host: { listeners: { "click": "handleClick()" } }, usesInheritance: true, hostDirectives: [{ directive: i1.LoadingSpinnerDirective }], ngImport: i0 });
361
369
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: SignInDirective, decorators: [{
362
370
  type: Directive,
363
371
  args: [{
@@ -385,7 +393,7 @@ class SignOutDirective extends AuthActionDirective {
385
393
  }
386
394
  }
387
395
  SignOutDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: SignOutDirective, deps: null, target: i0.ɵɵFactoryTarget.Directive });
388
- SignOutDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.9", type: SignOutDirective, isStandalone: true, selector: "[odxButton][odxAuthSignOut]", outputs: { afterSignOut: "odxAuthSignOut" }, host: { listeners: { "click": "handleClick()" } }, usesInheritance: true, hostDirectives: [{ directive: i1$1.LoadingSpinnerDirective }], ngImport: i0 });
396
+ SignOutDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.9", type: SignOutDirective, isStandalone: true, selector: "[odxButton][odxAuthSignOut]", outputs: { afterSignOut: "odxAuthSignOut" }, host: { listeners: { "click": "handleClick()" } }, usesInheritance: true, hostDirectives: [{ directive: i1.LoadingSpinnerDirective }], ngImport: i0 });
389
397
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: SignOutDirective, decorators: [{
390
398
  type: Directive,
391
399
  args: [{
@@ -415,7 +423,7 @@ class AuthComponent {
415
423
  }
416
424
  }
417
425
  AuthComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: AuthComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
418
- AuthComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", type: AuthComponent, isStandalone: true, selector: "odx-auth", inputs: { signInButtonText: "signInButtonText", signOutButtonText: "signOutButtonText" }, ngImport: i0, template: "<odx-action-group>\n <ng-template [ngrxLet]=\"{ idClaims: authService.identityClaims$, isAuthenticated: authService.isAuthenticated$ }\" let-vm>\n <ng-template [ngIf]=\"vm.isAuthenticated\" [ngIfElse]=\"notAuthenticated\">\n <button odxButton [odxDropdown]=\"userProfileMenu\" [odxDropdownOptions]=\"dropdownOptions\" data-testid=\"odx-auth-user-profile-button\">\n <ng-template [ngTemplateOutlet]=\"userAvatar\"></ng-template>\n </button>\n <ng-template #userProfileMenu>\n <odx-area-header class=\"odx-padding-x-12\" size=\"small\">\n <ng-template [ngTemplateOutlet]=\"userAvatar\" ngProjectAs=\"odx-avatar\"></ng-template>\n {{ vm.idClaims?.username }}\n <odx-area-header-subtitle>\n {{ vm.idClaims?.email }}\n </odx-area-header-subtitle>\n </odx-area-header>\n <ng-content></ng-content>\n <div class=\"odx-margin-top-12\" odxLayout=\"flex vertical-center\">\n <odx-logo odxLayout=\"auto\" class=\"odx-margin-left-12 odx-margin-right-auto\"></odx-logo>\n <button odxButton odxAuthSignOut variant=\"ghost\" data-testid=\"odx-auth-sign-out-button\">\n {{ signOutButtonText }}\n <odx-icon name=\"logout\" alignRight></odx-icon>\n </button>\n </div>\n </ng-template>\n </ng-template>\n <ng-template #notAuthenticated>\n <button class=\"odx-auth-sign-in\" odxButton odxAuthSignIn variant=\"secondary\" data-testid=\"odx-auth-sign-in-button\">\n <odx-icon name=\"user\" alignLeft></odx-icon>\n {{ signInButtonText }}\n </button>\n </ng-template>\n <ng-template #userAvatar>\n <odx-avatar class=\"odx-auth-user-avatar\">\n {{ vm.idClaims?.initials ?? '' }}\n </odx-avatar>\n </ng-template>\n </ng-template>\n</odx-action-group>\n", styles: [".odx-auth-user-profile .odx-dropdown__inner>.odx-area-header{max-width:max(360px,25vw);min-width:296px}\n"], dependencies: [{ kind: "ngmodule", type: CoreModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2.LetDirective, selector: "[ngrxLet]", inputs: ["ngrxLet", "ngrxLetSuspenseTpl"] }, { kind: "ngmodule", type: AreaHeaderModule }, { kind: "component", type: i3.AreaHeaderComponent, selector: "odx-area-header", inputs: ["size"] }, { kind: "directive", type: i3.AreaHeaderSubtitleDirective, selector: "odx-area-header-subtitle" }, { kind: "component", type: i4.AvatarComponent, selector: "odx-avatar", inputs: ["size", "variant"] }, { kind: "component", type: i5.ActionGroupComponent, selector: "odx-action-group", inputs: ["reverse"] }, { kind: "component", type: i6.ButtonComponent, selector: "button[odxButton], a[odxButton]", inputs: ["variant", "size"] }, { kind: "component", type: i7.IconComponent, selector: "odx-icon", inputs: ["inline", "size", "name", "iconSet"] }, { kind: "ngmodule", type: DropdownModule }, { kind: "directive", type: i8.DropdownDirective, selector: "[odxDropdown]", inputs: ["odxDropdown", "odxDropdownDisabled", "odxDropdownShowLoader", "odxDropdownClickOutsideActive", "odxDropdownOptions", "odxDropdownReferenceElement", "odxDropdownTriggerElement", "odxDropdownHost", "odxDropdownOpenTrigger", "odxDropdownCloseTrigger"], outputs: ["odxDropdownBeforeOpen", "odxDropdownAfterOpen", "odxDropdownBeforeClose", "odxDropdownAfterClose"], exportAs: ["odxDropdown"] }, { kind: "ngmodule", type: HeaderModule }, { kind: "directive", type: LogoDirective, selector: "odx-logo", inputs: ["size", "variant"] }, { kind: "directive", type: SignInDirective, selector: "[odxButton][odxAuthSignIn]", outputs: ["odxAuthSignIn"] }, { kind: "directive", type: SignOutDirective, selector: "[odxButton][odxAuthSignOut]", outputs: ["odxAuthSignOut"] }, { kind: "ngmodule", type: LoadingSpinnerModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
426
+ AuthComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", type: AuthComponent, isStandalone: true, selector: "odx-auth", inputs: { signInButtonText: "signInButtonText", signOutButtonText: "signOutButtonText" }, ngImport: i0, template: "<odx-action-group>\n <ng-template [ngrxLet]=\"{ idClaims: authService.identityClaims$, isAuthenticated: authService.isAuthenticated$ }\" let-vm>\n <ng-template [ngIf]=\"vm.isAuthenticated\" [ngIfElse]=\"notAuthenticated\">\n <button odxButton [odxDropdown]=\"userProfileMenu\" [odxDropdownOptions]=\"dropdownOptions\" data-testid=\"odx-auth-user-profile-button\">\n <ng-template [ngTemplateOutlet]=\"userAvatar\"></ng-template>\n </button>\n <ng-template #userProfileMenu>\n <odx-area-header class=\"odx-padding-x-12\" size=\"small\">\n <ng-template [ngTemplateOutlet]=\"userAvatar\" ngProjectAs=\"odx-avatar\"></ng-template>\n {{ vm.idClaims?.username }}\n <odx-area-header-subtitle>\n {{ vm.idClaims?.email }}\n </odx-area-header-subtitle>\n </odx-area-header>\n <ng-content></ng-content>\n <div class=\"odx-margin-top-12\" odxLayout=\"flex vertical-center\">\n <odx-logo odxLayout=\"auto\" class=\"odx-margin-left-12 odx-margin-right-auto\"></odx-logo>\n <button odxButton odxAuthSignOut variant=\"ghost\" data-testid=\"odx-auth-sign-out-button\">\n {{ signOutButtonText }}\n <odx-icon name=\"logout\" alignRight></odx-icon>\n </button>\n </div>\n </ng-template>\n </ng-template>\n <ng-template #notAuthenticated>\n <button class=\"odx-auth-sign-in\" odxButton odxAuthSignIn variant=\"secondary\" data-testid=\"odx-auth-sign-in-button\">\n <odx-icon name=\"user\" alignLeft></odx-icon>\n {{ signInButtonText }}\n </button>\n </ng-template>\n <ng-template #userAvatar>\n <odx-avatar class=\"odx-auth-user-avatar\">\n {{ vm.idClaims?.initials ?? '' }}\n </odx-avatar>\n </ng-template>\n </ng-template>\n</odx-action-group>\n", styles: [".odx-auth-user-profile .odx-dropdown__inner>.odx-area-header{max-width:max(360px,25vw);min-width:296px}\n"], dependencies: [{ kind: "ngmodule", type: CoreModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2.LetDirective, selector: "[ngrxLet]", inputs: ["ngrxLet", "ngrxLetSuspenseTpl"] }, { kind: "ngmodule", type: AreaHeaderModule }, { kind: "component", type: i3.AreaHeaderComponent, selector: "odx-area-header", inputs: ["size"] }, { kind: "directive", type: i3.AreaHeaderSubtitleDirective, selector: "odx-area-header-subtitle" }, { kind: "component", type: i4.AvatarComponent, selector: "odx-avatar", inputs: ["size", "variant"] }, { kind: "component", type: i5.ActionGroupComponent, selector: "odx-action-group", inputs: ["reverse"] }, { kind: "component", type: i6.ButtonComponent, selector: "button[odxButton], a[odxButton]", inputs: ["variant", "size"] }, { kind: "component", type: i7.IconComponent, selector: "odx-icon", inputs: ["inline", "size", "name", "iconSet"] }, { kind: "ngmodule", type: DropdownModule }, { kind: "directive", type: i8.DropdownDirective, selector: "[odxDropdown]", inputs: ["odxDropdown", "odxDropdownDisabled", "odxDropdownShowLoader", "odxDropdownClickOutsideActive", "odxDropdownOptions", "odxDropdownReferenceElement", "odxDropdownTriggerElement", "odxDropdownHost", "odxDropdownOpenTrigger", "odxDropdownCloseTrigger"], outputs: ["odxDropdownBeforeOpen", "odxDropdownAfterOpen", "odxDropdownBeforeClose", "odxDropdownAfterClose"], exportAs: ["odxDropdown"] }, { kind: "ngmodule", type: HeaderModule }, { kind: "directive", type: LogoDirective, selector: "odx-logo", inputs: ["size", "variant"] }, { kind: "directive", type: SignInDirective, selector: "[odxButton][odxAuthSignIn]", outputs: ["odxAuthSignIn"] }, { kind: "directive", type: SignOutDirective, selector: "[odxButton][odxAuthSignOut]", outputs: ["odxAuthSignOut"] }, { kind: "ngmodule", type: LoadingSpinnerModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
419
427
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: AuthComponent, decorators: [{
420
428
  type: Component,
421
429
  args: [{ standalone: true, selector: 'odx-auth', imports: [CoreModule, AreaHeaderModule, DropdownModule, HeaderModule, LogoDirective, SignInDirective, SignOutDirective, LoadingSpinnerModule], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<odx-action-group>\n <ng-template [ngrxLet]=\"{ idClaims: authService.identityClaims$, isAuthenticated: authService.isAuthenticated$ }\" let-vm>\n <ng-template [ngIf]=\"vm.isAuthenticated\" [ngIfElse]=\"notAuthenticated\">\n <button odxButton [odxDropdown]=\"userProfileMenu\" [odxDropdownOptions]=\"dropdownOptions\" data-testid=\"odx-auth-user-profile-button\">\n <ng-template [ngTemplateOutlet]=\"userAvatar\"></ng-template>\n </button>\n <ng-template #userProfileMenu>\n <odx-area-header class=\"odx-padding-x-12\" size=\"small\">\n <ng-template [ngTemplateOutlet]=\"userAvatar\" ngProjectAs=\"odx-avatar\"></ng-template>\n {{ vm.idClaims?.username }}\n <odx-area-header-subtitle>\n {{ vm.idClaims?.email }}\n </odx-area-header-subtitle>\n </odx-area-header>\n <ng-content></ng-content>\n <div class=\"odx-margin-top-12\" odxLayout=\"flex vertical-center\">\n <odx-logo odxLayout=\"auto\" class=\"odx-margin-left-12 odx-margin-right-auto\"></odx-logo>\n <button odxButton odxAuthSignOut variant=\"ghost\" data-testid=\"odx-auth-sign-out-button\">\n {{ signOutButtonText }}\n <odx-icon name=\"logout\" alignRight></odx-icon>\n </button>\n </div>\n </ng-template>\n </ng-template>\n <ng-template #notAuthenticated>\n <button class=\"odx-auth-sign-in\" odxButton odxAuthSignIn variant=\"secondary\" data-testid=\"odx-auth-sign-in-button\">\n <odx-icon name=\"user\" alignLeft></odx-icon>\n {{ signInButtonText }}\n </button>\n </ng-template>\n <ng-template #userAvatar>\n <odx-avatar class=\"odx-auth-user-avatar\">\n {{ vm.idClaims?.initials ?? '' }}\n </odx-avatar>\n </ng-template>\n </ng-template>\n</odx-action-group>\n", styles: [".odx-auth-user-profile .odx-dropdown__inner>.odx-area-header{max-width:max(360px,25vw);min-width:296px}\n"] }]
@@ -444,7 +452,7 @@ class AuthDirective {
444
452
  }
445
453
  }
446
454
  AuthDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: AuthDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
447
- AuthDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.9", type: AuthDirective, isStandalone: true, selector: "ng-template[odxAuth]", inputs: { authorizationHandler: ["odxAuth", "authorizationHandler"], elseTemplate: ["odxAuthElse", "elseTemplate"] }, hostDirectives: [{ directive: i1.NgIf }], ngImport: i0 });
455
+ AuthDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.9", type: AuthDirective, isStandalone: true, selector: "ng-template[odxAuth]", inputs: { authorizationHandler: ["odxAuth", "authorizationHandler"], elseTemplate: ["odxAuthElse", "elseTemplate"] }, hostDirectives: [{ directive: i1$1.NgIf }], ngImport: i0 });
448
456
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: AuthDirective, decorators: [{
449
457
  type: Directive,
450
458
  args: [{
@@ -509,6 +517,44 @@ const coreIdentityPlugin = () => {
509
517
  };
510
518
  };
511
519
 
520
+ class AuthLoadingScreenComponent {
521
+ constructor() {
522
+ this.authConfig = injectAuthConfig();
523
+ this.icon$ = inject(AuthService).isRedirecting$.pipe(startWith(false), distinctUntilChanged(), map((isRedirecting) => (isRedirecting ? 'link-external' : 'user')));
524
+ }
525
+ static initialize(authService, dynamicViewService) {
526
+ authService.isLoading$.subscribe((isLoading) => {
527
+ if (isLoading) {
528
+ AuthLoadingScreenComponent.instance ?? (AuthLoadingScreenComponent.instance = dynamicViewService.createView(AuthLoadingScreenComponent));
529
+ }
530
+ else {
531
+ AuthLoadingScreenComponent.instance?.destroy();
532
+ AuthLoadingScreenComponent.instance = null;
533
+ }
534
+ });
535
+ }
536
+ }
537
+ AuthLoadingScreenComponent.instance = null;
538
+ AuthLoadingScreenComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: AuthLoadingScreenComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
539
+ AuthLoadingScreenComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", type: AuthLoadingScreenComponent, isStandalone: true, selector: "div.odx-auth-loading-screen", host: { properties: { "@hostAnimation": "true" } }, ngImport: i0, template: "<div class=\"odx-auth-loading-screen__content\" odxLayout=\"grid 12 horizontal-center vertical-center gap-small\">\n <odx-logo size=\"large\"></odx-logo>\n <odx-circular-progress class=\"odx-auth-loading-screen__spinner\" value=\"-1\" size=\"medium\" stroke=\"3\">\n <odx-icon [name]=\"icon$ | async\" iconSet=\"core\"></odx-icon>\n </odx-circular-progress>\n <p class=\"odx-auth-loading-screen__message\" *ngIf=\"authConfig.loadingScreenMessage as content\">\n <ng-template [odxDynamicView]=\"content\"></ng-template>\n </p>\n</div>\n", styles: ["@keyframes odx-auth-loading-screen-animation{0%{opacity:0;transform:translate(-50%,-50%) scale(.9)}to{opacity:1;transform:translate(-50%,-50%)}}.odx-auth-loading-screen{--odx-c-highlight: var(--odx-c-primary);background-color:var(--odx-c-background-content);position:fixed;z-index:var(--odx-v-layer-5);inset:0}.odx-auth-loading-screen__content{top:50%;left:50%;transform:translate(-50%,-50%);position:absolute;animation:odx-auth-loading-screen-animation .75s ease}.odx-auth-loading-screen__message{text-align:center}.odx-auth-loading-screen__spinner{position:relative}.odx-auth-loading-screen__spinner .odx-icon{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "component", type: IconComponent, selector: "odx-icon", inputs: ["inline", "size", "name", "iconSet"] }, { kind: "directive", type: LogoDirective, selector: "odx-logo", inputs: ["size", "variant"] }, { kind: "component", type: CircularProgressComponent, selector: "odx-circular-progress", inputs: ["stroke", "size", "value"] }, { kind: "directive", type: DynamicViewDirective, selector: "ng-template[odxDynamicView]", inputs: ["odxDynamicView", "odxDynamicViewInjector", "odxDynamicViewContext"] }], animations: [trigger('hostAnimation', [transition(':leave', useAnimation(fadeOut()))])], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
540
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: AuthLoadingScreenComponent, decorators: [{
541
+ type: Component,
542
+ args: [{ standalone: true, selector: 'div.odx-auth-loading-screen', imports: [CommonModule, ButtonComponent, IconComponent, LogoDirective, CircularProgressComponent, DynamicViewDirective], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
543
+ '[@hostAnimation]': 'true',
544
+ }, animations: [trigger('hostAnimation', [transition(':leave', useAnimation(fadeOut()))])], template: "<div class=\"odx-auth-loading-screen__content\" odxLayout=\"grid 12 horizontal-center vertical-center gap-small\">\n <odx-logo size=\"large\"></odx-logo>\n <odx-circular-progress class=\"odx-auth-loading-screen__spinner\" value=\"-1\" size=\"medium\" stroke=\"3\">\n <odx-icon [name]=\"icon$ | async\" iconSet=\"core\"></odx-icon>\n </odx-circular-progress>\n <p class=\"odx-auth-loading-screen__message\" *ngIf=\"authConfig.loadingScreenMessage as content\">\n <ng-template [odxDynamicView]=\"content\"></ng-template>\n </p>\n</div>\n", styles: ["@keyframes odx-auth-loading-screen-animation{0%{opacity:0;transform:translate(-50%,-50%) scale(.9)}to{opacity:1;transform:translate(-50%,-50%)}}.odx-auth-loading-screen{--odx-c-highlight: var(--odx-c-primary);background-color:var(--odx-c-background-content);position:fixed;z-index:var(--odx-v-layer-5);inset:0}.odx-auth-loading-screen__content{top:50%;left:50%;transform:translate(-50%,-50%);position:absolute;animation:odx-auth-loading-screen-animation .75s ease}.odx-auth-loading-screen__message{text-align:center}.odx-auth-loading-screen__spinner{position:relative}.odx-auth-loading-screen__spinner .odx-icon{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}\n"] }]
545
+ }] });
546
+
547
+ const loadingScreenPlugin = () => {
548
+ const { enableLoadingScreen, showRedirectOverlay } = injectAuthConfig();
549
+ const dynamicViewService = inject(DynamicViewService);
550
+ return (authService) => {
551
+ if (enableLoadingScreen || showRedirectOverlay) {
552
+ AuthLoadingScreenComponent.initialize(authService, dynamicViewService);
553
+ }
554
+ return of({});
555
+ };
556
+ };
557
+
512
558
  function configureInterceptor() {
513
559
  const { allowedUrls } = injectAuthConfig();
514
560
  return {
@@ -518,11 +564,10 @@ function configureInterceptor() {
518
564
  },
519
565
  };
520
566
  }
521
- function initializeAuthErrorHandler() {
567
+ function initializeAuthErrorHandlers() {
522
568
  const authService = inject(AuthService);
523
- const injector = inject(EnvironmentInjector);
524
- const { errorHandler } = injectAuthConfig();
525
- authService.errors$.pipe(tap((error) => injector.runInContext(() => errorHandler(error)))).subscribe();
569
+ const handler = handleAuthError(inject(ODX_AUTH_ERROR_HANDLERS));
570
+ authService.errors$.pipe(tap((error) => handler(error))).subscribe();
526
571
  }
527
572
  function initalizeAuthConfig() {
528
573
  const { clientId, scopes, redirectPath, environment, postLogoutRedirectUrl, issuer, timeoutFactor, discoveryUrl } = injectAuthConfig();
@@ -552,13 +597,12 @@ function provideAuth(config) {
552
597
  },
553
598
  {
554
599
  provide: ENVIRONMENT_INITIALIZER,
555
- useValue: initializeAuthErrorHandler,
600
+ useValue: initializeAuthErrorHandlers,
556
601
  multi: true,
557
602
  },
558
603
  {
559
- provide: ENVIRONMENT_INITIALIZER,
560
- useValue: initalizeAuthOverlay,
561
- multi: true,
604
+ provide: ODX_AUTH_ERROR_HANDLERS,
605
+ useFactory: () => [offlineAuthErrorHandler, injectAuthConfig().errorHandler],
562
606
  },
563
607
  {
564
608
  provide: APP_INITIALIZER,
@@ -567,11 +611,11 @@ function provideAuth(config) {
567
611
  },
568
612
  {
569
613
  provide: OAuthStorage,
570
- useFactory: () => inject(AuthConfig).storage ?? inject(WindowRef).nativeWindow.localStorage,
614
+ useFactory: () => injectAuthConfig().storage ?? inject(WindowRef).nativeWindow.localStorage,
571
615
  },
572
616
  {
573
617
  provide: ODX_AUTH_CORE_PLUGINS,
574
- useValue: [coreIdentityPlugin],
618
+ useValue: [coreIdentityPlugin, loadingScreenPlugin],
575
619
  },
576
620
  ]);
577
621
  }
@@ -598,5 +642,5 @@ function unauthGuard(authorizedHandler, redirectTo) {
598
642
  * Generated bundle index. Do not edit.
599
643
  */
600
644
 
601
- export { AuthActionDirective, AuthComponent, AuthConfig, AuthDefaultConfig, AuthDirective, AuthHttpCache, AuthModule, AuthOverlayComponent, AuthPluginManager, AuthService, DEFAULT_AUTH_SCOPES, DEFAULT_ISSUERS, ODX_AUTH_CORE_PLUGINS, ODX_AUTH_HTTP_CACHE_STORAGE, ODX_AUTH_PLUGINS, SignInDirective, SignOutDirective, authGuard, configureInterceptor, coreIdentityPlugin, createInitials, initalizeAuthConfig, initalizeAuthOverlay, initializeAuthErrorHandler, injectAuthConfig, provideAuth, provideAuthConfig, resolveEmail, resolveUsername, unauthGuard };
645
+ export { AuthActionDirective, AuthComponent, AuthConfig, AuthDefaultConfig, AuthDirective, AuthHttpCache, AuthLoadingScreenComponent, AuthModule, AuthPluginManager, AuthService, DEFAULT_AUTH_SCOPES, DEFAULT_ISSUERS, ODX_AUTH_CORE_PLUGINS, ODX_AUTH_ERROR_HANDLERS, ODX_AUTH_HTTP_CACHE_STORAGE, ODX_AUTH_PLUGINS, SignInDirective, SignOutDirective, authGuard, configureInterceptor, coreIdentityPlugin, createInitials, handleAuthError, initalizeAuthConfig, initializeAuthErrorHandlers, injectAuthConfig, loadingScreenPlugin, offlineAuthErrorHandler, provideAuth, provideAuthConfig, resolveEmail, resolveUsername, unauthGuard };
602
646
  //# sourceMappingURL=odx-auth.mjs.map