@meshmakers/shared-auth 3.4.890 → 3.4.900

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.
package/README.md CHANGED
@@ -142,6 +142,32 @@ The interceptor adds the `Authorization: Bearer <token>` header to:
142
142
  - Same-origin requests (relative URLs)
143
143
  - Requests to configured `wellKnownServiceUris`
144
144
 
145
+ It also recovers from an expired access token: when a request that carried a token
146
+ comes back `401`, the token is refreshed and the request is retried once with the new
147
+ bearer. The refresh is single-flight (concurrent 401s share one refresh), and a `401`
148
+ that arrives after that refresh already finished reuses its result instead of starting
149
+ another one. A retry is never itself retried. Requests without a token and the token
150
+ endpoint itself are left alone, and if the refresh fails the original `401` reaches the
151
+ caller.
152
+
153
+ ### Error classification
154
+
155
+ `authorizationRefusal(error)` tells a `401` and a `403` apart on an error typed `unknown`,
156
+ so each host can phrase the outcome in its own words without writing that status check again.
157
+
158
+ ```typescript
159
+ import { authorizationRefusal } from '@meshmakers/shared-auth';
160
+
161
+ switch (authorizationRefusal(error)) {
162
+ case 'forbidden': return this.translate.instant('pipeline.forbidden');
163
+ case 'unauthorized': return this.translate.instant('pipeline.unauthorized');
164
+ default: return null; // not an authorization problem — leave it to the caller
165
+ }
166
+ ```
167
+
168
+ The status is read structurally and must be a `number`, so a rethrown error that lost its
169
+ `HttpErrorResponse` prototype still classifies, while a body that merely carries `"403"` does not.
170
+
145
171
  ### UI Components
146
172
 
147
173
  #### LoginAppBarSectionComponent
@@ -1,7 +1,8 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { inject, signal, computed, Injectable, makeEnvironmentProviders } from '@angular/core';
3
3
  import { OAuthStorage, OAuthService, provideOAuthClient } from 'angular-oauth2-oidc';
4
- import { HttpParams } from '@angular/common/http';
4
+ import { HttpParams, HttpErrorResponse } from '@angular/common/http';
5
+ import { catchError, throwError, from, switchMap } from 'rxjs';
5
6
  import { Router } from '@angular/router';
6
7
 
7
8
  /**
@@ -1041,13 +1042,98 @@ function isSameOriginUrl(req) {
1041
1042
  function isKnownServiceUri(req, serviceUris) {
1042
1043
  if (serviceUris != null) {
1043
1044
  for (const serviceUri of serviceUris) {
1044
- if (req.url.startsWith(serviceUri)) {
1045
+ // An unset host must never match. Blank is a prefix of every URL, and the
1046
+ // platform answers `/` for an unconfigured service, which would also match
1047
+ // protocol-relative URLs that isSameOriginUrl deliberately rejects.
1048
+ if (!serviceUri || serviceUri === '/' || !req.url.startsWith(serviceUri)) {
1049
+ continue;
1050
+ }
1051
+ // A bare prefix test would also accept a host that merely begins with a configured
1052
+ // one — `https://api.example.com.attacker.test` — and hand it the operator's bearer.
1053
+ // The match must therefore end on a path, query or fragment boundary.
1054
+ const remainder = req.url.slice(serviceUri.length);
1055
+ if (serviceUri.endsWith('/') || remainder === '' || '/?#'.includes(remainder.charAt(0))) {
1045
1056
  return true;
1046
1057
  }
1047
1058
  }
1048
1059
  }
1049
1060
  return false;
1050
1061
  }
1062
+ /**
1063
+ * Checks if the request targets the OIDC token endpoint.
1064
+ *
1065
+ * The comparison runs on the path alone, because a guard keyed on the raw URL fails
1066
+ * silently rather than loudly: a query string, fragment or trailing slash would make the
1067
+ * refresh post to that endpoint through this same chain, and the single-flight promise
1068
+ * would wait on itself. It never settles, its `finally` never runs, and since every request
1069
+ * of that service shares it, each later 401 would await a promise that cannot resolve.
1070
+ */
1071
+ function isTokenEndpointUrl(url) {
1072
+ const path = url.split('#')[0].split('?')[0].replace(/\/+$/, '');
1073
+ return path.endsWith('/connect/token');
1074
+ }
1075
+ /**
1076
+ * The one denial code a refresh can repair. Adapter-owned and stable: the server deliberately
1077
+ * does not make a client key on the framework's English `error_description`, which a dependency
1078
+ * bump may reword.
1079
+ */
1080
+ const REFRESHABLE_DENIAL_CODE = 'token_expired';
1081
+ /**
1082
+ * Answers whether refreshing the access token could possibly change this `401`.
1083
+ *
1084
+ * A server that follows RFC 6750 §3 names the reason it refused. Only an expired token gets a
1085
+ * different answer from a newer one — a wrong issuer, a wrong audience or an unknown signing key
1086
+ * describe something every token of this session shares, so refreshing spends one grant per user
1087
+ * action and lands in exactly the same place, indefinitely.
1088
+ *
1089
+ * A response that names no reason keeps the old behaviour and is refreshed, and so does a standard
1090
+ * challenge that carries no `error_code`. That is deliberate rather than a gap: every ASP.NET
1091
+ * service in the platform answers `Bearer error="invalid_token"` with a framework-worded
1092
+ * description and no such parameter, and RFC 6750 defines no code that separates an expired token
1093
+ * from a rejected one — so treating a bare challenge as unrepairable would silently drop the
1094
+ * recovery for every sibling service. The adapter closes the ambiguous case from its own side by
1095
+ * naming `token_not_evaluated` when it holds a token it never evaluated.
1096
+ */
1097
+ function mayRefreshRepair(error) {
1098
+ const challenge = error.headers?.get('WWW-Authenticate');
1099
+ if (!challenge) {
1100
+ return true;
1101
+ }
1102
+ // RFC 7235 allows an auth-param value as either a quoted-string or a bare token.
1103
+ const code = /error_code=(?:"([^"]*)"|([^\s,]+))/.exec(challenge);
1104
+ const value = code?.[1] ?? code?.[2];
1105
+ return value === undefined || value === REFRESHABLE_DENIAL_CODE;
1106
+ }
1107
+ // =============================================================================
1108
+ // SINGLE-FLIGHT TOKEN REFRESH
1109
+ // =============================================================================
1110
+ /**
1111
+ * Parallel 401s share one refresh instead of spending one grant each.
1112
+ *
1113
+ * Keyed by the service rather than held in a module variable: `AuthorizeService` is provided
1114
+ * per injector, so an app still gets exactly one in-flight refresh page-wide, while two
1115
+ * injectors in one process no longer await each other's promise and each test gets a clean
1116
+ * slate — a module variable is unreachable from a `beforeEach` unless it is exported.
1117
+ *
1118
+ * Do not restate the single-flight rationale as "concurrent grants would end the session":
1119
+ * that holds only for a client whose `RefreshTokenUsage` is `OneTimeOnly`, and ours are not —
1120
+ * Duende defaults the property to `ReUse` and nothing in octo-identity-services overrides it
1121
+ * (the CK attribute carries no default and reaches Duende through AutoMapper's by-name
1122
+ * convention, which is why grepping the C# for its name finds nothing).
1123
+ */
1124
+ const refreshInFlight = new WeakMap();
1125
+ function refreshAccessTokenOnce(authorizeService) {
1126
+ let pending = refreshInFlight.get(authorizeService);
1127
+ if (!pending) {
1128
+ pending = Promise.resolve()
1129
+ .then(() => authorizeService.refreshAccessToken())
1130
+ .finally(() => {
1131
+ refreshInFlight.delete(authorizeService);
1132
+ });
1133
+ refreshInFlight.set(authorizeService, pending);
1134
+ }
1135
+ return pending;
1136
+ }
1051
1137
  // =============================================================================
1052
1138
  // FUNCTIONAL INTERCEPTOR (RECOMMENDED)
1053
1139
  // =============================================================================
@@ -1063,6 +1149,12 @@ function isKnownServiceUri(req, serviceUris) {
1063
1149
  * `acr_values=tenant:{tenantId}` to the form body so the Identity Server
1064
1150
  * can resolve the correct tenant during refresh token exchanges.
1065
1151
  *
1152
+ * When a request that carried a token comes back `401`, the access token is refreshed
1153
+ * and the request retried once with the new bearer. If the token has already been replaced
1154
+ * by then — a slower request reporting its `401` after someone else's refresh finished —
1155
+ * the retry goes out with that token and no second refresh is started. A refresh that fails
1156
+ * or yields no new token rethrows the original `401`, so hosts can still classify it.
1157
+ *
1066
1158
  * @example
1067
1159
  * ```typescript
1068
1160
  * // app.config.ts
@@ -1081,7 +1173,8 @@ const authorizeInterceptor = (req, next) => {
1081
1173
  const authorizeService = inject(AuthorizeService);
1082
1174
  const token = authorizeService.getAccessTokenSync();
1083
1175
  const serviceUris = authorizeService.getServiceUris();
1084
- if (token && (isSameOriginUrl(req) || isKnownServiceUri(req, serviceUris))) {
1176
+ const tokenAttached = !!token && (isSameOriginUrl(req) || isKnownServiceUri(req, serviceUris));
1177
+ if (tokenAttached) {
1085
1178
  req = req.clone({
1086
1179
  setHeaders: {
1087
1180
  Authorization: `Bearer ${token}`
@@ -1098,7 +1191,7 @@ const authorizeInterceptor = (req, next) => {
1098
1191
  // and injecting a stale storage tenant (e.g., left over from a previous
1099
1192
  // session) would force a refresh-token mismatch on the very next call,
1100
1193
  // causing an infinite reload loop.
1101
- if (req.method === 'POST' && req.url.endsWith('/connect/token') && req.body instanceof HttpParams) {
1194
+ if (req.method === 'POST' && isTokenEndpointUrl(req.url) && req.body instanceof HttpParams) {
1102
1195
  if (req.body.get('grant_type') === 'refresh_token') {
1103
1196
  const tenantId = authorizeService.getStorageTenantId();
1104
1197
  if (tenantId) {
@@ -1108,9 +1201,91 @@ const authorizeInterceptor = (req, next) => {
1108
1201
  }
1109
1202
  }
1110
1203
  }
1111
- return next(req);
1204
+ // Never let the token endpoint into the recovery path — see isTokenEndpointUrl for the
1205
+ // deadlock this avoids.
1206
+ if (!tokenAttached || isTokenEndpointUrl(req.url)) {
1207
+ return next(req);
1208
+ }
1209
+ const authorizedReq = req;
1210
+ // `next()` hands off to the rest of the chain, so a retried request does not re-enter this
1211
+ // interceptor — at most one retry by construction, without any bookkeeping.
1212
+ const retryWith = (bearer) => next(authorizedReq.clone({ setHeaders: { Authorization: `Bearer ${bearer}` } }));
1213
+ return next(authorizedReq).pipe(catchError((error) => {
1214
+ if (!(error instanceof HttpErrorResponse) || error.status !== 401) {
1215
+ return throwError(() => error);
1216
+ }
1217
+ // Checked before the retry below as well: a token the server refuses for its issuer or
1218
+ // audience is refused again once refreshed, so neither path is worth an HTTP call.
1219
+ if (!mayRefreshRepair(error)) {
1220
+ console.warn('[AuthInterceptor] The server refused this token for a reason a refresh cannot repair:', error.headers?.get('WWW-Authenticate'));
1221
+ return throwError(() => error);
1222
+ }
1223
+ // Single-flight only covers the window while a refresh runs. A slower request that
1224
+ // went out with the same stale token reports its 401 after that window closed, and
1225
+ // its failure is already explained by the token that has since been replaced —
1226
+ // refreshing again would spend a second grant to learn the same thing.
1227
+ const currentToken = authorizeService.getAccessTokenSync();
1228
+ if (currentToken && currentToken !== token) {
1229
+ return retryWith(currentToken);
1230
+ }
1231
+ // Surface the original 401, not the refresh error: consuming apps classify the status
1232
+ // code to pick their message, and AuthorizeService already reacts to
1233
+ // `token_refresh_error` by clearing the session. The mapping is bound to the refresh
1234
+ // promise rather than placed in the pipe, because as an operator it only holds while it
1235
+ // sits above the switchMap below — and a 403 from the retried request must reach the
1236
+ // host as 403, not as the remembered 401.
1237
+ const refresh = refreshAccessTokenOnce(authorizeService).catch((refreshError) => {
1238
+ console.warn('[AuthInterceptor] Token refresh threw; surfacing the original 401.', refreshError);
1239
+ return Promise.reject(error);
1240
+ });
1241
+ return from(refresh).pipe(switchMap(() => {
1242
+ const refreshedToken = authorizeService.getAccessTokenSync();
1243
+ if (!refreshedToken || refreshedToken === token) {
1244
+ // Distinct from the throw above, and from each other: all three end in the same
1245
+ // 401 for the host, so the log is the only place they stay tellable apart.
1246
+ console.warn(refreshedToken
1247
+ ? '[AuthInterceptor] Token refresh returned the same access token; surfacing the original 401.'
1248
+ : '[AuthInterceptor] Token refresh returned no access token; surfacing the original 401.');
1249
+ return throwError(() => error);
1250
+ }
1251
+ return retryWith(refreshedToken);
1252
+ }));
1253
+ }));
1112
1254
  };
1113
1255
 
1256
+ /**
1257
+ * Classifies an HTTP failure as an authorization refusal, or `null` when it is anything else.
1258
+ *
1259
+ * Hosts phrase the outcome themselves - one app wants an i18n key, another a translated string,
1260
+ * and the public landing pages deliberately merge both cases because an anonymous visitor cannot
1261
+ * act on the difference. What they share is reading `status`, and six copies of that read is how
1262
+ * the six drift apart.
1263
+ *
1264
+ * Takes `unknown` because that is how the error arrives: through a rejected promise, a `catch`
1265
+ * block, or a rethrow that may have stripped the `HttpErrorResponse` prototype along the way.
1266
+ * The status is therefore read structurally and must be a `number` - a body that merely carries
1267
+ * `"403"` is not a refusal.
1268
+ *
1269
+ * @example
1270
+ * ```typescript
1271
+ * switch (authorizationRefusal(error)) {
1272
+ * case 'forbidden': return this.translate.instant('pipeline.forbidden');
1273
+ * case 'unauthorized': return this.translate.instant('pipeline.unauthorized');
1274
+ * default: return null;
1275
+ * }
1276
+ * ```
1277
+ */
1278
+ function authorizationRefusal(error) {
1279
+ const status = error?.status;
1280
+ if (status === 401) {
1281
+ return 'unauthorized';
1282
+ }
1283
+ if (status === 403) {
1284
+ return 'forbidden';
1285
+ }
1286
+ return null;
1287
+ }
1288
+
1114
1289
  /**
1115
1290
  * Walks up the route tree to find the tenantId parameter.
1116
1291
  */
@@ -1286,5 +1461,5 @@ function provideMmSharedAuth() {
1286
1461
  * Generated bundle index. Do not edit.
1287
1462
  */
1288
1463
 
1289
- export { AuthorizeOptions, AuthorizeService, Roles, TenantAwareOAuthStorage, authorizeChildGuard, authorizeDeactivateGuard, authorizeGuard, authorizeInterceptor, authorizeMatchGuard, provideMmSharedAuth };
1464
+ export { AuthorizeOptions, AuthorizeService, Roles, TenantAwareOAuthStorage, authorizationRefusal, authorizeChildGuard, authorizeDeactivateGuard, authorizeGuard, authorizeInterceptor, authorizeMatchGuard, provideMmSharedAuth };
1290
1465
  //# sourceMappingURL=meshmakers-shared-auth.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"meshmakers-shared-auth.mjs","sources":["../../../../projects/meshmakers/shared-auth/src/lib/tenant-aware-oauth-storage.ts","../../../../projects/meshmakers/shared-auth/src/lib/authorize.service.ts","../../../../projects/meshmakers/shared-auth/src/lib/roles.ts","../../../../projects/meshmakers/shared-auth/src/lib/authorize.interceptor.ts","../../../../projects/meshmakers/shared-auth/src/lib/authorize.guard.ts","../../../../projects/meshmakers/shared-auth/src/public-api.ts","../../../../projects/meshmakers/shared-auth/src/meshmakers-shared-auth.ts"],"sourcesContent":["import { OAuthStorage } from 'angular-oauth2-oidc';\n\n/**\n * Known OAuth storage keys used by angular-oauth2-oidc.\n * Used by clearAllTenants() to identify and remove OAuth-related entries\n * across all tenants without affecting other localStorage data.\n */\nconst OAUTH_STORAGE_KEYS = [\n 'access_token',\n 'refresh_token',\n 'id_token',\n 'id_token_claims_obj',\n 'id_token_header',\n 'expires_at',\n 'access_token_stored_at',\n 'id_token_expires_at',\n 'id_token_stored_at',\n 'nonce',\n 'PKCE_verifier',\n 'session_state',\n 'granted_scopes',\n 'requested_route',\n];\n\n/**\n * Keys that are specific to a single OAuth authorization flow and must\n * be isolated per browser tab. These are stored in sessionStorage\n * (which is per-tab) to prevent cross-tab nonce/PKCE collisions.\n *\n * Without this, two tabs starting login simultaneously would overwrite\n * each other's nonce in shared localStorage, causing \"invalid_nonce_in_state\"\n * errors when the first tab's callback tries to validate.\n */\nconst SESSION_SCOPED_KEYS = new Set([\n 'nonce',\n 'PKCE_verifier',\n 'requested_route',\n]);\n\n/**\n * SessionStorage key used to persist the current storage tenant ID across\n * page reloads (e.g., OAuth redirects). The tenant ID must survive the\n * round-trip to the identity server because the redirect URI may not\n * contain the tenant path segment.\n */\nconst STORAGE_TENANT_KEY = 'octo_storage_tenant';\n\n/**\n * Tenant-aware OAuth storage that prefixes all keys with a tenant ID\n * and splits storage between localStorage and sessionStorage.\n *\n * **Key prefixing:** When a tenant ID is set, all storage keys are prefixed\n * with `{tenantId}__` (double underscore separator). This isolates OAuth\n * tokens per tenant, preventing race conditions during tenant switches.\n *\n * **Storage split:** Flow-specific ephemeral keys (nonce, PKCE_verifier)\n * are stored in sessionStorage (per browser tab), while tokens and session\n * data are stored in localStorage (shared across tabs). This prevents\n * cross-tab nonce collisions when multiple tabs initiate login simultaneously.\n *\n * When no tenant ID is set (null), keys are stored without a prefix,\n * maintaining backwards compatibility with existing single-tenant apps.\n *\n * @example\n * ```typescript\n * const storage = new TenantAwareOAuthStorage();\n * storage.setTenantId('maco');\n * storage.setItem('access_token', 'abc');\n * // Stored in: localStorage['maco__access_token'] = 'abc'\n *\n * storage.setItem('nonce', 'xyz');\n * // Stored in: sessionStorage['maco__nonce'] = 'xyz' (per-tab!)\n * ```\n */\nexport class TenantAwareOAuthStorage extends OAuthStorage {\n private tenantId: string | null = null;\n\n /**\n * Sets the tenant ID used to prefix storage keys.\n * Must be called before the OAuthService reads/writes tokens.\n * The tenant ID is persisted in sessionStorage so it survives OAuth redirects.\n *\n * @param tenantId The tenant ID, or null for unprefixed (backwards-compatible) mode.\n */\n setTenantId(tenantId: string | null): void {\n this.tenantId = tenantId;\n try {\n if (tenantId) {\n sessionStorage.setItem(STORAGE_TENANT_KEY, tenantId);\n } else {\n sessionStorage.removeItem(STORAGE_TENANT_KEY);\n }\n } catch {\n // sessionStorage may be unavailable\n }\n }\n\n /**\n * Returns the currently configured tenant ID.\n */\n getTenantId(): string | null {\n return this.tenantId;\n }\n\n /**\n * Restores the tenant ID from sessionStorage.\n * Call this on app startup when the tenant cannot be determined from the URL\n * (e.g., after an OAuth redirect to the root path).\n *\n * @returns The restored tenant ID, or null if none was persisted.\n */\n restoreTenantId(): string | null {\n try {\n const stored = sessionStorage.getItem(STORAGE_TENANT_KEY);\n if (stored) {\n this.tenantId = stored;\n return stored;\n }\n } catch {\n // sessionStorage may be unavailable\n }\n return null;\n }\n\n /**\n * Returns the prefixed key for the given base key.\n * When tenantId is null, returns the base key unchanged.\n */\n prefixKey(key: string): string {\n if (this.tenantId) {\n return `${this.tenantId}__${key}`;\n }\n return key;\n }\n\n getItem(key: string): string | null {\n const prefixed = this.prefixKey(key);\n if (SESSION_SCOPED_KEYS.has(key)) {\n return sessionStorage.getItem(prefixed);\n }\n return localStorage.getItem(prefixed);\n }\n\n removeItem(key: string): void {\n const prefixed = this.prefixKey(key);\n if (SESSION_SCOPED_KEYS.has(key)) {\n sessionStorage.removeItem(prefixed);\n } else {\n localStorage.removeItem(prefixed);\n }\n }\n\n setItem(key: string, data: string): void {\n const prefixed = this.prefixKey(key);\n if (SESSION_SCOPED_KEYS.has(key)) {\n sessionStorage.setItem(prefixed, data);\n } else {\n localStorage.setItem(prefixed, data);\n }\n }\n\n /**\n * Clears all OAuth-related keys for ALL tenants from both\n * localStorage and sessionStorage, while leaving non-OAuth data intact.\n *\n * Used during full logout to ensure no stale tokens remain for any tenant.\n */\n clearAllTenants(): void {\n this.clearOAuthKeysFromStorage(localStorage);\n this.clearOAuthKeysFromStorage(sessionStorage);\n }\n\n private clearOAuthKeysFromStorage(storage: Storage): void {\n const keysToRemove: string[] = [];\n for (let i = 0; i < storage.length; i++) {\n const storageKey = storage.key(i);\n if (!storageKey) continue;\n\n for (const oauthKey of OAUTH_STORAGE_KEYS) {\n if (storageKey === oauthKey || storageKey.endsWith('__' + oauthKey)) {\n keysToRemove.push(storageKey);\n break;\n }\n }\n }\n for (const key of keysToRemove) {\n storage.removeItem(key);\n }\n }\n}\n","import { Injectable, Signal, WritableSignal, computed, inject, signal } from \"@angular/core\";\nimport { AuthConfig, OAuthService } from \"angular-oauth2-oidc\";\nimport { Roles } from \"./roles\";\nimport { TenantAwareOAuthStorage } from \"./tenant-aware-oauth-storage\";\n\nexport interface IUser {\n family_name: string | null;\n given_name: string | null;\n name: string;\n role: string[] | null;\n sub: string;\n idp: string;\n email: string | null;\n}\n\nexport interface DiscoveryDocumentRetryOptions {\n attempts: number;\n initialDelayMs: number;\n maxDelayMs: number;\n}\n\nexport class AuthorizeOptions {\n wellKnownServiceUris?: string[];\n // Url of the Identity Provider\n issuer?: string;\n // URL of the SPA to redirect the user to after login\n redirectUri?: string;\n postLogoutRedirectUri?: string;\n // The SPA's id. The SPA is registered with this id at the auth-server\n clientId?: string;\n // set the scope for the permissions the client should request\n // The first three are defined by OIDC. The 4th is a use case-specific one\n scope?: string;\n showDebugInformation?: boolean;\n sessionChecksEnabled?: boolean;\n // Default tenant ID for single-tenant apps. When set, login() uses this tenant\n // if no tenantId is explicitly provided (sends acr_values=tenant:{defaultTenantId}).\n defaultTenantId?: string;\n // Retry policy for loadDiscoveryDocumentAndTryLogin() — covers the brief\n // window during which the Identity service is unreachable after a redeploy\n // or rolling restart. Defaults: 6 attempts, 1500ms initial, 30000ms cap.\n discoveryDocumentRetry?: DiscoveryDocumentRetryOptions;\n}\n\n@Injectable()\nexport class AuthorizeService {\n private readonly oauthService = inject(OAuthService);\n\n // =============================================================================\n // INTERNAL STATE (Writable Signals)\n // =============================================================================\n\n private readonly _isAuthenticated: WritableSignal<boolean> = signal(false);\n private readonly _issuer: WritableSignal<string | null> = signal(null);\n private readonly _accessToken: WritableSignal<string | null> = signal(null);\n private readonly _user: WritableSignal<IUser | null> = signal(null);\n private readonly _userInitials: WritableSignal<string | null> = signal(null);\n private readonly _isInitialized: WritableSignal<boolean> = signal(false);\n private readonly _isInitializing: WritableSignal<boolean> = signal(false);\n private readonly _sessionLoading: WritableSignal<boolean> = signal(false);\n private readonly _allowedTenants: WritableSignal<string[]> = signal([]);\n private readonly _tokenTenantId: WritableSignal<string | null> = signal(null);\n private readonly _discoveryDocumentError: WritableSignal<Error | null> = signal(null);\n private readonly _discoveryDocumentRetryAttempt: WritableSignal<number> = signal(0);\n\n private static readonly TENANT_REAUTH_KEY = 'octo_tenant_reauth';\n private static readonly TENANT_SWITCH_ATTEMPTED_KEY = 'octo_tenant_switch_attempted';\n private static readonly DEFAULT_DISCOVERY_RETRY: DiscoveryDocumentRetryOptions = {\n attempts: 6,\n initialDelayMs: 1500,\n maxDelayMs: 30000\n };\n\n private readonly tenantStorage = new TenantAwareOAuthStorage();\n private _loginInProgress = false;\n private authorizeOptions: AuthorizeOptions | null = null;\n private lastAuthConfig: AuthConfig | null = null;\n\n // =============================================================================\n // PUBLIC API (Readonly Signals) - NEW API\n // =============================================================================\n\n /**\n * Signal indicating whether the user is currently authenticated.\n */\n readonly isAuthenticated: Signal<boolean> = this._isAuthenticated.asReadonly();\n\n /**\n * Signal containing the issuer URL.\n */\n readonly issuer: Signal<string | null> = this._issuer.asReadonly();\n\n /**\n * Signal containing the current access token.\n */\n readonly accessToken: Signal<string | null> = this._accessToken.asReadonly();\n\n /**\n * Signal containing the current user information.\n */\n readonly user: Signal<IUser | null> = this._user.asReadonly();\n\n /**\n * Computed signal containing the user's initials (e.g., \"JD\" for John Doe).\n */\n readonly userInitials: Signal<string | null> = this._userInitials.asReadonly();\n\n /**\n * Signal indicating whether the session is currently loading.\n */\n readonly sessionLoading: Signal<boolean> = this._sessionLoading.asReadonly();\n\n /**\n * Signal containing the list of tenants the user is allowed to access.\n * Parsed from the allowed_tenants claims in the access token.\n */\n readonly allowedTenants: Signal<string[]> = this._allowedTenants.asReadonly();\n\n /**\n * Signal containing the tenant_id claim from the current access token.\n * Used to detect tenant mismatch when navigating between tenants.\n */\n readonly tokenTenantId: Signal<string | null> = this._tokenTenantId.asReadonly();\n\n /**\n * Computed signal containing the user's roles.\n */\n readonly roles: Signal<string[]> = computed(() => this._user()?.role ?? []);\n\n /**\n * Signal carrying the last error from loadDiscoveryDocumentAndTryLogin().\n * Non-null means the Identity service was unreachable across the full retry\n * budget; host apps can render a recovery screen and call retryDiscoveryDocument().\n */\n readonly discoveryDocumentError: Signal<Error | null> = this._discoveryDocumentError.asReadonly();\n\n /**\n * Signal containing the current discovery-document retry attempt (1-based while\n * retrying, 0 when idle). Useful for showing \"Reconnecting (attempt N)…\" UI.\n */\n readonly discoveryDocumentRetryAttempt: Signal<number> = this._discoveryDocumentRetryAttempt.asReadonly();\n\n /**\n * Computed signal for the user's display name.\n * Uses given_name + family_name if available, otherwise derives from the username.\n */\n readonly displayName: Signal<string | null> = computed(() => {\n const user = this._user();\n if (!user) return null;\n if (user.given_name && user.family_name) {\n return user.given_name + ' ' + user.family_name;\n }\n return this.deriveDisplayNameFromUsername(user.name);\n });\n\n constructor() {\n console.debug(\"AuthorizeService::created\");\n\n this.oauthService.discoveryDocumentLoaded$.subscribe((_) => {\n console.debug(\"discoveryDocumentLoaded$\");\n });\n\n this.oauthService.events.subscribe((e) => {\n console.debug(\"oauth/oidc event\", e);\n });\n\n this.oauthService.events\n .pipe((source) => source)\n .subscribe((e) => {\n if (e.type === \"session_terminated\") {\n console.debug(\"Your session has been terminated!\");\n this._accessToken.set(null);\n this._user.set(null);\n this._isAuthenticated.set(false);\n this._tokenTenantId.set(null);\n // Reload the page to trigger the auth flow and redirect to login\n this.reloadPage();\n }\n\n if (e.type === \"token_refresh_error\") {\n console.warn(\"AuthorizeService: Token refresh failed — clearing local session and reloading\");\n this._accessToken.set(null);\n this._user.set(null);\n this._isAuthenticated.set(false);\n this._tokenTenantId.set(null);\n this._allowedTenants.set([]);\n // Do NOT call oauthService.logOut() here — it destroys the server-side\n // session at the Identity Server's end_session_endpoint. If the refresh\n // token is invalid (e.g., after an IDS restart), the user still has a\n // valid IDS session cookie and can silently re-authenticate. Calling\n // logOut() would force a full re-login with credentials.\n // Instead, clear local tokens and reload — the guard will trigger\n // login() which obtains a fresh authorization code via the existing session.\n this.tenantStorage.clearAllTenants();\n this.reloadPage();\n }\n });\n\n this.oauthService.events.subscribe(async (e) => {\n if (e.type === \"token_received\") {\n this._loginInProgress = false;\n await this.loadUserAsync();\n }\n });\n\n this.oauthService.events.subscribe(async (e) => {\n if (e.type === \"session_unchanged\") {\n if (this._user() == null) {\n await this.loadUserAsync();\n }\n }\n });\n\n this.oauthService.events.subscribe((e) => {\n if (e.type === \"logout\") {\n console.debug(\"AuthorizeService: Logout event received\");\n this._accessToken.set(null);\n this._user.set(null);\n this._isAuthenticated.set(false);\n this._tokenTenantId.set(null);\n this._allowedTenants.set([]);\n // Do NOT call reloadPage() here — oauthService.logOut() already\n // redirects to the Identity Server's end_session_endpoint.\n // Calling reload() would race with that redirect and cause the\n // page to reload before the server-side session is terminated,\n // leaving the user still logged in.\n }\n });\n\n // Listen for storage events from other tabs (e.g., SLO logout callback)\n // This enables immediate cross-tab logout detection\n window.addEventListener('storage', (event) => {\n console.debug(\"AuthorizeService: Storage event received\", event.key, event.newValue);\n // Check if the current tenant's access_token was removed (logout in another tab)\n // With per-tenant storage, the key is prefixed (e.g., \"maco__access_token\")\n // Note: OAuth library may set to empty string or null when clearing\n const expectedKey = this.tenantStorage.prefixKey('access_token');\n if (event.key === expectedKey && (event.newValue === null || event.newValue === '') && this._isAuthenticated()) {\n console.debug(\"AuthorizeService: Access token removed in another tab - logging out and reloading\");\n this._accessToken.set(null);\n this._user.set(null);\n this._isAuthenticated.set(false);\n this._tokenTenantId.set(null);\n // Reload the page to trigger the auth flow and redirect to login\n this.reloadPage();\n }\n });\n\n // Also listen for BroadcastChannel messages for cross-tab logout\n // This is more reliable than storage events for iframe-based SLO\n if (typeof BroadcastChannel !== 'undefined') {\n console.debug(\"AuthorizeService: Setting up BroadcastChannel listener for 'octo-auth-logout'\");\n const logoutChannel = new BroadcastChannel('octo-auth-logout');\n logoutChannel.onmessage = (event) => {\n console.debug(\"AuthorizeService: BroadcastChannel message received\", event.data);\n if (event.data?.type === 'logout' && this._isAuthenticated()) {\n console.debug(\"AuthorizeService: Logout broadcast received - reloading\");\n this._accessToken.set(null);\n this._user.set(null);\n this._isAuthenticated.set(false);\n this._tokenTenantId.set(null);\n this.reloadPage();\n }\n };\n } else {\n console.warn(\"AuthorizeService: BroadcastChannel not supported in this browser\");\n }\n\n // When the tab returns from the background and we previously failed to load\n // the discovery document (e.g. because the Identity service was redeploying),\n // try to recover automatically. Without this the loading icon would sit\n // forever until the user manually reloads.\n if (typeof document !== 'undefined') {\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState !== 'visible') {\n return;\n }\n if (this._discoveryDocumentError() && !this._isInitializing() && this.authorizeOptions) {\n console.debug(\"AuthorizeService: visibilitychange → retrying after discovery-document failure\");\n // Re-run initialize() with the last options. We intentionally swallow\n // the error here — initialize() already records it in the signal.\n void this.initialize(this.authorizeOptions).catch(() => undefined);\n }\n });\n }\n }\n\n /**\n * Public hook to retry loading the discovery document after a failure.\n * No-op while initialization is in progress.\n */\n public async retryDiscoveryDocument(): Promise<void> {\n if (this._isInitializing() || !this.authorizeOptions) {\n return;\n }\n await this.initialize(this.authorizeOptions);\n }\n\n /**\n * Checks if the current user has the specified role.\n */\n public isInRole(role: Roles): boolean {\n return this._user()?.role?.includes(role) ?? false;\n }\n\n /**\n * Gets the configured service URIs that should receive the authorization token.\n */\n public getServiceUris(): string[] | null {\n return this.authorizeOptions?.wellKnownServiceUris ?? null;\n }\n\n /**\n * Sets the tenant ID for per-tenant token storage isolation.\n * Must be called BEFORE initialize() to ensure tokens are stored/retrieved\n * under the correct tenant prefix in localStorage.\n *\n * When set, all OAuth storage keys are prefixed with `{tenantId}__`\n * (e.g., `maco__access_token`), preventing token collisions between tenants.\n * The tenant ID is also persisted in sessionStorage so it survives OAuth redirects.\n *\n * @param tenantId The tenant ID to use for storage key prefixing, or null for unprefixed mode.\n */\n public setStorageTenantId(tenantId: string | null): void {\n this.tenantStorage.setTenantId(tenantId);\n console.debug(`AuthorizeService::setStorageTenantId(\"${tenantId}\")`);\n }\n\n /**\n * Returns the current storage tenant ID.\n * Used by the interceptor to inject acr_values into token endpoint requests.\n */\n public getStorageTenantId(): string | null {\n return this.tenantStorage.getTenantId();\n }\n\n /**\n * Restores the storage tenant ID from sessionStorage.\n * Use this when the tenant ID cannot be determined from the URL\n * (e.g., after an OAuth redirect to the root path).\n *\n * @returns The restored tenant ID, or null if none was persisted.\n */\n public restoreStorageTenantId(): string | null {\n const tenantId = this.tenantStorage.restoreTenantId();\n if (tenantId) {\n console.debug(`AuthorizeService::restoreStorageTenantId(\"${tenantId}\")`);\n }\n return tenantId;\n }\n\n /**\n * Gets the current access token synchronously.\n * Use this for functional interceptors that need immediate access to the token.\n *\n * @returns The current access token or null if not authenticated\n */\n public getAccessTokenSync(): string | null {\n return this._accessToken();\n }\n\n /**\n * Checks if the user is allowed to access the specified tenant.\n * Returns true if no allowed_tenants claims are present (backwards compatibility).\n */\n public isTenantAllowed(tenantId: string): boolean {\n const allowed = this._allowedTenants();\n if (allowed.length === 0) {\n return true; // No claims = backwards compatible (old tokens)\n }\n return allowed.some(t => t.toLowerCase() === tenantId.toLowerCase());\n }\n\n /**\n * Initiates the login flow.\n * Multiple guards (canActivateChild, canMatch) may call this concurrently\n * during route resolution. Only the first call proceeds — subsequent calls\n * are skipped to prevent generating a new nonce that overwrites the first\n * one, which would cause an \"invalid_nonce_in_state\" error after the\n * identity server redirects back.\n *\n * @param tenantId Optional tenant ID. When provided, includes acr_values=tenant:{tenantId}\n * so the identity server redirects to the correct tenant's login page.\n */\n public login(tenantId?: string): void {\n if (this._loginInProgress) {\n console.debug('AuthorizeService::login skipped (already in progress)');\n return;\n }\n this._loginInProgress = true;\n\n const effectiveTenantId = tenantId ?? this.authorizeOptions?.defaultTenantId;\n if (effectiveTenantId) {\n this.oauthService.initImplicitFlow('', { acr_values: `tenant:${effectiveTenantId}` });\n } else {\n this.oauthService.initImplicitFlow();\n }\n }\n\n /**\n * Forces re-authentication for a different tenant by clearing the local\n * OAuth session and reloading the page. On reload, the guard will see\n * isAuthenticated=false and call login(tenantId) with the correct acr_values.\n *\n * This is used when the current token's tenant_id does not match the\n * route's tenantId (e.g., navigating from octosystem to meshtest).\n */\n /**\n * Returns true if the switch was initiated, false if skipped (loop prevention).\n */\n public switchTenant(targetTenantId: string, targetUrl: string): boolean {\n console.debug(`AuthorizeService::switchTenant to \"${targetTenantId}\" at \"${targetUrl}\"`);\n\n // Prevent infinite redirect loop: if we already attempted a switch to this\n // exact tenant and ended up here again, the Identity Server did not issue\n // a token for the target tenant. Do NOT attempt again.\n try {\n const previousAttempt = sessionStorage.getItem(AuthorizeService.TENANT_SWITCH_ATTEMPTED_KEY);\n if (previousAttempt && previousAttempt.toLowerCase() === targetTenantId.toLowerCase()) {\n console.warn(`AuthorizeService::switchTenant — already attempted switch to \"${targetTenantId}\", skipping to prevent loop`);\n return false;\n }\n } catch {\n // sessionStorage may be unavailable\n }\n\n // Store target tenant so login() uses the correct acr_values after reload\n try {\n sessionStorage.setItem(AuthorizeService.TENANT_REAUTH_KEY, targetTenantId);\n sessionStorage.setItem(AuthorizeService.TENANT_SWITCH_ATTEMPTED_KEY, targetTenantId);\n } catch {\n // sessionStorage may be unavailable\n }\n\n // Stop automatic refresh and session checks to prevent the OAuth library\n // from firing additional authorize requests during the page reload window.\n // This prevents race conditions where a session check or silent refresh\n // could overwrite the nonce/PKCE verifier of the new tenant's auth flow.\n this.oauthService.stopAutomaticRefresh();\n\n // With per-tenant storage (TenantAwareOAuthStorage), we do NOT clear\n // tokens from localStorage. Each tenant's tokens are stored under\n // prefixed keys (e.g., \"maco__access_token\") and are isolated from\n // each other. After page reload, the storage tenant will be set to\n // the target tenant, and cached tokens (if valid) can be reused.\n\n // Clear our signals\n this._accessToken.set(null);\n this._user.set(null);\n this._isAuthenticated.set(false);\n this._tokenTenantId.set(null);\n\n // Full page navigation to the target URL — triggers fresh auth flow\n this.navigateTo(targetUrl);\n return true;\n }\n\n /**\n * Returns the pending tenant switch target (if any) WITHOUT clearing it.\n * The key persists across the OAuth redirect cycle (guard → IDS → callback → guard)\n * and is only cleared after a successful token exchange in loadUserAsync().\n *\n * Previously this method removed the key immediately, but that caused a race condition:\n * the guard consumed it before the IDS redirect, and after the callback redirect the\n * key was gone — causing the guard to fall back to the route tenant (wrong tenant).\n */\n public consumePendingTenantSwitch(): string | null {\n try {\n return sessionStorage.getItem(AuthorizeService.TENANT_REAUTH_KEY);\n } catch {\n return null;\n }\n }\n\n /**\n * Clears the pending tenant switch key. Called after a successful token exchange\n * when the token's tenant_id matches the target, completing the switch cycle.\n */\n public clearPendingTenantSwitch(): void {\n try {\n sessionStorage.removeItem(AuthorizeService.TENANT_REAUTH_KEY);\n } catch {\n // sessionStorage may be unavailable\n }\n }\n\n /**\n * Returns the tenant for which a switch was already attempted (if any) and clears it.\n * Used by the guard to prevent infinite redirect loops: if the Identity Server\n * issues a token for the wrong tenant even after a switch, we skip the second attempt.\n */\n public consumeSwitchAttempted(): string | null {\n try {\n const tenantId = sessionStorage.getItem(AuthorizeService.TENANT_SWITCH_ATTEMPTED_KEY);\n sessionStorage.removeItem(AuthorizeService.TENANT_SWITCH_ATTEMPTED_KEY);\n return tenantId;\n } catch {\n return null;\n }\n }\n\n /**\n * Logs out the current user by redirecting to the Identity Server's\n * OIDC end_session_endpoint for proper Single Logout (SLO).\n *\n * We cannot rely on oauthService.logOut() for the redirect because it calls\n * clearStorage() which may clear internal state before the redirect happens.\n * Instead, we capture the logoutUrl and id_token first, then clear state,\n * then manually redirect to the end_session_endpoint.\n */\n public logout(): void {\n // Capture the current tenant BEFORE clearing storage so the host app\n // can return the user to the same tenant after the IdS end_session\n // round-trip. The post_logout_redirect_uri registered with the IdS is\n // typically `${origin}/` and therefore drops the tenant path segment,\n // so without this hint the next login would fall through to whatever\n // default-tenant redirect the host application has wired up.\n const currentTenant = this.tenantStorage.getTenantId();\n if (currentTenant) {\n try {\n sessionStorage.setItem('octo_post_logout_tenant', currentTenant);\n } catch {\n // sessionStorage may be unavailable\n }\n }\n\n // Read the end_session_endpoint (stored as logoutUrl on the service) and id_token BEFORE clearing storage\n const endSessionEndpoint = this.oauthService.logoutUrl;\n const idToken = this.oauthService.getIdToken();\n const postLogoutRedirectUri = this.lastAuthConfig?.postLogoutRedirectUri;\n\n // Clear local OAuth state (tokens, discovery doc, etc.)\n this.oauthService.logOut(true); // true = noRedirectToLogoutUrl (we redirect manually)\n\n // Clear OAuth tokens for ALL tenants, not just the current one.\n // With per-tenant storage, oauthService.logOut() only clears the current\n // tenant's prefixed keys. We need to ensure no stale tokens remain for\n // any tenant after a full logout.\n this.tenantStorage.clearAllTenants();\n\n if (endSessionEndpoint) {\n // Build the end_session URL with id_token_hint and post_logout_redirect_uri\n const params = new URLSearchParams();\n if (idToken) {\n params.set('id_token_hint', idToken);\n }\n if (postLogoutRedirectUri) {\n params.set('post_logout_redirect_uri', postLogoutRedirectUri);\n }\n this.navigateTo(`${endSessionEndpoint}?${params.toString()}`);\n } else {\n // Fallback: no end_session_endpoint available, just reload\n this.reloadPage();\n }\n }\n\n /**\n * Updates the redirect URIs without performing a full re-initialization.\n * Use this when the OAuth session is already established and only the\n * redirect targets need to change (e.g., when switching tenants).\n */\n public updateRedirectUris(redirectUri: string, postLogoutRedirectUri: string): void {\n if (this.authorizeOptions) {\n this.authorizeOptions.redirectUri = redirectUri;\n this.authorizeOptions.postLogoutRedirectUri = postLogoutRedirectUri;\n }\n\n if (this.lastAuthConfig) {\n this.lastAuthConfig.redirectUri = redirectUri;\n this.lastAuthConfig.postLogoutRedirectUri = postLogoutRedirectUri;\n }\n\n // Update the redirect URIs directly on the OAuthService without calling\n // configure(), because configure() does Object.assign(this, new AuthConfig(), config)\n // which resets ALL properties — including logoutUrl, tokenEndpoint, and other\n // discovery document endpoints — back to their AuthConfig defaults (empty).\n this.oauthService.redirectUri = redirectUri;\n this.oauthService.postLogoutRedirectUri = postLogoutRedirectUri;\n\n console.debug(\"AuthorizeService::updateRedirectUris::done\");\n }\n\n /**\n * Refreshes the access token and updates the allowed tenants signal.\n * Call this after actions that change the user's tenant access (e.g., provisioning).\n */\n public async refreshAccessToken(): Promise<void> {\n console.debug(\"AuthorizeService::refreshAccessToken::started\");\n await this.oauthService.refreshToken();\n await this.loadUserAsync();\n console.debug(\"AuthorizeService::refreshAccessToken::done\");\n }\n\n /**\n * Initializes the authorization service with the specified options.\n */\n public async initialize(authorizeOptions: AuthorizeOptions): Promise<void> {\n console.debug(\"AuthorizeService::initialize::started\");\n\n await this.uninitialize();\n\n if (this._isInitializing()) {\n return;\n }\n if (this._isInitialized()) {\n console.debug(\"AuthorizeService::initialize::alreadyInitialized\");\n return;\n }\n this._isInitializing.set(true);\n\n try {\n const config: AuthConfig = {\n responseType: \"code\",\n issuer: authorizeOptions.issuer,\n redirectUri: authorizeOptions.redirectUri,\n postLogoutRedirectUri: authorizeOptions.postLogoutRedirectUri,\n clientId: authorizeOptions.clientId,\n scope: authorizeOptions.scope,\n showDebugInformation: authorizeOptions.showDebugInformation,\n sessionChecksEnabled: authorizeOptions.sessionChecksEnabled,\n sessionCheckIntervall: 60 * 1000,\n preserveRequestedRoute: true\n };\n\n this.authorizeOptions = authorizeOptions;\n this.lastAuthConfig = config;\n\n this.oauthService.setStorage(this.tenantStorage);\n this.oauthService.configure(config);\n\n console.debug(\"AuthorizeService::initialize::loadingDiscoveryDocumentAndTryLogin\");\n await this.loadDiscoveryDocumentAndTryLoginWithRetry(\n authorizeOptions.discoveryDocumentRetry ?? AuthorizeService.DEFAULT_DISCOVERY_RETRY\n );\n\n console.debug(\"AuthorizeService::initialize::setupAutomaticSilentRefresh\");\n this.oauthService.setupAutomaticSilentRefresh();\n\n this._issuer.set(authorizeOptions.issuer ?? null);\n\n if (this.oauthService.hasValidIdToken()) {\n // if the idToken is still valid, we can use the session\n console.debug(\"AuthorizeService::initialize::hasValidIdToken\");\n this._sessionLoading.set(true);\n await this.oauthService.refreshToken();\n }\n\n this._isInitialized.set(true);\n console.debug(\"AuthorizeService::initialize::done\");\n } finally {\n this._isInitializing.set(false);\n }\n\n console.debug(\"AuthorizeService::initialize::completed\");\n }\n\n /**\n * Wraps oauthService.loadDiscoveryDocumentAndTryLogin() with bounded\n * exponential-backoff retries so a brief outage of the Identity service\n * (cold start after redeploy, rolling restart, transient ingress 5xx)\n * does not leave the SPA stuck on the loading icon.\n *\n * The CORS-error symptom seen on test-2 — \"No 'Access-Control-Allow-Origin'\n * header\" against /.well-known/openid-configuration — is exactly such a\n * transient failure: the ingress synthesises a default error page that\n * does not carry the CORS headers, so the browser reports a CORS violation\n * rather than the actual HTTP status. Retrying after the backend comes\n * back resolves it without user interaction.\n *\n * Throws the last error if every attempt fails. The error is also stored\n * in the discoveryDocumentError signal so callers can render a recovery UI.\n */\n private async loadDiscoveryDocumentAndTryLoginWithRetry(\n retry: DiscoveryDocumentRetryOptions\n ): Promise<void> {\n const attempts = Math.max(1, retry.attempts);\n const initialDelayMs = Math.max(0, retry.initialDelayMs);\n const maxDelayMs = Math.max(initialDelayMs, retry.maxDelayMs);\n\n let lastError: unknown = null;\n for (let attempt = 1; attempt <= attempts; attempt++) {\n this._discoveryDocumentRetryAttempt.set(attempt);\n try {\n await this.oauthService.loadDiscoveryDocumentAndTryLogin();\n this._discoveryDocumentError.set(null);\n this._discoveryDocumentRetryAttempt.set(0);\n if (attempt > 1) {\n console.info(\n `AuthorizeService: discovery document loaded on retry attempt ${attempt}/${attempts}`\n );\n }\n return;\n } catch (err) {\n lastError = err;\n if (attempt === attempts) {\n break;\n }\n const delayMs = Math.min(\n initialDelayMs * Math.pow(2, attempt - 1),\n maxDelayMs\n );\n console.warn(\n `AuthorizeService: discovery document load failed (attempt ${attempt}/${attempts}); retrying in ${delayMs}ms`,\n err\n );\n await new Promise<void>(resolve => setTimeout(resolve, delayMs));\n }\n }\n\n const error = lastError instanceof Error\n ? lastError\n : new Error('Failed to load OIDC discovery document');\n this._discoveryDocumentError.set(error);\n this._discoveryDocumentRetryAttempt.set(0);\n console.error(\n `AuthorizeService: discovery document load failed after ${attempts} attempt(s)`,\n error\n );\n throw error;\n }\n\n /**\n * Uninitializes the authorization service.\n */\n public async uninitialize(): Promise<void> {\n console.debug(\"AuthorizeService::uninitialize::started\");\n\n if (this._isInitializing()) {\n return;\n }\n if (!this._isInitialized()) {\n console.debug(\"AuthorizeService::uninitialize::alreadyUninitialized\");\n return;\n }\n\n try {\n this._isInitializing.set(true);\n\n this.oauthService.stopAutomaticRefresh();\n\n this.authorizeOptions = null;\n this.lastAuthConfig = null;\n\n // Note: Do NOT clear auth signals (_accessToken, _isAuthenticated, etc.) here.\n // The access token and user info are globally valid (not per-tenant) and remain\n // valid during re-initialization. Clearing them creates a window where the HTTP\n // interceptor sends requests without a Bearer token, causing 401 errors.\n // Signals are already properly cleared on logout/session_terminated events.\n\n this._isInitialized.set(false);\n console.debug(\"AuthorizeService::uninitialize::done\");\n } finally {\n this._isInitializing.set(false);\n }\n\n console.debug(\"AuthorizeService::uninitialize::completed\");\n }\n\n private async loadUserAsync(): Promise<void> {\n const claims = this.oauthService.getIdentityClaims();\n if (!claims) {\n console.error(\"claims where null when loading identity claims\");\n return;\n }\n\n // Capture auth state BEFORE setting _isAuthenticated to true,\n // so we can distinguish initial login from token refresh below.\n const previouslyAuthenticated = this._isAuthenticated();\n\n const user = claims as IUser;\n if (user.given_name && user.family_name) {\n this._userInitials.set(user.given_name.charAt(0).toUpperCase() + user.family_name.charAt(0).toUpperCase());\n } else {\n const derived = this.deriveDisplayNameFromUsername(user.name);\n this._userInitials.set(this.deriveInitials(derived));\n }\n\n const accessToken = this.oauthService.getAccessToken();\n this._user.set(user);\n this._accessToken.set(accessToken);\n this._isAuthenticated.set(true);\n this._sessionLoading.set(false);\n\n // Parse allowed_tenants from the access token\n this._allowedTenants.set(this.parseAllowedTenantsFromToken(accessToken));\n const tokenTenantId = this.parseTenantIdFromToken(accessToken);\n this._tokenTenantId.set(tokenTenantId);\n\n // Detect tenant mismatch after silent refresh: if we were already authenticated\n // (i.e., this is a token refresh, not an initial login) and the new token's tenant_id\n // doesn't match the expected tenant from storage, trigger re-authentication.\n // This handles the case where the Identity Server returns a token for the wrong tenant\n // (e.g., after a service restart when the in-memory token-to-tenant cache is lost).\n const expectedTenantId = this.tenantStorage.getTenantId();\n if (previouslyAuthenticated && tokenTenantId && expectedTenantId &&\n tokenTenantId.toLowerCase() !== expectedTenantId.toLowerCase()) {\n console.warn(\n `AuthorizeService::loadUserAsync: Tenant mismatch after silent refresh — ` +\n `token=\"${tokenTenantId}\", expected=\"${expectedTenantId}\". Triggering re-authentication.`\n );\n this.switchTenant(expectedTenantId, window.location.href);\n return;\n }\n\n // Clear the pending tenant switch key now that we have a valid token.\n // This completes the switch cycle and prevents the guard from re-using\n // the pending tenant on subsequent route activations.\n this.clearPendingTenantSwitch();\n\n console.debug(`AuthorizeService::loadUserAsync::done (tokenTenantId=\"${tokenTenantId}\", allowedTenants=${JSON.stringify(this._allowedTenants())})`);\n }\n\n /**\n * Decodes the JWT access token payload and extracts allowed_tenants claims.\n * The claim can be a single string or an array of strings.\n */\n private parseAllowedTenantsFromToken(accessToken: string | null): string[] {\n if (!accessToken) {\n return [];\n }\n\n try {\n const parts = accessToken.split('.');\n if (parts.length !== 3) {\n return [];\n }\n\n const base64 = parts[1].replace(/-/g, '+').replace(/_/g, '/');\n const payload = JSON.parse(atob(base64));\n const allowedTenants = payload['allowed_tenants'];\n\n if (!allowedTenants) {\n return [];\n }\n\n if (Array.isArray(allowedTenants)) {\n return allowedTenants;\n }\n\n // Single value claim\n if (typeof allowedTenants === 'string') {\n return [allowedTenants];\n }\n\n return [];\n } catch (e) {\n console.warn('Failed to parse allowed_tenants from access token', e);\n return [];\n }\n }\n\n /**\n * Decodes the JWT access token payload and extracts the tenant_id claim.\n */\n private parseTenantIdFromToken(accessToken: string | null): string | null {\n if (!accessToken) {\n return null;\n }\n\n try {\n const parts = accessToken.split('.');\n if (parts.length !== 3) {\n return null;\n }\n\n const base64 = parts[1].replace(/-/g, '+').replace(/_/g, '/');\n const payload = JSON.parse(atob(base64));\n return payload['tenant_id'] ?? null;\n } catch {\n return null;\n }\n }\n\n private deriveDisplayNameFromUsername(username: string): string {\n let name = username;\n // Strip xt_{tenantId}_ prefix\n const xtMatch = name.match(/^xt_[^_]+_(.+)$/);\n if (xtMatch) { name = xtMatch[1]; }\n // Extract local part of email\n const atIndex = name.indexOf('@');\n if (atIndex > 0) { name = name.substring(0, atIndex); }\n // Split by dots and capitalize\n const parts = name.split('.').filter(p => p.length > 0);\n return parts.map(p => p.charAt(0).toUpperCase() + p.slice(1)).join(' ');\n }\n\n private deriveInitials(displayName: string): string {\n const words = displayName.split(' ').filter(w => w.length > 0);\n if (words.length >= 2) {\n return words[0].charAt(0).toUpperCase() + words[1].charAt(0).toUpperCase();\n }\n if (words.length === 1 && words[0].length >= 2) {\n return words[0].charAt(0).toUpperCase() + words[0].charAt(1).toLowerCase();\n }\n return '??';\n }\n\n /**\n * Reloads the page. This method is protected to allow mocking in tests.\n * @internal\n */\n protected reloadPage(): void {\n window.location.reload();\n }\n\n /**\n * Navigates to the given URL via full page navigation.\n * This method is protected to allow mocking in tests.\n * @internal\n */\n protected navigateTo(url: string): void {\n window.location.href = url;\n }\n}\n","export enum Roles {\n ReportingManagement = 'ReportingManagement',\n ReportingViewer = 'ReportingViewer',\n AdminPanelManagement = 'AdminPanelManagement',\n BotManagement = 'BotManagement',\n UserManagement = 'UserManagement',\n CommunicationManagement = 'CommunicationManagement',\n TenantManagement = 'TenantManagement',\n Development = 'Development',\n StreamDataAdmin = 'StreamDataAdmin',\n StreamDataWriter = 'StreamDataWriter',\n StreamDataReader = 'StreamDataReader'\n}\n","import { inject } from '@angular/core';\nimport { HttpHandlerFn, HttpInterceptorFn, HttpParams, HttpRequest } from '@angular/common/http';\nimport { AuthorizeService } from './authorize.service';\n\n// =============================================================================\n// URL MATCHING UTILITIES\n// =============================================================================\n\n/**\n * Checks if the request URL is from the same origin as the application.\n */\nfunction isSameOriginUrl(req: HttpRequest<unknown>): boolean {\n // It's an absolute url with the same origin.\n if (req.url.startsWith(`${window.location.origin}/`)) {\n return true;\n }\n\n // It's a protocol relative url with the same origin.\n // For example: //www.example.com/api/Products\n if (req.url.startsWith(`//${window.location.host}/`)) {\n return true;\n }\n\n // It's a relative url like /api/Products\n if (/^\\/[^/].*/.test(req.url)) {\n return true;\n }\n\n // It's an absolute or protocol relative url that doesn't have the same origin.\n return false;\n}\n\n/**\n * Checks if the request URL matches any of the known service URIs.\n */\nfunction isKnownServiceUri(req: HttpRequest<unknown>, serviceUris: string[] | null): boolean {\n if (serviceUris != null) {\n for (const serviceUri of serviceUris) {\n if (req.url.startsWith(serviceUri)) {\n return true;\n }\n }\n }\n return false;\n}\n\n// =============================================================================\n// FUNCTIONAL INTERCEPTOR (RECOMMENDED)\n// =============================================================================\n\n/**\n * Functional HTTP interceptor that adds Bearer token to authorized requests\n * and injects tenant context (acr_values) into token endpoint requests.\n *\n * Adds the Authorization header to requests that are either:\n * - Same-origin requests (relative URLs or same host)\n * - Requests to known service URIs configured in AuthorizeOptions\n *\n * For token endpoint POST requests (`/connect/token`), appends\n * `acr_values=tenant:{tenantId}` to the form body so the Identity Server\n * can resolve the correct tenant during refresh token exchanges.\n *\n * @example\n * ```typescript\n * // app.config.ts\n * import { provideHttpClient, withInterceptors } from '@angular/common/http';\n * import { authorizeInterceptor } from '@meshmakers/shared-auth';\n *\n * export const appConfig: ApplicationConfig = {\n * providers: [\n * provideHttpClient(withInterceptors([authorizeInterceptor])),\n * provideMmSharedAuth(),\n * ]\n * };\n * ```\n */\nexport const authorizeInterceptor: HttpInterceptorFn = (req: HttpRequest<unknown>, next: HttpHandlerFn) => {\n const authorizeService = inject(AuthorizeService);\n const token = authorizeService.getAccessTokenSync();\n const serviceUris = authorizeService.getServiceUris();\n\n if (token && (isSameOriginUrl(req) || isKnownServiceUri(req, serviceUris))) {\n req = req.clone({\n setHeaders: {\n Authorization: `Bearer ${token}`\n }\n });\n }\n\n // Inject acr_values=tenant:{tenantId} into refresh_token grant requests only.\n // This ensures the Identity Server resolves the correct tenant during\n // refresh token exchanges, even after a service restart when its\n // in-memory token-to-tenant cache is lost.\n //\n // Authorization-code exchanges must NOT carry acr_values from local storage:\n // the tenant is already bound to the authorization code on the server side,\n // and injecting a stale storage tenant (e.g., left over from a previous\n // session) would force a refresh-token mismatch on the very next call,\n // causing an infinite reload loop.\n if (req.method === 'POST' && req.url.endsWith('/connect/token') && req.body instanceof HttpParams) {\n if (req.body.get('grant_type') === 'refresh_token') {\n const tenantId = authorizeService.getStorageTenantId();\n if (tenantId) {\n req = req.clone({\n body: req.body.set('acr_values', `tenant:${tenantId}`)\n });\n }\n }\n }\n\n return next(req);\n};\n","import { inject } from '@angular/core';\nimport { ActivatedRouteSnapshot, CanActivateFn, CanMatchFn, Router, RouterStateSnapshot } from '@angular/router';\nimport { AuthorizeService } from './authorize.service';\n\n/**\n * Walks up the route tree to find the tenantId parameter.\n */\nfunction findRouteTenantId(route: ActivatedRouteSnapshot): string | undefined {\n let current: ActivatedRouteSnapshot | null = route;\n while (current) {\n if (current.params?.['tenantId']) {\n return current.params['tenantId'];\n }\n current = current.parent;\n }\n return undefined;\n}\n\n/**\n * Handles authorization check for route activation.\n * Redirects to login if not authenticated, or to home if user lacks required roles.\n * Forces re-authentication when the token's tenant_id does not match the route's tenantId.\n *\n * @param route - The activated route snapshot containing route data\n * @returns true if authorized, false otherwise\n *\n * @example\n * ```typescript\n * // Route without role requirements\n * { path: 'dashboard', component: DashboardComponent, canActivate: [authorizeGuard] }\n *\n * // Route with role requirements\n * { path: 'admin', component: AdminComponent, canActivate: [authorizeGuard], data: { roles: ['AdminPanelManagement'] } }\n * ```\n */\nexport const authorizeGuard: CanActivateFn = async (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {\n const authorizeService = inject(AuthorizeService);\n const router = inject(Router);\n\n // Use signal directly (synchronous)\n const isAuthenticated = authorizeService.isAuthenticated();\n\n if (!isAuthenticated) {\n // Check if this is a reload after switchTenant — use the stored tenantId\n const pendingTenant = authorizeService.consumePendingTenantSwitch();\n const tenantId = pendingTenant ?? findRouteTenantId(route);\n authorizeService.login(tenantId);\n return false;\n }\n\n // Force re-authentication if the token was issued for a different tenant.\n // switchTenant clears the local session and reloads, so the next guard\n // invocation enters the !isAuthenticated branch above with the correct tenantId.\n const tokenTenantId = authorizeService.tokenTenantId();\n const routeTenantId = findRouteTenantId(route);\n if (tokenTenantId && routeTenantId && routeTenantId.toLowerCase() !== tokenTenantId.toLowerCase()) {\n console.debug(`[AuthGuard] Tenant mismatch: token=\"${tokenTenantId}\", route=\"${routeTenantId}\" — attempting tenant switch`);\n // switchTenant returns false if a switch was already attempted (loop prevention).\n // Use state.url (the router's target URL) — not window.location.href which is\n // still the current page during in-app navigation (e.g., tenant switcher dropdown).\n const targetUrl = window.location.origin + state.url;\n if (authorizeService.switchTenant(routeTenantId, targetUrl)) {\n return false;\n }\n // Switch was skipped — fall through to role-based checks\n console.warn(`[AuthGuard] Tenant mismatch persists after switch attempt (token=\"${tokenTenantId}\", route=\"${routeTenantId}\"). Proceeding with current token.`);\n } else {\n // No mismatch — clear any leftover switch-attempted flag\n authorizeService.consumeSwitchAttempted();\n }\n\n // Use roles signal directly (synchronous)\n const userRoles = authorizeService.roles();\n const requiredRoles = route.data['roles'] as string[] | undefined;\n\n if (requiredRoles === undefined || requiredRoles === null) {\n if (typeof ngDevMode === 'undefined' || ngDevMode) {\n console.warn(`[AuthGuard] Route \"${route.routeConfig?.path}\" has no required roles defined — access granted to any authenticated user.`);\n }\n return true;\n }\n\n // Empty roles array (roles: []) means intentionally open to any authenticated user\n if (requiredRoles.length === 0) {\n return true;\n }\n\n if (!requiredRoles.some(role => userRoles.includes(role))) {\n // Navigate to the current tenant's home, not root ''.\n // Navigating to '' would redirect to a default tenant (e.g., octosystem),\n // causing a tenant mismatch → switchTenant → redirect loop.\n const tenantHome = routeTenantId ? `/${routeTenantId}` : '';\n await router.navigate([tenantHome]);\n return false;\n }\n\n return true;\n};\n\n/**\n * Guard for child routes. Delegates to authorizeGuard.\n *\n * @example\n * ```typescript\n * {\n * path: 'parent',\n * canActivateChild: [authorizeChildGuard],\n * children: [\n * { path: 'child', component: ChildComponent, data: { roles: ['RequiredRole'] } }\n * ]\n * }\n * ```\n */\nexport const authorizeChildGuard: CanActivateFn = authorizeGuard;\n\n/**\n * Guard for lazy-loaded routes. Checks if user is authenticated.\n * Replaces the deprecated canLoad guard.\n *\n * @example\n * ```typescript\n * {\n * path: 'lazy',\n * loadChildren: () => import('./lazy/lazy.routes'),\n * canMatch: [authorizeMatchGuard]\n * }\n * ```\n */\nexport const authorizeMatchGuard: CanMatchFn = (_route, segments) => {\n const authorizeService = inject(AuthorizeService);\n\n // Use signal directly (synchronous)\n const isAuthenticated = authorizeService.isAuthenticated();\n\n if (!isAuthenticated) {\n // The first URL segment is typically the tenantId (e.g., /:tenantId/...)\n const tenantId = segments.length > 0 ? segments[0].path : undefined;\n authorizeService.login(tenantId);\n return false;\n }\n\n return true;\n};\n\n/**\n * Guard that always allows deactivation.\n * Use this as a placeholder or override in specific routes.\n *\n * @example\n * ```typescript\n * { path: 'form', component: FormComponent, canDeactivate: [authorizeDeactivateGuard] }\n * ```\n */\nexport const authorizeDeactivateGuard = () => true;\n","/*\n * Public API Surface of shared-auth\n */\n\nimport { EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';\nimport { AuthorizeService } from './lib/authorize.service';\nimport { provideOAuthClient } from 'angular-oauth2-oidc';\n\n// Core services\nexport * from './lib/authorize.service';\nexport * from './lib/roles';\nexport { TenantAwareOAuthStorage } from './lib/tenant-aware-oauth-storage';\n\n// Functional interceptor\nexport { authorizeInterceptor } from './lib/authorize.interceptor';\n\n// Functional guards\nexport {\n authorizeGuard,\n authorizeChildGuard,\n authorizeMatchGuard,\n authorizeDeactivateGuard\n} from './lib/authorize.guard';\n\n// UI Components (Kendo) - available via '@meshmakers/shared-auth/login-ui'\n// import { LoginAppBarSectionComponent } from '@meshmakers/shared-auth/login-ui';\n\n/**\n * Provides all shared-auth dependencies.\n *\n * @example\n * ```typescript\n * // app.config.ts\n * import { provideHttpClient, withInterceptors } from '@angular/common/http';\n * import { provideMmSharedAuth, authorizeInterceptor } from '@meshmakers/shared-auth';\n *\n * export const appConfig: ApplicationConfig = {\n * providers: [\n * provideHttpClient(withInterceptors([authorizeInterceptor])),\n * provideMmSharedAuth(),\n * // ... other providers\n * ]\n * };\n * ```\n *\n * @remarks\n * Functional guards and interceptors don't need to be provided - they use inject() internally.\n * For the functional interceptor, use `provideHttpClient(withInterceptors([authorizeInterceptor]))`.\n */\nexport function provideMmSharedAuth(): EnvironmentProviders {\n return makeEnvironmentProviders([\n provideOAuthClient(),\n AuthorizeService\n ]);\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"meshmakers-shared-auth.mjs","sources":["../../../../projects/meshmakers/shared-auth/src/lib/tenant-aware-oauth-storage.ts","../../../../projects/meshmakers/shared-auth/src/lib/authorize.service.ts","../../../../projects/meshmakers/shared-auth/src/lib/roles.ts","../../../../projects/meshmakers/shared-auth/src/lib/authorize.interceptor.ts","../../../../projects/meshmakers/shared-auth/src/lib/authorization-refusal.ts","../../../../projects/meshmakers/shared-auth/src/lib/authorize.guard.ts","../../../../projects/meshmakers/shared-auth/src/public-api.ts","../../../../projects/meshmakers/shared-auth/src/meshmakers-shared-auth.ts"],"sourcesContent":["import { OAuthStorage } from 'angular-oauth2-oidc';\n\n/**\n * Known OAuth storage keys used by angular-oauth2-oidc.\n * Used by clearAllTenants() to identify and remove OAuth-related entries\n * across all tenants without affecting other localStorage data.\n */\nconst OAUTH_STORAGE_KEYS = [\n 'access_token',\n 'refresh_token',\n 'id_token',\n 'id_token_claims_obj',\n 'id_token_header',\n 'expires_at',\n 'access_token_stored_at',\n 'id_token_expires_at',\n 'id_token_stored_at',\n 'nonce',\n 'PKCE_verifier',\n 'session_state',\n 'granted_scopes',\n 'requested_route',\n];\n\n/**\n * Keys that are specific to a single OAuth authorization flow and must\n * be isolated per browser tab. These are stored in sessionStorage\n * (which is per-tab) to prevent cross-tab nonce/PKCE collisions.\n *\n * Without this, two tabs starting login simultaneously would overwrite\n * each other's nonce in shared localStorage, causing \"invalid_nonce_in_state\"\n * errors when the first tab's callback tries to validate.\n */\nconst SESSION_SCOPED_KEYS = new Set([\n 'nonce',\n 'PKCE_verifier',\n 'requested_route',\n]);\n\n/**\n * SessionStorage key used to persist the current storage tenant ID across\n * page reloads (e.g., OAuth redirects). The tenant ID must survive the\n * round-trip to the identity server because the redirect URI may not\n * contain the tenant path segment.\n */\nconst STORAGE_TENANT_KEY = 'octo_storage_tenant';\n\n/**\n * Tenant-aware OAuth storage that prefixes all keys with a tenant ID\n * and splits storage between localStorage and sessionStorage.\n *\n * **Key prefixing:** When a tenant ID is set, all storage keys are prefixed\n * with `{tenantId}__` (double underscore separator). This isolates OAuth\n * tokens per tenant, preventing race conditions during tenant switches.\n *\n * **Storage split:** Flow-specific ephemeral keys (nonce, PKCE_verifier)\n * are stored in sessionStorage (per browser tab), while tokens and session\n * data are stored in localStorage (shared across tabs). This prevents\n * cross-tab nonce collisions when multiple tabs initiate login simultaneously.\n *\n * When no tenant ID is set (null), keys are stored without a prefix,\n * maintaining backwards compatibility with existing single-tenant apps.\n *\n * @example\n * ```typescript\n * const storage = new TenantAwareOAuthStorage();\n * storage.setTenantId('maco');\n * storage.setItem('access_token', 'abc');\n * // Stored in: localStorage['maco__access_token'] = 'abc'\n *\n * storage.setItem('nonce', 'xyz');\n * // Stored in: sessionStorage['maco__nonce'] = 'xyz' (per-tab!)\n * ```\n */\nexport class TenantAwareOAuthStorage extends OAuthStorage {\n private tenantId: string | null = null;\n\n /**\n * Sets the tenant ID used to prefix storage keys.\n * Must be called before the OAuthService reads/writes tokens.\n * The tenant ID is persisted in sessionStorage so it survives OAuth redirects.\n *\n * @param tenantId The tenant ID, or null for unprefixed (backwards-compatible) mode.\n */\n setTenantId(tenantId: string | null): void {\n this.tenantId = tenantId;\n try {\n if (tenantId) {\n sessionStorage.setItem(STORAGE_TENANT_KEY, tenantId);\n } else {\n sessionStorage.removeItem(STORAGE_TENANT_KEY);\n }\n } catch {\n // sessionStorage may be unavailable\n }\n }\n\n /**\n * Returns the currently configured tenant ID.\n */\n getTenantId(): string | null {\n return this.tenantId;\n }\n\n /**\n * Restores the tenant ID from sessionStorage.\n * Call this on app startup when the tenant cannot be determined from the URL\n * (e.g., after an OAuth redirect to the root path).\n *\n * @returns The restored tenant ID, or null if none was persisted.\n */\n restoreTenantId(): string | null {\n try {\n const stored = sessionStorage.getItem(STORAGE_TENANT_KEY);\n if (stored) {\n this.tenantId = stored;\n return stored;\n }\n } catch {\n // sessionStorage may be unavailable\n }\n return null;\n }\n\n /**\n * Returns the prefixed key for the given base key.\n * When tenantId is null, returns the base key unchanged.\n */\n prefixKey(key: string): string {\n if (this.tenantId) {\n return `${this.tenantId}__${key}`;\n }\n return key;\n }\n\n getItem(key: string): string | null {\n const prefixed = this.prefixKey(key);\n if (SESSION_SCOPED_KEYS.has(key)) {\n return sessionStorage.getItem(prefixed);\n }\n return localStorage.getItem(prefixed);\n }\n\n removeItem(key: string): void {\n const prefixed = this.prefixKey(key);\n if (SESSION_SCOPED_KEYS.has(key)) {\n sessionStorage.removeItem(prefixed);\n } else {\n localStorage.removeItem(prefixed);\n }\n }\n\n setItem(key: string, data: string): void {\n const prefixed = this.prefixKey(key);\n if (SESSION_SCOPED_KEYS.has(key)) {\n sessionStorage.setItem(prefixed, data);\n } else {\n localStorage.setItem(prefixed, data);\n }\n }\n\n /**\n * Clears all OAuth-related keys for ALL tenants from both\n * localStorage and sessionStorage, while leaving non-OAuth data intact.\n *\n * Used during full logout to ensure no stale tokens remain for any tenant.\n */\n clearAllTenants(): void {\n this.clearOAuthKeysFromStorage(localStorage);\n this.clearOAuthKeysFromStorage(sessionStorage);\n }\n\n private clearOAuthKeysFromStorage(storage: Storage): void {\n const keysToRemove: string[] = [];\n for (let i = 0; i < storage.length; i++) {\n const storageKey = storage.key(i);\n if (!storageKey) continue;\n\n for (const oauthKey of OAUTH_STORAGE_KEYS) {\n if (storageKey === oauthKey || storageKey.endsWith('__' + oauthKey)) {\n keysToRemove.push(storageKey);\n break;\n }\n }\n }\n for (const key of keysToRemove) {\n storage.removeItem(key);\n }\n }\n}\n","import { Injectable, Signal, WritableSignal, computed, inject, signal } from \"@angular/core\";\nimport { AuthConfig, OAuthService } from \"angular-oauth2-oidc\";\nimport { Roles } from \"./roles\";\nimport { TenantAwareOAuthStorage } from \"./tenant-aware-oauth-storage\";\n\nexport interface IUser {\n family_name: string | null;\n given_name: string | null;\n name: string;\n role: string[] | null;\n sub: string;\n idp: string;\n email: string | null;\n}\n\nexport interface DiscoveryDocumentRetryOptions {\n attempts: number;\n initialDelayMs: number;\n maxDelayMs: number;\n}\n\nexport class AuthorizeOptions {\n wellKnownServiceUris?: string[];\n // Url of the Identity Provider\n issuer?: string;\n // URL of the SPA to redirect the user to after login\n redirectUri?: string;\n postLogoutRedirectUri?: string;\n // The SPA's id. The SPA is registered with this id at the auth-server\n clientId?: string;\n // set the scope for the permissions the client should request\n // The first three are defined by OIDC. The 4th is a use case-specific one\n scope?: string;\n showDebugInformation?: boolean;\n sessionChecksEnabled?: boolean;\n // Default tenant ID for single-tenant apps. When set, login() uses this tenant\n // if no tenantId is explicitly provided (sends acr_values=tenant:{defaultTenantId}).\n defaultTenantId?: string;\n // Retry policy for loadDiscoveryDocumentAndTryLogin() — covers the brief\n // window during which the Identity service is unreachable after a redeploy\n // or rolling restart. Defaults: 6 attempts, 1500ms initial, 30000ms cap.\n discoveryDocumentRetry?: DiscoveryDocumentRetryOptions;\n}\n\n@Injectable()\nexport class AuthorizeService {\n private readonly oauthService = inject(OAuthService);\n\n // =============================================================================\n // INTERNAL STATE (Writable Signals)\n // =============================================================================\n\n private readonly _isAuthenticated: WritableSignal<boolean> = signal(false);\n private readonly _issuer: WritableSignal<string | null> = signal(null);\n private readonly _accessToken: WritableSignal<string | null> = signal(null);\n private readonly _user: WritableSignal<IUser | null> = signal(null);\n private readonly _userInitials: WritableSignal<string | null> = signal(null);\n private readonly _isInitialized: WritableSignal<boolean> = signal(false);\n private readonly _isInitializing: WritableSignal<boolean> = signal(false);\n private readonly _sessionLoading: WritableSignal<boolean> = signal(false);\n private readonly _allowedTenants: WritableSignal<string[]> = signal([]);\n private readonly _tokenTenantId: WritableSignal<string | null> = signal(null);\n private readonly _discoveryDocumentError: WritableSignal<Error | null> = signal(null);\n private readonly _discoveryDocumentRetryAttempt: WritableSignal<number> = signal(0);\n\n private static readonly TENANT_REAUTH_KEY = 'octo_tenant_reauth';\n private static readonly TENANT_SWITCH_ATTEMPTED_KEY = 'octo_tenant_switch_attempted';\n private static readonly DEFAULT_DISCOVERY_RETRY: DiscoveryDocumentRetryOptions = {\n attempts: 6,\n initialDelayMs: 1500,\n maxDelayMs: 30000\n };\n\n private readonly tenantStorage = new TenantAwareOAuthStorage();\n private _loginInProgress = false;\n private authorizeOptions: AuthorizeOptions | null = null;\n private lastAuthConfig: AuthConfig | null = null;\n\n // =============================================================================\n // PUBLIC API (Readonly Signals) - NEW API\n // =============================================================================\n\n /**\n * Signal indicating whether the user is currently authenticated.\n */\n readonly isAuthenticated: Signal<boolean> = this._isAuthenticated.asReadonly();\n\n /**\n * Signal containing the issuer URL.\n */\n readonly issuer: Signal<string | null> = this._issuer.asReadonly();\n\n /**\n * Signal containing the current access token.\n */\n readonly accessToken: Signal<string | null> = this._accessToken.asReadonly();\n\n /**\n * Signal containing the current user information.\n */\n readonly user: Signal<IUser | null> = this._user.asReadonly();\n\n /**\n * Computed signal containing the user's initials (e.g., \"JD\" for John Doe).\n */\n readonly userInitials: Signal<string | null> = this._userInitials.asReadonly();\n\n /**\n * Signal indicating whether the session is currently loading.\n */\n readonly sessionLoading: Signal<boolean> = this._sessionLoading.asReadonly();\n\n /**\n * Signal containing the list of tenants the user is allowed to access.\n * Parsed from the allowed_tenants claims in the access token.\n */\n readonly allowedTenants: Signal<string[]> = this._allowedTenants.asReadonly();\n\n /**\n * Signal containing the tenant_id claim from the current access token.\n * Used to detect tenant mismatch when navigating between tenants.\n */\n readonly tokenTenantId: Signal<string | null> = this._tokenTenantId.asReadonly();\n\n /**\n * Computed signal containing the user's roles.\n */\n readonly roles: Signal<string[]> = computed(() => this._user()?.role ?? []);\n\n /**\n * Signal carrying the last error from loadDiscoveryDocumentAndTryLogin().\n * Non-null means the Identity service was unreachable across the full retry\n * budget; host apps can render a recovery screen and call retryDiscoveryDocument().\n */\n readonly discoveryDocumentError: Signal<Error | null> = this._discoveryDocumentError.asReadonly();\n\n /**\n * Signal containing the current discovery-document retry attempt (1-based while\n * retrying, 0 when idle). Useful for showing \"Reconnecting (attempt N)…\" UI.\n */\n readonly discoveryDocumentRetryAttempt: Signal<number> = this._discoveryDocumentRetryAttempt.asReadonly();\n\n /**\n * Computed signal for the user's display name.\n * Uses given_name + family_name if available, otherwise derives from the username.\n */\n readonly displayName: Signal<string | null> = computed(() => {\n const user = this._user();\n if (!user) return null;\n if (user.given_name && user.family_name) {\n return user.given_name + ' ' + user.family_name;\n }\n return this.deriveDisplayNameFromUsername(user.name);\n });\n\n constructor() {\n console.debug(\"AuthorizeService::created\");\n\n this.oauthService.discoveryDocumentLoaded$.subscribe((_) => {\n console.debug(\"discoveryDocumentLoaded$\");\n });\n\n this.oauthService.events.subscribe((e) => {\n console.debug(\"oauth/oidc event\", e);\n });\n\n this.oauthService.events\n .pipe((source) => source)\n .subscribe((e) => {\n if (e.type === \"session_terminated\") {\n console.debug(\"Your session has been terminated!\");\n this._accessToken.set(null);\n this._user.set(null);\n this._isAuthenticated.set(false);\n this._tokenTenantId.set(null);\n // Reload the page to trigger the auth flow and redirect to login\n this.reloadPage();\n }\n\n if (e.type === \"token_refresh_error\") {\n console.warn(\"AuthorizeService: Token refresh failed — clearing local session and reloading\");\n this._accessToken.set(null);\n this._user.set(null);\n this._isAuthenticated.set(false);\n this._tokenTenantId.set(null);\n this._allowedTenants.set([]);\n // Do NOT call oauthService.logOut() here — it destroys the server-side\n // session at the Identity Server's end_session_endpoint. If the refresh\n // token is invalid (e.g., after an IDS restart), the user still has a\n // valid IDS session cookie and can silently re-authenticate. Calling\n // logOut() would force a full re-login with credentials.\n // Instead, clear local tokens and reload — the guard will trigger\n // login() which obtains a fresh authorization code via the existing session.\n this.tenantStorage.clearAllTenants();\n this.reloadPage();\n }\n });\n\n this.oauthService.events.subscribe(async (e) => {\n if (e.type === \"token_received\") {\n this._loginInProgress = false;\n await this.loadUserAsync();\n }\n });\n\n this.oauthService.events.subscribe(async (e) => {\n if (e.type === \"session_unchanged\") {\n if (this._user() == null) {\n await this.loadUserAsync();\n }\n }\n });\n\n this.oauthService.events.subscribe((e) => {\n if (e.type === \"logout\") {\n console.debug(\"AuthorizeService: Logout event received\");\n this._accessToken.set(null);\n this._user.set(null);\n this._isAuthenticated.set(false);\n this._tokenTenantId.set(null);\n this._allowedTenants.set([]);\n // Do NOT call reloadPage() here — oauthService.logOut() already\n // redirects to the Identity Server's end_session_endpoint.\n // Calling reload() would race with that redirect and cause the\n // page to reload before the server-side session is terminated,\n // leaving the user still logged in.\n }\n });\n\n // Listen for storage events from other tabs (e.g., SLO logout callback)\n // This enables immediate cross-tab logout detection\n window.addEventListener('storage', (event) => {\n console.debug(\"AuthorizeService: Storage event received\", event.key, event.newValue);\n // Check if the current tenant's access_token was removed (logout in another tab)\n // With per-tenant storage, the key is prefixed (e.g., \"maco__access_token\")\n // Note: OAuth library may set to empty string or null when clearing\n const expectedKey = this.tenantStorage.prefixKey('access_token');\n if (event.key === expectedKey && (event.newValue === null || event.newValue === '') && this._isAuthenticated()) {\n console.debug(\"AuthorizeService: Access token removed in another tab - logging out and reloading\");\n this._accessToken.set(null);\n this._user.set(null);\n this._isAuthenticated.set(false);\n this._tokenTenantId.set(null);\n // Reload the page to trigger the auth flow and redirect to login\n this.reloadPage();\n }\n });\n\n // Also listen for BroadcastChannel messages for cross-tab logout\n // This is more reliable than storage events for iframe-based SLO\n if (typeof BroadcastChannel !== 'undefined') {\n console.debug(\"AuthorizeService: Setting up BroadcastChannel listener for 'octo-auth-logout'\");\n const logoutChannel = new BroadcastChannel('octo-auth-logout');\n logoutChannel.onmessage = (event) => {\n console.debug(\"AuthorizeService: BroadcastChannel message received\", event.data);\n if (event.data?.type === 'logout' && this._isAuthenticated()) {\n console.debug(\"AuthorizeService: Logout broadcast received - reloading\");\n this._accessToken.set(null);\n this._user.set(null);\n this._isAuthenticated.set(false);\n this._tokenTenantId.set(null);\n this.reloadPage();\n }\n };\n } else {\n console.warn(\"AuthorizeService: BroadcastChannel not supported in this browser\");\n }\n\n // When the tab returns from the background and we previously failed to load\n // the discovery document (e.g. because the Identity service was redeploying),\n // try to recover automatically. Without this the loading icon would sit\n // forever until the user manually reloads.\n if (typeof document !== 'undefined') {\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState !== 'visible') {\n return;\n }\n if (this._discoveryDocumentError() && !this._isInitializing() && this.authorizeOptions) {\n console.debug(\"AuthorizeService: visibilitychange → retrying after discovery-document failure\");\n // Re-run initialize() with the last options. We intentionally swallow\n // the error here — initialize() already records it in the signal.\n void this.initialize(this.authorizeOptions).catch(() => undefined);\n }\n });\n }\n }\n\n /**\n * Public hook to retry loading the discovery document after a failure.\n * No-op while initialization is in progress.\n */\n public async retryDiscoveryDocument(): Promise<void> {\n if (this._isInitializing() || !this.authorizeOptions) {\n return;\n }\n await this.initialize(this.authorizeOptions);\n }\n\n /**\n * Checks if the current user has the specified role.\n */\n public isInRole(role: Roles): boolean {\n return this._user()?.role?.includes(role) ?? false;\n }\n\n /**\n * Gets the configured service URIs that should receive the authorization token.\n */\n public getServiceUris(): string[] | null {\n return this.authorizeOptions?.wellKnownServiceUris ?? null;\n }\n\n /**\n * Sets the tenant ID for per-tenant token storage isolation.\n * Must be called BEFORE initialize() to ensure tokens are stored/retrieved\n * under the correct tenant prefix in localStorage.\n *\n * When set, all OAuth storage keys are prefixed with `{tenantId}__`\n * (e.g., `maco__access_token`), preventing token collisions between tenants.\n * The tenant ID is also persisted in sessionStorage so it survives OAuth redirects.\n *\n * @param tenantId The tenant ID to use for storage key prefixing, or null for unprefixed mode.\n */\n public setStorageTenantId(tenantId: string | null): void {\n this.tenantStorage.setTenantId(tenantId);\n console.debug(`AuthorizeService::setStorageTenantId(\"${tenantId}\")`);\n }\n\n /**\n * Returns the current storage tenant ID.\n * Used by the interceptor to inject acr_values into token endpoint requests.\n */\n public getStorageTenantId(): string | null {\n return this.tenantStorage.getTenantId();\n }\n\n /**\n * Restores the storage tenant ID from sessionStorage.\n * Use this when the tenant ID cannot be determined from the URL\n * (e.g., after an OAuth redirect to the root path).\n *\n * @returns The restored tenant ID, or null if none was persisted.\n */\n public restoreStorageTenantId(): string | null {\n const tenantId = this.tenantStorage.restoreTenantId();\n if (tenantId) {\n console.debug(`AuthorizeService::restoreStorageTenantId(\"${tenantId}\")`);\n }\n return tenantId;\n }\n\n /**\n * Gets the current access token synchronously.\n * Use this for functional interceptors that need immediate access to the token.\n *\n * @returns The current access token or null if not authenticated\n */\n public getAccessTokenSync(): string | null {\n return this._accessToken();\n }\n\n /**\n * Checks if the user is allowed to access the specified tenant.\n * Returns true if no allowed_tenants claims are present (backwards compatibility).\n */\n public isTenantAllowed(tenantId: string): boolean {\n const allowed = this._allowedTenants();\n if (allowed.length === 0) {\n return true; // No claims = backwards compatible (old tokens)\n }\n return allowed.some(t => t.toLowerCase() === tenantId.toLowerCase());\n }\n\n /**\n * Initiates the login flow.\n * Multiple guards (canActivateChild, canMatch) may call this concurrently\n * during route resolution. Only the first call proceeds — subsequent calls\n * are skipped to prevent generating a new nonce that overwrites the first\n * one, which would cause an \"invalid_nonce_in_state\" error after the\n * identity server redirects back.\n *\n * @param tenantId Optional tenant ID. When provided, includes acr_values=tenant:{tenantId}\n * so the identity server redirects to the correct tenant's login page.\n */\n public login(tenantId?: string): void {\n if (this._loginInProgress) {\n console.debug('AuthorizeService::login skipped (already in progress)');\n return;\n }\n this._loginInProgress = true;\n\n const effectiveTenantId = tenantId ?? this.authorizeOptions?.defaultTenantId;\n if (effectiveTenantId) {\n this.oauthService.initImplicitFlow('', { acr_values: `tenant:${effectiveTenantId}` });\n } else {\n this.oauthService.initImplicitFlow();\n }\n }\n\n /**\n * Forces re-authentication for a different tenant by clearing the local\n * OAuth session and reloading the page. On reload, the guard will see\n * isAuthenticated=false and call login(tenantId) with the correct acr_values.\n *\n * This is used when the current token's tenant_id does not match the\n * route's tenantId (e.g., navigating from octosystem to meshtest).\n */\n /**\n * Returns true if the switch was initiated, false if skipped (loop prevention).\n */\n public switchTenant(targetTenantId: string, targetUrl: string): boolean {\n console.debug(`AuthorizeService::switchTenant to \"${targetTenantId}\" at \"${targetUrl}\"`);\n\n // Prevent infinite redirect loop: if we already attempted a switch to this\n // exact tenant and ended up here again, the Identity Server did not issue\n // a token for the target tenant. Do NOT attempt again.\n try {\n const previousAttempt = sessionStorage.getItem(AuthorizeService.TENANT_SWITCH_ATTEMPTED_KEY);\n if (previousAttempt && previousAttempt.toLowerCase() === targetTenantId.toLowerCase()) {\n console.warn(`AuthorizeService::switchTenant — already attempted switch to \"${targetTenantId}\", skipping to prevent loop`);\n return false;\n }\n } catch {\n // sessionStorage may be unavailable\n }\n\n // Store target tenant so login() uses the correct acr_values after reload\n try {\n sessionStorage.setItem(AuthorizeService.TENANT_REAUTH_KEY, targetTenantId);\n sessionStorage.setItem(AuthorizeService.TENANT_SWITCH_ATTEMPTED_KEY, targetTenantId);\n } catch {\n // sessionStorage may be unavailable\n }\n\n // Stop automatic refresh and session checks to prevent the OAuth library\n // from firing additional authorize requests during the page reload window.\n // This prevents race conditions where a session check or silent refresh\n // could overwrite the nonce/PKCE verifier of the new tenant's auth flow.\n this.oauthService.stopAutomaticRefresh();\n\n // With per-tenant storage (TenantAwareOAuthStorage), we do NOT clear\n // tokens from localStorage. Each tenant's tokens are stored under\n // prefixed keys (e.g., \"maco__access_token\") and are isolated from\n // each other. After page reload, the storage tenant will be set to\n // the target tenant, and cached tokens (if valid) can be reused.\n\n // Clear our signals\n this._accessToken.set(null);\n this._user.set(null);\n this._isAuthenticated.set(false);\n this._tokenTenantId.set(null);\n\n // Full page navigation to the target URL — triggers fresh auth flow\n this.navigateTo(targetUrl);\n return true;\n }\n\n /**\n * Returns the pending tenant switch target (if any) WITHOUT clearing it.\n * The key persists across the OAuth redirect cycle (guard → IDS → callback → guard)\n * and is only cleared after a successful token exchange in loadUserAsync().\n *\n * Previously this method removed the key immediately, but that caused a race condition:\n * the guard consumed it before the IDS redirect, and after the callback redirect the\n * key was gone — causing the guard to fall back to the route tenant (wrong tenant).\n */\n public consumePendingTenantSwitch(): string | null {\n try {\n return sessionStorage.getItem(AuthorizeService.TENANT_REAUTH_KEY);\n } catch {\n return null;\n }\n }\n\n /**\n * Clears the pending tenant switch key. Called after a successful token exchange\n * when the token's tenant_id matches the target, completing the switch cycle.\n */\n public clearPendingTenantSwitch(): void {\n try {\n sessionStorage.removeItem(AuthorizeService.TENANT_REAUTH_KEY);\n } catch {\n // sessionStorage may be unavailable\n }\n }\n\n /**\n * Returns the tenant for which a switch was already attempted (if any) and clears it.\n * Used by the guard to prevent infinite redirect loops: if the Identity Server\n * issues a token for the wrong tenant even after a switch, we skip the second attempt.\n */\n public consumeSwitchAttempted(): string | null {\n try {\n const tenantId = sessionStorage.getItem(AuthorizeService.TENANT_SWITCH_ATTEMPTED_KEY);\n sessionStorage.removeItem(AuthorizeService.TENANT_SWITCH_ATTEMPTED_KEY);\n return tenantId;\n } catch {\n return null;\n }\n }\n\n /**\n * Logs out the current user by redirecting to the Identity Server's\n * OIDC end_session_endpoint for proper Single Logout (SLO).\n *\n * We cannot rely on oauthService.logOut() for the redirect because it calls\n * clearStorage() which may clear internal state before the redirect happens.\n * Instead, we capture the logoutUrl and id_token first, then clear state,\n * then manually redirect to the end_session_endpoint.\n */\n public logout(): void {\n // Capture the current tenant BEFORE clearing storage so the host app\n // can return the user to the same tenant after the IdS end_session\n // round-trip. The post_logout_redirect_uri registered with the IdS is\n // typically `${origin}/` and therefore drops the tenant path segment,\n // so without this hint the next login would fall through to whatever\n // default-tenant redirect the host application has wired up.\n const currentTenant = this.tenantStorage.getTenantId();\n if (currentTenant) {\n try {\n sessionStorage.setItem('octo_post_logout_tenant', currentTenant);\n } catch {\n // sessionStorage may be unavailable\n }\n }\n\n // Read the end_session_endpoint (stored as logoutUrl on the service) and id_token BEFORE clearing storage\n const endSessionEndpoint = this.oauthService.logoutUrl;\n const idToken = this.oauthService.getIdToken();\n const postLogoutRedirectUri = this.lastAuthConfig?.postLogoutRedirectUri;\n\n // Clear local OAuth state (tokens, discovery doc, etc.)\n this.oauthService.logOut(true); // true = noRedirectToLogoutUrl (we redirect manually)\n\n // Clear OAuth tokens for ALL tenants, not just the current one.\n // With per-tenant storage, oauthService.logOut() only clears the current\n // tenant's prefixed keys. We need to ensure no stale tokens remain for\n // any tenant after a full logout.\n this.tenantStorage.clearAllTenants();\n\n if (endSessionEndpoint) {\n // Build the end_session URL with id_token_hint and post_logout_redirect_uri\n const params = new URLSearchParams();\n if (idToken) {\n params.set('id_token_hint', idToken);\n }\n if (postLogoutRedirectUri) {\n params.set('post_logout_redirect_uri', postLogoutRedirectUri);\n }\n this.navigateTo(`${endSessionEndpoint}?${params.toString()}`);\n } else {\n // Fallback: no end_session_endpoint available, just reload\n this.reloadPage();\n }\n }\n\n /**\n * Updates the redirect URIs without performing a full re-initialization.\n * Use this when the OAuth session is already established and only the\n * redirect targets need to change (e.g., when switching tenants).\n */\n public updateRedirectUris(redirectUri: string, postLogoutRedirectUri: string): void {\n if (this.authorizeOptions) {\n this.authorizeOptions.redirectUri = redirectUri;\n this.authorizeOptions.postLogoutRedirectUri = postLogoutRedirectUri;\n }\n\n if (this.lastAuthConfig) {\n this.lastAuthConfig.redirectUri = redirectUri;\n this.lastAuthConfig.postLogoutRedirectUri = postLogoutRedirectUri;\n }\n\n // Update the redirect URIs directly on the OAuthService without calling\n // configure(), because configure() does Object.assign(this, new AuthConfig(), config)\n // which resets ALL properties — including logoutUrl, tokenEndpoint, and other\n // discovery document endpoints — back to their AuthConfig defaults (empty).\n this.oauthService.redirectUri = redirectUri;\n this.oauthService.postLogoutRedirectUri = postLogoutRedirectUri;\n\n console.debug(\"AuthorizeService::updateRedirectUris::done\");\n }\n\n /**\n * Refreshes the access token and updates the allowed tenants signal.\n * Call this after actions that change the user's tenant access (e.g., provisioning).\n */\n public async refreshAccessToken(): Promise<void> {\n console.debug(\"AuthorizeService::refreshAccessToken::started\");\n await this.oauthService.refreshToken();\n await this.loadUserAsync();\n console.debug(\"AuthorizeService::refreshAccessToken::done\");\n }\n\n /**\n * Initializes the authorization service with the specified options.\n */\n public async initialize(authorizeOptions: AuthorizeOptions): Promise<void> {\n console.debug(\"AuthorizeService::initialize::started\");\n\n await this.uninitialize();\n\n if (this._isInitializing()) {\n return;\n }\n if (this._isInitialized()) {\n console.debug(\"AuthorizeService::initialize::alreadyInitialized\");\n return;\n }\n this._isInitializing.set(true);\n\n try {\n const config: AuthConfig = {\n responseType: \"code\",\n issuer: authorizeOptions.issuer,\n redirectUri: authorizeOptions.redirectUri,\n postLogoutRedirectUri: authorizeOptions.postLogoutRedirectUri,\n clientId: authorizeOptions.clientId,\n scope: authorizeOptions.scope,\n showDebugInformation: authorizeOptions.showDebugInformation,\n sessionChecksEnabled: authorizeOptions.sessionChecksEnabled,\n sessionCheckIntervall: 60 * 1000,\n preserveRequestedRoute: true\n };\n\n this.authorizeOptions = authorizeOptions;\n this.lastAuthConfig = config;\n\n this.oauthService.setStorage(this.tenantStorage);\n this.oauthService.configure(config);\n\n console.debug(\"AuthorizeService::initialize::loadingDiscoveryDocumentAndTryLogin\");\n await this.loadDiscoveryDocumentAndTryLoginWithRetry(\n authorizeOptions.discoveryDocumentRetry ?? AuthorizeService.DEFAULT_DISCOVERY_RETRY\n );\n\n console.debug(\"AuthorizeService::initialize::setupAutomaticSilentRefresh\");\n this.oauthService.setupAutomaticSilentRefresh();\n\n this._issuer.set(authorizeOptions.issuer ?? null);\n\n if (this.oauthService.hasValidIdToken()) {\n // if the idToken is still valid, we can use the session\n console.debug(\"AuthorizeService::initialize::hasValidIdToken\");\n this._sessionLoading.set(true);\n await this.oauthService.refreshToken();\n }\n\n this._isInitialized.set(true);\n console.debug(\"AuthorizeService::initialize::done\");\n } finally {\n this._isInitializing.set(false);\n }\n\n console.debug(\"AuthorizeService::initialize::completed\");\n }\n\n /**\n * Wraps oauthService.loadDiscoveryDocumentAndTryLogin() with bounded\n * exponential-backoff retries so a brief outage of the Identity service\n * (cold start after redeploy, rolling restart, transient ingress 5xx)\n * does not leave the SPA stuck on the loading icon.\n *\n * The CORS-error symptom seen on test-2 — \"No 'Access-Control-Allow-Origin'\n * header\" against /.well-known/openid-configuration — is exactly such a\n * transient failure: the ingress synthesises a default error page that\n * does not carry the CORS headers, so the browser reports a CORS violation\n * rather than the actual HTTP status. Retrying after the backend comes\n * back resolves it without user interaction.\n *\n * Throws the last error if every attempt fails. The error is also stored\n * in the discoveryDocumentError signal so callers can render a recovery UI.\n */\n private async loadDiscoveryDocumentAndTryLoginWithRetry(\n retry: DiscoveryDocumentRetryOptions\n ): Promise<void> {\n const attempts = Math.max(1, retry.attempts);\n const initialDelayMs = Math.max(0, retry.initialDelayMs);\n const maxDelayMs = Math.max(initialDelayMs, retry.maxDelayMs);\n\n let lastError: unknown = null;\n for (let attempt = 1; attempt <= attempts; attempt++) {\n this._discoveryDocumentRetryAttempt.set(attempt);\n try {\n await this.oauthService.loadDiscoveryDocumentAndTryLogin();\n this._discoveryDocumentError.set(null);\n this._discoveryDocumentRetryAttempt.set(0);\n if (attempt > 1) {\n console.info(\n `AuthorizeService: discovery document loaded on retry attempt ${attempt}/${attempts}`\n );\n }\n return;\n } catch (err) {\n lastError = err;\n if (attempt === attempts) {\n break;\n }\n const delayMs = Math.min(\n initialDelayMs * Math.pow(2, attempt - 1),\n maxDelayMs\n );\n console.warn(\n `AuthorizeService: discovery document load failed (attempt ${attempt}/${attempts}); retrying in ${delayMs}ms`,\n err\n );\n await new Promise<void>(resolve => setTimeout(resolve, delayMs));\n }\n }\n\n const error = lastError instanceof Error\n ? lastError\n : new Error('Failed to load OIDC discovery document');\n this._discoveryDocumentError.set(error);\n this._discoveryDocumentRetryAttempt.set(0);\n console.error(\n `AuthorizeService: discovery document load failed after ${attempts} attempt(s)`,\n error\n );\n throw error;\n }\n\n /**\n * Uninitializes the authorization service.\n */\n public async uninitialize(): Promise<void> {\n console.debug(\"AuthorizeService::uninitialize::started\");\n\n if (this._isInitializing()) {\n return;\n }\n if (!this._isInitialized()) {\n console.debug(\"AuthorizeService::uninitialize::alreadyUninitialized\");\n return;\n }\n\n try {\n this._isInitializing.set(true);\n\n this.oauthService.stopAutomaticRefresh();\n\n this.authorizeOptions = null;\n this.lastAuthConfig = null;\n\n // Note: Do NOT clear auth signals (_accessToken, _isAuthenticated, etc.) here.\n // The access token and user info are globally valid (not per-tenant) and remain\n // valid during re-initialization. Clearing them creates a window where the HTTP\n // interceptor sends requests without a Bearer token, causing 401 errors.\n // Signals are already properly cleared on logout/session_terminated events.\n\n this._isInitialized.set(false);\n console.debug(\"AuthorizeService::uninitialize::done\");\n } finally {\n this._isInitializing.set(false);\n }\n\n console.debug(\"AuthorizeService::uninitialize::completed\");\n }\n\n private async loadUserAsync(): Promise<void> {\n const claims = this.oauthService.getIdentityClaims();\n if (!claims) {\n console.error(\"claims where null when loading identity claims\");\n return;\n }\n\n // Capture auth state BEFORE setting _isAuthenticated to true,\n // so we can distinguish initial login from token refresh below.\n const previouslyAuthenticated = this._isAuthenticated();\n\n const user = claims as IUser;\n if (user.given_name && user.family_name) {\n this._userInitials.set(user.given_name.charAt(0).toUpperCase() + user.family_name.charAt(0).toUpperCase());\n } else {\n const derived = this.deriveDisplayNameFromUsername(user.name);\n this._userInitials.set(this.deriveInitials(derived));\n }\n\n const accessToken = this.oauthService.getAccessToken();\n this._user.set(user);\n this._accessToken.set(accessToken);\n this._isAuthenticated.set(true);\n this._sessionLoading.set(false);\n\n // Parse allowed_tenants from the access token\n this._allowedTenants.set(this.parseAllowedTenantsFromToken(accessToken));\n const tokenTenantId = this.parseTenantIdFromToken(accessToken);\n this._tokenTenantId.set(tokenTenantId);\n\n // Detect tenant mismatch after silent refresh: if we were already authenticated\n // (i.e., this is a token refresh, not an initial login) and the new token's tenant_id\n // doesn't match the expected tenant from storage, trigger re-authentication.\n // This handles the case where the Identity Server returns a token for the wrong tenant\n // (e.g., after a service restart when the in-memory token-to-tenant cache is lost).\n const expectedTenantId = this.tenantStorage.getTenantId();\n if (previouslyAuthenticated && tokenTenantId && expectedTenantId &&\n tokenTenantId.toLowerCase() !== expectedTenantId.toLowerCase()) {\n console.warn(\n `AuthorizeService::loadUserAsync: Tenant mismatch after silent refresh — ` +\n `token=\"${tokenTenantId}\", expected=\"${expectedTenantId}\". Triggering re-authentication.`\n );\n this.switchTenant(expectedTenantId, window.location.href);\n return;\n }\n\n // Clear the pending tenant switch key now that we have a valid token.\n // This completes the switch cycle and prevents the guard from re-using\n // the pending tenant on subsequent route activations.\n this.clearPendingTenantSwitch();\n\n console.debug(`AuthorizeService::loadUserAsync::done (tokenTenantId=\"${tokenTenantId}\", allowedTenants=${JSON.stringify(this._allowedTenants())})`);\n }\n\n /**\n * Decodes the JWT access token payload and extracts allowed_tenants claims.\n * The claim can be a single string or an array of strings.\n */\n private parseAllowedTenantsFromToken(accessToken: string | null): string[] {\n if (!accessToken) {\n return [];\n }\n\n try {\n const parts = accessToken.split('.');\n if (parts.length !== 3) {\n return [];\n }\n\n const base64 = parts[1].replace(/-/g, '+').replace(/_/g, '/');\n const payload = JSON.parse(atob(base64));\n const allowedTenants = payload['allowed_tenants'];\n\n if (!allowedTenants) {\n return [];\n }\n\n if (Array.isArray(allowedTenants)) {\n return allowedTenants;\n }\n\n // Single value claim\n if (typeof allowedTenants === 'string') {\n return [allowedTenants];\n }\n\n return [];\n } catch (e) {\n console.warn('Failed to parse allowed_tenants from access token', e);\n return [];\n }\n }\n\n /**\n * Decodes the JWT access token payload and extracts the tenant_id claim.\n */\n private parseTenantIdFromToken(accessToken: string | null): string | null {\n if (!accessToken) {\n return null;\n }\n\n try {\n const parts = accessToken.split('.');\n if (parts.length !== 3) {\n return null;\n }\n\n const base64 = parts[1].replace(/-/g, '+').replace(/_/g, '/');\n const payload = JSON.parse(atob(base64));\n return payload['tenant_id'] ?? null;\n } catch {\n return null;\n }\n }\n\n private deriveDisplayNameFromUsername(username: string): string {\n let name = username;\n // Strip xt_{tenantId}_ prefix\n const xtMatch = name.match(/^xt_[^_]+_(.+)$/);\n if (xtMatch) { name = xtMatch[1]; }\n // Extract local part of email\n const atIndex = name.indexOf('@');\n if (atIndex > 0) { name = name.substring(0, atIndex); }\n // Split by dots and capitalize\n const parts = name.split('.').filter(p => p.length > 0);\n return parts.map(p => p.charAt(0).toUpperCase() + p.slice(1)).join(' ');\n }\n\n private deriveInitials(displayName: string): string {\n const words = displayName.split(' ').filter(w => w.length > 0);\n if (words.length >= 2) {\n return words[0].charAt(0).toUpperCase() + words[1].charAt(0).toUpperCase();\n }\n if (words.length === 1 && words[0].length >= 2) {\n return words[0].charAt(0).toUpperCase() + words[0].charAt(1).toLowerCase();\n }\n return '??';\n }\n\n /**\n * Reloads the page. This method is protected to allow mocking in tests.\n * @internal\n */\n protected reloadPage(): void {\n window.location.reload();\n }\n\n /**\n * Navigates to the given URL via full page navigation.\n * This method is protected to allow mocking in tests.\n * @internal\n */\n protected navigateTo(url: string): void {\n window.location.href = url;\n }\n}\n","export enum Roles {\n ReportingManagement = 'ReportingManagement',\n ReportingViewer = 'ReportingViewer',\n AdminPanelManagement = 'AdminPanelManagement',\n BotManagement = 'BotManagement',\n UserManagement = 'UserManagement',\n CommunicationManagement = 'CommunicationManagement',\n TenantManagement = 'TenantManagement',\n Development = 'Development',\n StreamDataAdmin = 'StreamDataAdmin',\n StreamDataWriter = 'StreamDataWriter',\n StreamDataReader = 'StreamDataReader'\n}\n","import { inject } from '@angular/core';\nimport {\n HttpErrorResponse,\n HttpHandlerFn,\n HttpInterceptorFn,\n HttpParams,\n HttpRequest\n} from '@angular/common/http';\nimport { from, catchError, switchMap, throwError } from 'rxjs';\nimport { AuthorizeService } from './authorize.service';\n\n// =============================================================================\n// URL MATCHING UTILITIES\n// =============================================================================\n\n/**\n * Checks if the request URL is from the same origin as the application.\n */\nfunction isSameOriginUrl(req: HttpRequest<unknown>): boolean {\n // It's an absolute url with the same origin.\n if (req.url.startsWith(`${window.location.origin}/`)) {\n return true;\n }\n\n // It's a protocol relative url with the same origin.\n // For example: //www.example.com/api/Products\n if (req.url.startsWith(`//${window.location.host}/`)) {\n return true;\n }\n\n // It's a relative url like /api/Products\n if (/^\\/[^/].*/.test(req.url)) {\n return true;\n }\n\n // It's an absolute or protocol relative url that doesn't have the same origin.\n return false;\n}\n\n/**\n * Checks if the request URL matches any of the known service URIs.\n */\nfunction isKnownServiceUri(req: HttpRequest<unknown>, serviceUris: string[] | null): boolean {\n if (serviceUris != null) {\n for (const serviceUri of serviceUris) {\n // An unset host must never match. Blank is a prefix of every URL, and the\n // platform answers `/` for an unconfigured service, which would also match\n // protocol-relative URLs that isSameOriginUrl deliberately rejects.\n if (!serviceUri || serviceUri === '/' || !req.url.startsWith(serviceUri)) {\n continue;\n }\n\n // A bare prefix test would also accept a host that merely begins with a configured\n // one — `https://api.example.com.attacker.test` — and hand it the operator's bearer.\n // The match must therefore end on a path, query or fragment boundary.\n const remainder = req.url.slice(serviceUri.length);\n if (serviceUri.endsWith('/') || remainder === '' || '/?#'.includes(remainder.charAt(0))) {\n return true;\n }\n }\n }\n return false;\n}\n\n/**\n * Checks if the request targets the OIDC token endpoint.\n *\n * The comparison runs on the path alone, because a guard keyed on the raw URL fails\n * silently rather than loudly: a query string, fragment or trailing slash would make the\n * refresh post to that endpoint through this same chain, and the single-flight promise\n * would wait on itself. It never settles, its `finally` never runs, and since every request\n * of that service shares it, each later 401 would await a promise that cannot resolve.\n */\nfunction isTokenEndpointUrl(url: string): boolean {\n const path = url.split('#')[0].split('?')[0].replace(/\\/+$/, '');\n return path.endsWith('/connect/token');\n}\n\n/**\n * The one denial code a refresh can repair. Adapter-owned and stable: the server deliberately\n * does not make a client key on the framework's English `error_description`, which a dependency\n * bump may reword.\n */\nconst REFRESHABLE_DENIAL_CODE = 'token_expired';\n\n/**\n * Answers whether refreshing the access token could possibly change this `401`.\n *\n * A server that follows RFC 6750 §3 names the reason it refused. Only an expired token gets a\n * different answer from a newer one — a wrong issuer, a wrong audience or an unknown signing key\n * describe something every token of this session shares, so refreshing spends one grant per user\n * action and lands in exactly the same place, indefinitely.\n *\n * A response that names no reason keeps the old behaviour and is refreshed, and so does a standard\n * challenge that carries no `error_code`. That is deliberate rather than a gap: every ASP.NET\n * service in the platform answers `Bearer error=\"invalid_token\"` with a framework-worded\n * description and no such parameter, and RFC 6750 defines no code that separates an expired token\n * from a rejected one — so treating a bare challenge as unrepairable would silently drop the\n * recovery for every sibling service. The adapter closes the ambiguous case from its own side by\n * naming `token_not_evaluated` when it holds a token it never evaluated.\n */\nfunction mayRefreshRepair(error: HttpErrorResponse): boolean {\n const challenge = error.headers?.get('WWW-Authenticate');\n if (!challenge) {\n return true;\n }\n\n // RFC 7235 allows an auth-param value as either a quoted-string or a bare token.\n const code = /error_code=(?:\"([^\"]*)\"|([^\\s,]+))/.exec(challenge);\n const value = code?.[1] ?? code?.[2];\n return value === undefined || value === REFRESHABLE_DENIAL_CODE;\n}\n\n// =============================================================================\n// SINGLE-FLIGHT TOKEN REFRESH\n// =============================================================================\n\n/**\n * Parallel 401s share one refresh instead of spending one grant each.\n *\n * Keyed by the service rather than held in a module variable: `AuthorizeService` is provided\n * per injector, so an app still gets exactly one in-flight refresh page-wide, while two\n * injectors in one process no longer await each other's promise and each test gets a clean\n * slate — a module variable is unreachable from a `beforeEach` unless it is exported.\n *\n * Do not restate the single-flight rationale as \"concurrent grants would end the session\":\n * that holds only for a client whose `RefreshTokenUsage` is `OneTimeOnly`, and ours are not —\n * Duende defaults the property to `ReUse` and nothing in octo-identity-services overrides it\n * (the CK attribute carries no default and reaches Duende through AutoMapper's by-name\n * convention, which is why grepping the C# for its name finds nothing).\n */\nconst refreshInFlight = new WeakMap<AuthorizeService, Promise<void>>();\n\nfunction refreshAccessTokenOnce(authorizeService: AuthorizeService): Promise<void> {\n let pending = refreshInFlight.get(authorizeService);\n if (!pending) {\n pending = Promise.resolve()\n .then(() => authorizeService.refreshAccessToken())\n .finally(() => {\n refreshInFlight.delete(authorizeService);\n });\n refreshInFlight.set(authorizeService, pending);\n }\n\n return pending;\n}\n\n// =============================================================================\n// FUNCTIONAL INTERCEPTOR (RECOMMENDED)\n// =============================================================================\n\n/**\n * Functional HTTP interceptor that adds Bearer token to authorized requests\n * and injects tenant context (acr_values) into token endpoint requests.\n *\n * Adds the Authorization header to requests that are either:\n * - Same-origin requests (relative URLs or same host)\n * - Requests to known service URIs configured in AuthorizeOptions\n *\n * For token endpoint POST requests (`/connect/token`), appends\n * `acr_values=tenant:{tenantId}` to the form body so the Identity Server\n * can resolve the correct tenant during refresh token exchanges.\n *\n * When a request that carried a token comes back `401`, the access token is refreshed\n * and the request retried once with the new bearer. If the token has already been replaced\n * by then — a slower request reporting its `401` after someone else's refresh finished —\n * the retry goes out with that token and no second refresh is started. A refresh that fails\n * or yields no new token rethrows the original `401`, so hosts can still classify it.\n *\n * @example\n * ```typescript\n * // app.config.ts\n * import { provideHttpClient, withInterceptors } from '@angular/common/http';\n * import { authorizeInterceptor } from '@meshmakers/shared-auth';\n *\n * export const appConfig: ApplicationConfig = {\n * providers: [\n * provideHttpClient(withInterceptors([authorizeInterceptor])),\n * provideMmSharedAuth(),\n * ]\n * };\n * ```\n */\nexport const authorizeInterceptor: HttpInterceptorFn = (req: HttpRequest<unknown>, next: HttpHandlerFn) => {\n const authorizeService = inject(AuthorizeService);\n const token = authorizeService.getAccessTokenSync();\n const serviceUris = authorizeService.getServiceUris();\n\n const tokenAttached = !!token && (isSameOriginUrl(req) || isKnownServiceUri(req, serviceUris));\n if (tokenAttached) {\n req = req.clone({\n setHeaders: {\n Authorization: `Bearer ${token}`\n }\n });\n }\n\n // Inject acr_values=tenant:{tenantId} into refresh_token grant requests only.\n // This ensures the Identity Server resolves the correct tenant during\n // refresh token exchanges, even after a service restart when its\n // in-memory token-to-tenant cache is lost.\n //\n // Authorization-code exchanges must NOT carry acr_values from local storage:\n // the tenant is already bound to the authorization code on the server side,\n // and injecting a stale storage tenant (e.g., left over from a previous\n // session) would force a refresh-token mismatch on the very next call,\n // causing an infinite reload loop.\n if (req.method === 'POST' && isTokenEndpointUrl(req.url) && req.body instanceof HttpParams) {\n if (req.body.get('grant_type') === 'refresh_token') {\n const tenantId = authorizeService.getStorageTenantId();\n if (tenantId) {\n req = req.clone({\n body: req.body.set('acr_values', `tenant:${tenantId}`)\n });\n }\n }\n }\n\n // Never let the token endpoint into the recovery path — see isTokenEndpointUrl for the\n // deadlock this avoids.\n if (!tokenAttached || isTokenEndpointUrl(req.url)) {\n return next(req);\n }\n\n const authorizedReq = req;\n\n // `next()` hands off to the rest of the chain, so a retried request does not re-enter this\n // interceptor — at most one retry by construction, without any bookkeeping.\n const retryWith = (bearer: string) =>\n next(authorizedReq.clone({ setHeaders: { Authorization: `Bearer ${bearer}` } }));\n\n return next(authorizedReq).pipe(\n catchError((error: unknown) => {\n if (!(error instanceof HttpErrorResponse) || error.status !== 401) {\n return throwError(() => error);\n }\n\n // Checked before the retry below as well: a token the server refuses for its issuer or\n // audience is refused again once refreshed, so neither path is worth an HTTP call.\n if (!mayRefreshRepair(error)) {\n console.warn('[AuthInterceptor] The server refused this token for a reason a refresh cannot repair:',\n error.headers?.get('WWW-Authenticate'));\n return throwError(() => error);\n }\n\n // Single-flight only covers the window while a refresh runs. A slower request that\n // went out with the same stale token reports its 401 after that window closed, and\n // its failure is already explained by the token that has since been replaced —\n // refreshing again would spend a second grant to learn the same thing.\n const currentToken = authorizeService.getAccessTokenSync();\n if (currentToken && currentToken !== token) {\n return retryWith(currentToken);\n }\n\n // Surface the original 401, not the refresh error: consuming apps classify the status\n // code to pick their message, and AuthorizeService already reacts to\n // `token_refresh_error` by clearing the session. The mapping is bound to the refresh\n // promise rather than placed in the pipe, because as an operator it only holds while it\n // sits above the switchMap below — and a 403 from the retried request must reach the\n // host as 403, not as the remembered 401.\n const refresh = refreshAccessTokenOnce(authorizeService).catch((refreshError: unknown) => {\n console.warn('[AuthInterceptor] Token refresh threw; surfacing the original 401.', refreshError);\n return Promise.reject(error);\n });\n\n return from(refresh).pipe(\n switchMap(() => {\n const refreshedToken = authorizeService.getAccessTokenSync();\n if (!refreshedToken || refreshedToken === token) {\n // Distinct from the throw above, and from each other: all three end in the same\n // 401 for the host, so the log is the only place they stay tellable apart.\n console.warn(refreshedToken\n ? '[AuthInterceptor] Token refresh returned the same access token; surfacing the original 401.'\n : '[AuthInterceptor] Token refresh returned no access token; surfacing the original 401.');\n return throwError(() => error);\n }\n\n return retryWith(refreshedToken);\n })\n );\n })\n );\n};\n","/**\n * The two ways a server refuses an authenticated call: the caller is not signed in any more,\n * or is signed in without the role the route requires.\n */\nexport type AuthorizationRefusal = 'unauthorized' | 'forbidden';\n\n/**\n * Classifies an HTTP failure as an authorization refusal, or `null` when it is anything else.\n *\n * Hosts phrase the outcome themselves - one app wants an i18n key, another a translated string,\n * and the public landing pages deliberately merge both cases because an anonymous visitor cannot\n * act on the difference. What they share is reading `status`, and six copies of that read is how\n * the six drift apart.\n *\n * Takes `unknown` because that is how the error arrives: through a rejected promise, a `catch`\n * block, or a rethrow that may have stripped the `HttpErrorResponse` prototype along the way.\n * The status is therefore read structurally and must be a `number` - a body that merely carries\n * `\"403\"` is not a refusal.\n *\n * @example\n * ```typescript\n * switch (authorizationRefusal(error)) {\n * case 'forbidden': return this.translate.instant('pipeline.forbidden');\n * case 'unauthorized': return this.translate.instant('pipeline.unauthorized');\n * default: return null;\n * }\n * ```\n */\nexport function authorizationRefusal(error: unknown): AuthorizationRefusal | null {\n const status = (error as { status?: unknown } | null)?.status;\n\n if (status === 401) {\n return 'unauthorized';\n }\n if (status === 403) {\n return 'forbidden';\n }\n return null;\n}\n","import { inject } from '@angular/core';\nimport { ActivatedRouteSnapshot, CanActivateFn, CanMatchFn, Router, RouterStateSnapshot } from '@angular/router';\nimport { AuthorizeService } from './authorize.service';\n\n/**\n * Walks up the route tree to find the tenantId parameter.\n */\nfunction findRouteTenantId(route: ActivatedRouteSnapshot): string | undefined {\n let current: ActivatedRouteSnapshot | null = route;\n while (current) {\n if (current.params?.['tenantId']) {\n return current.params['tenantId'];\n }\n current = current.parent;\n }\n return undefined;\n}\n\n/**\n * Handles authorization check for route activation.\n * Redirects to login if not authenticated, or to home if user lacks required roles.\n * Forces re-authentication when the token's tenant_id does not match the route's tenantId.\n *\n * @param route - The activated route snapshot containing route data\n * @returns true if authorized, false otherwise\n *\n * @example\n * ```typescript\n * // Route without role requirements\n * { path: 'dashboard', component: DashboardComponent, canActivate: [authorizeGuard] }\n *\n * // Route with role requirements\n * { path: 'admin', component: AdminComponent, canActivate: [authorizeGuard], data: { roles: ['AdminPanelManagement'] } }\n * ```\n */\nexport const authorizeGuard: CanActivateFn = async (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {\n const authorizeService = inject(AuthorizeService);\n const router = inject(Router);\n\n // Use signal directly (synchronous)\n const isAuthenticated = authorizeService.isAuthenticated();\n\n if (!isAuthenticated) {\n // Check if this is a reload after switchTenant — use the stored tenantId\n const pendingTenant = authorizeService.consumePendingTenantSwitch();\n const tenantId = pendingTenant ?? findRouteTenantId(route);\n authorizeService.login(tenantId);\n return false;\n }\n\n // Force re-authentication if the token was issued for a different tenant.\n // switchTenant clears the local session and reloads, so the next guard\n // invocation enters the !isAuthenticated branch above with the correct tenantId.\n const tokenTenantId = authorizeService.tokenTenantId();\n const routeTenantId = findRouteTenantId(route);\n if (tokenTenantId && routeTenantId && routeTenantId.toLowerCase() !== tokenTenantId.toLowerCase()) {\n console.debug(`[AuthGuard] Tenant mismatch: token=\"${tokenTenantId}\", route=\"${routeTenantId}\" — attempting tenant switch`);\n // switchTenant returns false if a switch was already attempted (loop prevention).\n // Use state.url (the router's target URL) — not window.location.href which is\n // still the current page during in-app navigation (e.g., tenant switcher dropdown).\n const targetUrl = window.location.origin + state.url;\n if (authorizeService.switchTenant(routeTenantId, targetUrl)) {\n return false;\n }\n // Switch was skipped — fall through to role-based checks\n console.warn(`[AuthGuard] Tenant mismatch persists after switch attempt (token=\"${tokenTenantId}\", route=\"${routeTenantId}\"). Proceeding with current token.`);\n } else {\n // No mismatch — clear any leftover switch-attempted flag\n authorizeService.consumeSwitchAttempted();\n }\n\n // Use roles signal directly (synchronous)\n const userRoles = authorizeService.roles();\n const requiredRoles = route.data['roles'] as string[] | undefined;\n\n if (requiredRoles === undefined || requiredRoles === null) {\n if (typeof ngDevMode === 'undefined' || ngDevMode) {\n console.warn(`[AuthGuard] Route \"${route.routeConfig?.path}\" has no required roles defined — access granted to any authenticated user.`);\n }\n return true;\n }\n\n // Empty roles array (roles: []) means intentionally open to any authenticated user\n if (requiredRoles.length === 0) {\n return true;\n }\n\n if (!requiredRoles.some(role => userRoles.includes(role))) {\n // Navigate to the current tenant's home, not root ''.\n // Navigating to '' would redirect to a default tenant (e.g., octosystem),\n // causing a tenant mismatch → switchTenant → redirect loop.\n const tenantHome = routeTenantId ? `/${routeTenantId}` : '';\n await router.navigate([tenantHome]);\n return false;\n }\n\n return true;\n};\n\n/**\n * Guard for child routes. Delegates to authorizeGuard.\n *\n * @example\n * ```typescript\n * {\n * path: 'parent',\n * canActivateChild: [authorizeChildGuard],\n * children: [\n * { path: 'child', component: ChildComponent, data: { roles: ['RequiredRole'] } }\n * ]\n * }\n * ```\n */\nexport const authorizeChildGuard: CanActivateFn = authorizeGuard;\n\n/**\n * Guard for lazy-loaded routes. Checks if user is authenticated.\n * Replaces the deprecated canLoad guard.\n *\n * @example\n * ```typescript\n * {\n * path: 'lazy',\n * loadChildren: () => import('./lazy/lazy.routes'),\n * canMatch: [authorizeMatchGuard]\n * }\n * ```\n */\nexport const authorizeMatchGuard: CanMatchFn = (_route, segments) => {\n const authorizeService = inject(AuthorizeService);\n\n // Use signal directly (synchronous)\n const isAuthenticated = authorizeService.isAuthenticated();\n\n if (!isAuthenticated) {\n // The first URL segment is typically the tenantId (e.g., /:tenantId/...)\n const tenantId = segments.length > 0 ? segments[0].path : undefined;\n authorizeService.login(tenantId);\n return false;\n }\n\n return true;\n};\n\n/**\n * Guard that always allows deactivation.\n * Use this as a placeholder or override in specific routes.\n *\n * @example\n * ```typescript\n * { path: 'form', component: FormComponent, canDeactivate: [authorizeDeactivateGuard] }\n * ```\n */\nexport const authorizeDeactivateGuard = () => true;\n","/*\n * Public API Surface of shared-auth\n */\n\nimport { EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';\nimport { AuthorizeService } from './lib/authorize.service';\nimport { provideOAuthClient } from 'angular-oauth2-oidc';\n\n// Core services\nexport * from './lib/authorize.service';\nexport * from './lib/roles';\nexport { TenantAwareOAuthStorage } from './lib/tenant-aware-oauth-storage';\n\n// Functional interceptor\nexport { authorizeInterceptor } from './lib/authorize.interceptor';\n\n// Error classification for hosts that phrase the refusal themselves\nexport { authorizationRefusal } from './lib/authorization-refusal';\nexport type { AuthorizationRefusal } from './lib/authorization-refusal';\n\n// Functional guards\nexport {\n authorizeGuard,\n authorizeChildGuard,\n authorizeMatchGuard,\n authorizeDeactivateGuard\n} from './lib/authorize.guard';\n\n// UI Components (Kendo) - available via '@meshmakers/shared-auth/login-ui'\n// import { LoginAppBarSectionComponent } from '@meshmakers/shared-auth/login-ui';\n\n/**\n * Provides all shared-auth dependencies.\n *\n * @example\n * ```typescript\n * // app.config.ts\n * import { provideHttpClient, withInterceptors } from '@angular/common/http';\n * import { provideMmSharedAuth, authorizeInterceptor } from '@meshmakers/shared-auth';\n *\n * export const appConfig: ApplicationConfig = {\n * providers: [\n * provideHttpClient(withInterceptors([authorizeInterceptor])),\n * provideMmSharedAuth(),\n * // ... other providers\n * ]\n * };\n * ```\n *\n * @remarks\n * Functional guards and interceptors don't need to be provided - they use inject() internally.\n * For the functional interceptor, use `provideHttpClient(withInterceptors([authorizeInterceptor]))`.\n */\nexport function provideMmSharedAuth(): EnvironmentProviders {\n return makeEnvironmentProviders([\n provideOAuthClient(),\n AuthorizeService\n ]);\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meshmakers/shared-auth",
3
- "version": "3.4.890",
3
+ "version": "3.4.900",
4
4
  "peerDependencies": {
5
5
  "@angular/common": "^22.0.0",
6
6
  "@angular/core": "^22.0.0",
@@ -367,6 +367,12 @@ declare class TenantAwareOAuthStorage extends OAuthStorage {
367
367
  * `acr_values=tenant:{tenantId}` to the form body so the Identity Server
368
368
  * can resolve the correct tenant during refresh token exchanges.
369
369
  *
370
+ * When a request that carried a token comes back `401`, the access token is refreshed
371
+ * and the request retried once with the new bearer. If the token has already been replaced
372
+ * by then — a slower request reporting its `401` after someone else's refresh finished —
373
+ * the retry goes out with that token and no second refresh is started. A refresh that fails
374
+ * or yields no new token rethrows the original `401`, so hosts can still classify it.
375
+ *
370
376
  * @example
371
377
  * ```typescript
372
378
  * // app.config.ts
@@ -383,6 +389,35 @@ declare class TenantAwareOAuthStorage extends OAuthStorage {
383
389
  */
384
390
  declare const authorizeInterceptor: HttpInterceptorFn;
385
391
 
392
+ /**
393
+ * The two ways a server refuses an authenticated call: the caller is not signed in any more,
394
+ * or is signed in without the role the route requires.
395
+ */
396
+ type AuthorizationRefusal = 'unauthorized' | 'forbidden';
397
+ /**
398
+ * Classifies an HTTP failure as an authorization refusal, or `null` when it is anything else.
399
+ *
400
+ * Hosts phrase the outcome themselves - one app wants an i18n key, another a translated string,
401
+ * and the public landing pages deliberately merge both cases because an anonymous visitor cannot
402
+ * act on the difference. What they share is reading `status`, and six copies of that read is how
403
+ * the six drift apart.
404
+ *
405
+ * Takes `unknown` because that is how the error arrives: through a rejected promise, a `catch`
406
+ * block, or a rethrow that may have stripped the `HttpErrorResponse` prototype along the way.
407
+ * The status is therefore read structurally and must be a `number` - a body that merely carries
408
+ * `"403"` is not a refusal.
409
+ *
410
+ * @example
411
+ * ```typescript
412
+ * switch (authorizationRefusal(error)) {
413
+ * case 'forbidden': return this.translate.instant('pipeline.forbidden');
414
+ * case 'unauthorized': return this.translate.instant('pipeline.unauthorized');
415
+ * default: return null;
416
+ * }
417
+ * ```
418
+ */
419
+ declare function authorizationRefusal(error: unknown): AuthorizationRefusal | null;
420
+
386
421
  /**
387
422
  * Handles authorization check for route activation.
388
423
  * Redirects to login if not authenticated, or to home if user lacks required roles.
@@ -465,5 +500,5 @@ declare const authorizeDeactivateGuard: () => boolean;
465
500
  */
466
501
  declare function provideMmSharedAuth(): EnvironmentProviders;
467
502
 
468
- export { AuthorizeOptions, AuthorizeService, Roles, TenantAwareOAuthStorage, authorizeChildGuard, authorizeDeactivateGuard, authorizeGuard, authorizeInterceptor, authorizeMatchGuard, provideMmSharedAuth };
469
- export type { DiscoveryDocumentRetryOptions, IUser };
503
+ export { AuthorizeOptions, AuthorizeService, Roles, TenantAwareOAuthStorage, authorizationRefusal, authorizeChildGuard, authorizeDeactivateGuard, authorizeGuard, authorizeInterceptor, authorizeMatchGuard, provideMmSharedAuth };
504
+ export type { AuthorizationRefusal, DiscoveryDocumentRetryOptions, IUser };