@securitydept/token-set-context-client-angular 0.3.0-beta.2 → 0.3.0-beta.3
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/fesm2022/securitydept-token-set-context-client-angular.mjs +146 -83
- package/fesm2022/securitydept-token-set-context-client-angular.mjs.map +1 -1
- package/package.json +4 -4
- package/types/securitydept-token-set-context-client-angular.d.ts +71 -18
- package/types/securitydept-token-set-context-client-angular.d.ts.map +1 -1
|
@@ -3,10 +3,11 @@ import * as i0 from '@angular/core';
|
|
|
3
3
|
import { signal, inject, DestroyRef, Injectable, InjectionToken, provideEnvironmentInitializer, EnvironmentInjector, runInInjectionContext, Component } from '@angular/core';
|
|
4
4
|
import { getTokenFreshness, TokenFreshnessState, attachTokenSetResumeReconciliation, TokenSetAuthFlowSource } from '@securitydept/token-set-context-client/orchestration';
|
|
5
5
|
import { from, switchMap, firstValueFrom, take } from 'rxjs';
|
|
6
|
-
import { createTokenSetAuthRegistry } from '@securitydept/token-set-context-client/registry';
|
|
6
|
+
import { createTokenSetAuthRegistry, TokenSetCallbackResumeController } from '@securitydept/token-set-context-client/registry';
|
|
7
7
|
export { ClientInitializationPriority } from '@securitydept/token-set-context-client/registry';
|
|
8
|
-
import { bridgeToAngularSignal, signalToObservable, AUTH_PLANNER_HOST, extractFullRouteRequirements, withRouteRequirements } from '@securitydept/client-angular';
|
|
8
|
+
import { bridgeToAngularSignal, signalToObservable, AUTH_PLANNER_HOST, extractFullRouteRequirements, resolvePageClientEnvironmentSource, PAGE_CLIENT_ENVIRONMENT, withRouteRequirements } from '@securitydept/client-angular';
|
|
9
9
|
import { Router } from '@angular/router';
|
|
10
|
+
import { assertResolveEnvironment } from '@securitydept/client/web';
|
|
10
11
|
|
|
11
12
|
// ============================================================================
|
|
12
13
|
// 5. TokenSetAuthService — per-client service wrapper
|
|
@@ -15,7 +16,7 @@ import { Router } from '@angular/router';
|
|
|
15
16
|
* Per-client Angular service that bridges a single OIDC mode client to
|
|
16
17
|
* Angular signals + RxJS.
|
|
17
18
|
*
|
|
18
|
-
*
|
|
19
|
+
* Lifecycle teardown is explicit. Construct with
|
|
19
20
|
* `new TokenSetAuthService(client, autoRestore)` and call `.dispose()` when
|
|
20
21
|
* the service owner tears down. The shared registry core handles this
|
|
21
22
|
* automatically — adopters that wire the service directly (rare) must call
|
|
@@ -158,7 +159,7 @@ class TokenSetAuthRegistry {
|
|
|
158
159
|
this.core.dispose();
|
|
159
160
|
}
|
|
160
161
|
// --------------------------------------------------------------------------
|
|
161
|
-
// Pass-through API
|
|
162
|
+
// Pass-through API for the earlier wrapper surface
|
|
162
163
|
// --------------------------------------------------------------------------
|
|
163
164
|
isReady(key) {
|
|
164
165
|
return this.core.isReady(key);
|
|
@@ -265,10 +266,10 @@ class TokenSetAuthRegistry {
|
|
|
265
266
|
async ensureAuthForResource(options = {}) {
|
|
266
267
|
return await this.core.ensureAuthForResource(options);
|
|
267
268
|
}
|
|
268
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
269
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
269
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: TokenSetAuthRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
270
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: TokenSetAuthRegistry });
|
|
270
271
|
}
|
|
271
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
272
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: TokenSetAuthRegistry, decorators: [{
|
|
272
273
|
type: Injectable
|
|
273
274
|
}], ctorParameters: () => [] });
|
|
274
275
|
|
|
@@ -319,10 +320,10 @@ class TokenSetBearerInterceptor {
|
|
|
319
320
|
return next.handle(req.clone({ setHeaders: { Authorization: authorization } }));
|
|
320
321
|
}));
|
|
321
322
|
}
|
|
322
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
323
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
323
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: TokenSetBearerInterceptor, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
324
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: TokenSetBearerInterceptor });
|
|
324
325
|
}
|
|
325
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
326
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: TokenSetBearerInterceptor, decorators: [{
|
|
326
327
|
type: Injectable
|
|
327
328
|
}] });
|
|
328
329
|
/**
|
|
@@ -511,6 +512,20 @@ async function resolveAuthorizationForRequest(registry, url, key, options) {
|
|
|
511
512
|
*/
|
|
512
513
|
class CallbackResumeService {
|
|
513
514
|
registry = inject(TokenSetAuthRegistry);
|
|
515
|
+
destroyRef = inject(DestroyRef, { optional: true });
|
|
516
|
+
controller = new TokenSetCallbackResumeController({
|
|
517
|
+
registry: this.registry.core,
|
|
518
|
+
getCallbackClient: (service) => service.client,
|
|
519
|
+
});
|
|
520
|
+
state = signal(this.controller.getState(), ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
|
|
521
|
+
state$ = signalToObservable(this.controller.state);
|
|
522
|
+
constructor() {
|
|
523
|
+
const cleanup = bridgeToAngularSignal(this.controller.state, this.state);
|
|
524
|
+
this.destroyRef?.onDestroy(() => {
|
|
525
|
+
cleanup();
|
|
526
|
+
this.controller.dispose();
|
|
527
|
+
});
|
|
528
|
+
}
|
|
514
529
|
/**
|
|
515
530
|
* Check whether a URL is an OIDC authorization callback for any registered
|
|
516
531
|
* client in this registry.
|
|
@@ -524,7 +539,15 @@ class CallbackResumeService {
|
|
|
524
539
|
* contains an `code` or `error` query parameter.
|
|
525
540
|
*/
|
|
526
541
|
isCallback(url) {
|
|
527
|
-
return this.
|
|
542
|
+
return this.controller.isCallback(url);
|
|
543
|
+
}
|
|
544
|
+
getState() {
|
|
545
|
+
return this.controller.getState();
|
|
546
|
+
}
|
|
547
|
+
resume(optionsOrCallbackUrl, explicitClientKey) {
|
|
548
|
+
return this.controller.resume(typeof optionsOrCallbackUrl === "string"
|
|
549
|
+
? { currentUrl: optionsOrCallbackUrl, clientKey: explicitClientKey }
|
|
550
|
+
: optionsOrCallbackUrl);
|
|
528
551
|
}
|
|
529
552
|
/**
|
|
530
553
|
* Handle the OIDC callback, auto-detecting which client the callback
|
|
@@ -539,31 +562,19 @@ class CallbackResumeService {
|
|
|
539
562
|
* auto-detecting from registered callback paths.
|
|
540
563
|
*/
|
|
541
564
|
async handleCallback(callbackUrl, explicitClientKey) {
|
|
542
|
-
|
|
543
|
-
const clientKey = explicitClientKey ?? this.registry.clientKeyForCallback(callbackUrl);
|
|
544
|
-
if (!clientKey) {
|
|
545
|
-
throw new Error(`[CallbackResumeService] Cannot determine which client this callback belongs to. ` +
|
|
546
|
-
`URL: ${callbackUrl}. Register callbackPath in the client entry.`);
|
|
547
|
-
}
|
|
548
|
-
// Use whenReady() — not require() — so that if the client's async
|
|
549
|
-
// clientFactory is still in-flight when the callback page first loads,
|
|
550
|
-
// we wait for it to materialize rather than throwing.
|
|
551
|
-
const service = await this.registry.whenReady(clientKey);
|
|
552
|
-
const result = await service.client.handleCallback(callbackUrl);
|
|
553
|
-
// postAuthRedirectUri is recovered from the core client's pending state,
|
|
554
|
-
// which was stored by authorizeUrl() when loginWithRedirect() was called.
|
|
565
|
+
const result = await this.resume(callbackUrl, explicitClientKey);
|
|
555
566
|
return {
|
|
556
|
-
clientKey,
|
|
567
|
+
clientKey: result.clientKey,
|
|
557
568
|
snapshot: result.snapshot,
|
|
558
569
|
resumeUrl: result.postAuthRedirectUri ?? "/",
|
|
559
570
|
};
|
|
560
571
|
}
|
|
561
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
562
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
572
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: CallbackResumeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
573
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: CallbackResumeService });
|
|
563
574
|
}
|
|
564
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
575
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: CallbackResumeService, decorators: [{
|
|
565
576
|
type: Injectable
|
|
566
|
-
}] });
|
|
577
|
+
}], ctorParameters: () => [] });
|
|
567
578
|
|
|
568
579
|
// ============================================================================
|
|
569
580
|
// 8. Callback helper — OIDC redirect callback with multi-client discrimination
|
|
@@ -585,6 +596,26 @@ function isOidcCallback(options) {
|
|
|
585
596
|
|
|
586
597
|
/** InjectionToken for the multi-client auth registry. */
|
|
587
598
|
const TOKEN_SET_AUTH_REGISTRY = new InjectionToken("TOKEN_SET_AUTH_REGISTRY");
|
|
599
|
+
/**
|
|
600
|
+
* InjectionToken for the current callback URL used by the drop-in callback component.
|
|
601
|
+
*
|
|
602
|
+
* Defaults to `window.location.href` in real page hosts and `undefined` in
|
|
603
|
+
* non-page or test hosts unless explicitly overridden.
|
|
604
|
+
*/
|
|
605
|
+
const TOKEN_SET_CALLBACK_CURRENT_URL = new InjectionToken("TOKEN_SET_CALLBACK_CURRENT_URL", {
|
|
606
|
+
providedIn: "root",
|
|
607
|
+
factory: () => () => {
|
|
608
|
+
const windowLike = globalThis.window;
|
|
609
|
+
return windowLike?.location?.href;
|
|
610
|
+
},
|
|
611
|
+
});
|
|
612
|
+
const TOKEN_SET_CALLBACK_COMPONENT_OPTIONS = new InjectionToken("TOKEN_SET_CALLBACK_COMPONENT_OPTIONS", {
|
|
613
|
+
providedIn: "root",
|
|
614
|
+
factory: () => ({
|
|
615
|
+
fallbackUrl: "/",
|
|
616
|
+
errorRedirectUrl: "/",
|
|
617
|
+
}),
|
|
618
|
+
});
|
|
588
619
|
|
|
589
620
|
/**
|
|
590
621
|
* Create Angular providers for the multi-client token-set auth integration.
|
|
@@ -711,16 +742,30 @@ function provideTokenSetAuth(options) {
|
|
|
711
742
|
* @example
|
|
712
743
|
* ```ts
|
|
713
744
|
* // routes.ts — declare requirements in route data
|
|
714
|
-
* import {
|
|
745
|
+
* import { createBrowserPageClientEnvironment } from "@securitydept/client/web";
|
|
715
746
|
* import {
|
|
716
|
-
*
|
|
747
|
+
* providePageClientEnvironment,
|
|
748
|
+
* withRouteRequirements,
|
|
749
|
+
* } from "@securitydept/client-angular";
|
|
750
|
+
* import {
|
|
751
|
+
* createTokenSetOidcLoginRedirectHandler,
|
|
717
752
|
* createTokenSetRouteAggregationGuard,
|
|
718
753
|
* } from "@securitydept/token-set-context-client-angular";
|
|
719
754
|
*
|
|
755
|
+
* export const appConfig = {
|
|
756
|
+
* providers: [
|
|
757
|
+
* providePageClientEnvironment({
|
|
758
|
+
* environment: createBrowserPageClientEnvironment(),
|
|
759
|
+
* }),
|
|
760
|
+
* ],
|
|
761
|
+
* };
|
|
762
|
+
*
|
|
720
763
|
* // Simple — kind-level handler
|
|
721
764
|
* const guard = createTokenSetRouteAggregationGuard({
|
|
722
765
|
* requirementHandlers: {
|
|
723
|
-
* frontend_oidc:
|
|
766
|
+
* frontend_oidc: createTokenSetOidcLoginRedirectHandler({
|
|
767
|
+
* clientKey: "confluence",
|
|
768
|
+
* }),
|
|
724
769
|
* },
|
|
725
770
|
* });
|
|
726
771
|
*
|
|
@@ -782,21 +827,23 @@ function createTokenSetRouteAggregationGuard(options = {}) {
|
|
|
782
827
|
const policy = options.requirementPolicies?.[req.id];
|
|
783
828
|
let clientKeys;
|
|
784
829
|
if (policy?.selector) {
|
|
785
|
-
|
|
786
|
-
if (policy.selector.clientKey !== undefined) {
|
|
830
|
+
if (policy.selector.clientKey) {
|
|
787
831
|
clientKeys = [policy.selector.clientKey];
|
|
788
832
|
}
|
|
789
|
-
else {
|
|
833
|
+
else if (policy.selector.query) {
|
|
790
834
|
clientKeys = registry.clientKeysForOptions(policy.selector.query);
|
|
791
835
|
}
|
|
836
|
+
else {
|
|
837
|
+
clientKeys = [];
|
|
838
|
+
}
|
|
792
839
|
}
|
|
793
840
|
else {
|
|
794
841
|
// Default: resolve by requirement kind.
|
|
795
842
|
clientKeys = registry.clientKeyListForRequirement(req.kind);
|
|
796
843
|
}
|
|
797
844
|
if (clientKeys.length === 0) {
|
|
798
|
-
//
|
|
799
|
-
// coexist in the same route chain without breaking the guard
|
|
845
|
+
// Unmapped requirement: leave it unresolved so other requirements can
|
|
846
|
+
// coexist in the same route chain without breaking the guard.
|
|
800
847
|
continue;
|
|
801
848
|
}
|
|
802
849
|
const entries = await Promise.all(clientKeys.map(async (key) => ({
|
|
@@ -816,25 +863,25 @@ function createTokenSetRouteAggregationGuard(options = {}) {
|
|
|
816
863
|
// ── Step 3: Build planner candidates ────────────────────────────────
|
|
817
864
|
// De-duplicate by requirementId (last declaration for the same id wins).
|
|
818
865
|
const seenIds = new Map();
|
|
819
|
-
for (const
|
|
820
|
-
seenIds.set(
|
|
866
|
+
for (const resolvedRequirement of resolvedRequirements) {
|
|
867
|
+
seenIds.set(resolvedRequirement.requirement.id, resolvedRequirement);
|
|
821
868
|
}
|
|
822
869
|
const deduped = [...seenIds.values()];
|
|
823
|
-
const candidates = deduped.map((
|
|
824
|
-
requirementId:
|
|
825
|
-
requirementKind:
|
|
826
|
-
label:
|
|
827
|
-
attributes:
|
|
828
|
-
checkAuthenticated: () =>
|
|
870
|
+
const candidates = deduped.map((resolvedRequirement) => ({
|
|
871
|
+
requirementId: resolvedRequirement.requirement.id,
|
|
872
|
+
requirementKind: resolvedRequirement.requirement.kind,
|
|
873
|
+
label: resolvedRequirement.requirement.label,
|
|
874
|
+
attributes: resolvedRequirement.requirement.attributes,
|
|
875
|
+
checkAuthenticated: () => resolvedRequirement.entries.every(({ service }) => service.isAuthenticated()),
|
|
829
876
|
onUnauthenticated: async () => {
|
|
830
|
-
const failing =
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
options.requirementHandlers?.[
|
|
877
|
+
const failing = resolvedRequirement.entries.filter(({ service }) => !service.isAuthenticated());
|
|
878
|
+
const handler = options.requirementPolicies?.[resolvedRequirement.requirement.id]
|
|
879
|
+
?.onUnauthenticated ??
|
|
880
|
+
options.requirementHandlers?.[resolvedRequirement.requirement.kind] ??
|
|
834
881
|
options.defaultOnUnauthenticated;
|
|
835
882
|
if (!handler)
|
|
836
883
|
return false;
|
|
837
|
-
const result = runUnauthenticatedHandlerInContext(injector, handler, failing,
|
|
884
|
+
const result = runUnauthenticatedHandlerInContext(injector, handler, failing, resolvedRequirement.requirement, handlerContext);
|
|
838
885
|
const resolved = await result;
|
|
839
886
|
if (typeof resolved === "string")
|
|
840
887
|
return resolved;
|
|
@@ -842,7 +889,7 @@ function createTokenSetRouteAggregationGuard(options = {}) {
|
|
|
842
889
|
return resolved;
|
|
843
890
|
return resolved.toString();
|
|
844
891
|
},
|
|
845
|
-
_resolved:
|
|
892
|
+
_resolved: resolvedRequirement,
|
|
846
893
|
}));
|
|
847
894
|
// ── Step 4: Single planner evaluation ───────────────────────────────
|
|
848
895
|
const decide = async () => {
|
|
@@ -901,28 +948,20 @@ function createTokenSetRouteAggregationGuard(options = {}) {
|
|
|
901
948
|
function runUnauthenticatedHandlerInContext(injector, handler, unauthenticated, requirement, context) {
|
|
902
949
|
return runInInjectionContext(injector, () => handler(unauthenticated, requirement, context));
|
|
903
950
|
}
|
|
904
|
-
|
|
905
|
-
* Build an Angular route-security handler that starts frontend-oidc login and
|
|
906
|
-
* records the attempted navigation URL as `postAuthRedirectUri`.
|
|
907
|
-
*
|
|
908
|
-
* Use this instead of reading `Router.url` inside a guard handler. During guard
|
|
909
|
-
* execution Angular has not committed the attempted navigation yet, so
|
|
910
|
-
* `Router.url` still points at the previously active page.
|
|
911
|
-
*
|
|
912
|
-
* After the browser redirect is started, the returned guard result intentionally
|
|
913
|
-
* never settles. That keeps Angular from finalizing the rejected in-app
|
|
914
|
-
* navigation while the page is already leaving for an external IdP.
|
|
915
|
-
*/
|
|
916
|
-
function createFrontendOidcLoginRedirectHandler(options) {
|
|
951
|
+
function createTokenSetOidcLoginRedirectHandler(options = {}) {
|
|
917
952
|
return async (unauthenticated, _requirement, context) => {
|
|
918
953
|
const clientKey = options?.clientKey ?? unauthenticated[0]?.clientKey;
|
|
919
954
|
if (!clientKey)
|
|
920
955
|
return false;
|
|
956
|
+
const environmentPromise = resolveOidcRouteEnvironment(options?.environment);
|
|
921
957
|
const registry = inject(TokenSetAuthRegistry);
|
|
922
958
|
const service = await registry.whenReady(clientKey);
|
|
923
|
-
if (!isLoginWithRedirectClient(service.client))
|
|
924
|
-
|
|
959
|
+
if (!isLoginWithRedirectClient(service.client)) {
|
|
960
|
+
failMissingOidcRedirectLoginCapability(clientKey);
|
|
961
|
+
}
|
|
962
|
+
const environment = await environmentPromise;
|
|
925
963
|
await service.client.loginWithRedirect({
|
|
964
|
+
environment,
|
|
926
965
|
postAuthRedirectUri: context.attemptedUrl || options?.fallbackPostAuthRedirectUri || "/",
|
|
927
966
|
});
|
|
928
967
|
return await neverSettlingRedirectGuardResult();
|
|
@@ -933,6 +972,18 @@ function neverSettlingRedirectGuardResult() {
|
|
|
933
972
|
// Intentionally never resolves: a full-page browser redirect is in progress.
|
|
934
973
|
});
|
|
935
974
|
}
|
|
975
|
+
function resolveOidcRouteEnvironment(environmentOverride) {
|
|
976
|
+
return resolvePageClientEnvironmentSource(assertResolveEnvironment(environmentOverride ??
|
|
977
|
+
inject(PAGE_CLIENT_ENVIRONMENT, { optional: true }), failMissingOidcRouteEnvironment), failMissingOidcRouteEnvironment);
|
|
978
|
+
}
|
|
979
|
+
function failMissingOidcRouteEnvironment() {
|
|
980
|
+
throw new Error("createTokenSetOidcLoginRedirectHandler requires an explicit environment.\n" +
|
|
981
|
+
"Provide it once from the Angular composition root with providePageClientEnvironment({ environment }), where environment is a provider-scoped ClientEnvironmentService or another stable resolver.\n" +
|
|
982
|
+
"or pass a stable environment override with createTokenSetOidcLoginRedirectHandler({ environment: ... }).");
|
|
983
|
+
}
|
|
984
|
+
function failMissingOidcRedirectLoginCapability(clientKey) {
|
|
985
|
+
throw new Error(`createTokenSetOidcLoginRedirectHandler requires client key "${clientKey}" to implement OidcRedirectLoginClient.loginWithRedirect(options).`);
|
|
986
|
+
}
|
|
936
987
|
function isLoginWithRedirectClient(client) {
|
|
937
988
|
return (typeof client === "object" &&
|
|
938
989
|
client !== null &&
|
|
@@ -1039,9 +1090,10 @@ function findNearestPlannerHostKey(route) {
|
|
|
1039
1090
|
* Drop-in standalone Angular component for OIDC redirect callback routes.
|
|
1040
1091
|
*
|
|
1041
1092
|
* Handles the full callback lifecycle on initialization:
|
|
1042
|
-
* 1. Calls `CallbackResumeService.
|
|
1043
|
-
* 2. Navigates to
|
|
1044
|
-
* 3. On error:
|
|
1093
|
+
* 1. Calls `CallbackResumeService.resume()` with the current URL.
|
|
1094
|
+
* 2. Navigates to the post-auth redirect set at login time.
|
|
1095
|
+
* 3. On error: calls the optional `onError` hook and navigates to the
|
|
1096
|
+
* configured error target (default `"/"`).
|
|
1045
1097
|
*
|
|
1046
1098
|
* Register as a route component — no additional glue needed:
|
|
1047
1099
|
* ```ts
|
|
@@ -1052,33 +1104,44 @@ function findNearestPlannerHostKey(route) {
|
|
|
1052
1104
|
* ];
|
|
1053
1105
|
* ```
|
|
1054
1106
|
*
|
|
1055
|
-
*
|
|
1056
|
-
*
|
|
1107
|
+
* Host policy can override `fallbackUrl`, `errorRedirectUrl`, and `onError`
|
|
1108
|
+
* through `TOKEN_SET_CALLBACK_COMPONENT_OPTIONS`. For fully custom UI or
|
|
1109
|
+
* orchestration, build your own component around `CallbackResumeService`.
|
|
1057
1110
|
*/
|
|
1058
1111
|
class TokenSetCallbackComponent {
|
|
1059
1112
|
callbackService = inject(CallbackResumeService);
|
|
1060
1113
|
router = inject(Router);
|
|
1114
|
+
getCurrentUrl = inject(TOKEN_SET_CALLBACK_CURRENT_URL);
|
|
1115
|
+
options = inject(TOKEN_SET_CALLBACK_COMPONENT_OPTIONS, {
|
|
1116
|
+
optional: true,
|
|
1117
|
+
}) ?? {
|
|
1118
|
+
fallbackUrl: "/",
|
|
1119
|
+
errorRedirectUrl: "/",
|
|
1120
|
+
};
|
|
1061
1121
|
ngOnInit() {
|
|
1062
|
-
const
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1122
|
+
const fallbackUrl = this.options.fallbackUrl ?? "/";
|
|
1123
|
+
const errorRedirectUrl = this.options.errorRedirectUrl ?? fallbackUrl;
|
|
1124
|
+
const currentUrl = this.getCurrentUrl();
|
|
1125
|
+
if (!currentUrl || !this.callbackService.isCallback(currentUrl)) {
|
|
1126
|
+
this.router.navigateByUrl(fallbackUrl, { replaceUrl: true });
|
|
1066
1127
|
return;
|
|
1067
1128
|
}
|
|
1068
1129
|
this.callbackService
|
|
1069
|
-
.
|
|
1130
|
+
.resume(currentUrl)
|
|
1070
1131
|
.then((result) => {
|
|
1071
|
-
this.router.navigateByUrl(result.
|
|
1132
|
+
this.router.navigateByUrl(result.postAuthRedirectUri ?? "/", {
|
|
1133
|
+
replaceUrl: true,
|
|
1134
|
+
});
|
|
1072
1135
|
})
|
|
1073
1136
|
.catch((error) => {
|
|
1074
|
-
|
|
1075
|
-
this.router.navigateByUrl(
|
|
1137
|
+
this.options.onError?.(error);
|
|
1138
|
+
this.router.navigateByUrl(errorRedirectUrl, { replaceUrl: true });
|
|
1076
1139
|
});
|
|
1077
1140
|
}
|
|
1078
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1079
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.
|
|
1141
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: TokenSetCallbackComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1142
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.12", type: TokenSetCallbackComponent, isStandalone: true, selector: "token-set-callback", ngImport: i0, template: "", isInline: true });
|
|
1080
1143
|
}
|
|
1081
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1144
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: TokenSetCallbackComponent, decorators: [{
|
|
1082
1145
|
type: Component,
|
|
1083
1146
|
args: [{
|
|
1084
1147
|
selector: "token-set-callback",
|
|
@@ -1123,5 +1186,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
1123
1186
|
* Generated bundle index. Do not edit.
|
|
1124
1187
|
*/
|
|
1125
1188
|
|
|
1126
|
-
export { CallbackResumeService, TOKEN_SET_AUTH_REGISTRY, TOKEN_SET_BEARER_INTERCEPTOR_OPTIONS, TOKEN_SET_ROUTE_PLANNER_HOST_KEY_DATA_KEY, TokenSetAuthRegistry, TokenSetAuthService, TokenSetBearerInterceptor, TokenSetCallbackComponent,
|
|
1189
|
+
export { CallbackResumeService, TOKEN_SET_AUTH_REGISTRY, TOKEN_SET_BEARER_INTERCEPTOR_OPTIONS, TOKEN_SET_CALLBACK_COMPONENT_OPTIONS, TOKEN_SET_CALLBACK_CURRENT_URL, TOKEN_SET_ROUTE_PLANNER_HOST_KEY_DATA_KEY, TokenSetAuthRegistry, TokenSetAuthService, TokenSetBearerInterceptor, TokenSetCallbackComponent, createTokenSetBearerInterceptor, createTokenSetOidcLoginRedirectHandler, createTokenSetRouteAggregationGuard, isOidcCallback, provideTokenSetAuth, provideTokenSetBearerInterceptor, secureRoute, secureRouteRoot, secureTokenSetRoute, secureTokenSetRouteRoot };
|
|
1127
1190
|
//# sourceMappingURL=securitydept-token-set-context-client-angular.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"securitydept-token-set-context-client-angular.mjs","sources":["../../src/lib/token-set-auth.service.ts","../../src/lib/token-set-auth-registry.ts","../../src/lib/bearer-interceptor.ts","../../src/lib/callback-resume.service.ts","../../src/lib/oidc-callback-url.ts","../../src/lib/tokens.ts","../../src/lib/provide-token-set-auth.ts","../../src/lib/route-aggregation-guard.ts","../../src/lib/secure-routes.ts","../../src/lib/token-set-callback.component.ts","../../src/lib/index.ts","../../src/public-api.ts","../../src/securitydept-token-set-context-client-angular.ts"],"sourcesContent":["import { signal, type WritableSignal } from \"@angular/core\";\nimport type { EventStreamTrait } from \"@securitydept/client\";\nimport {\n\tbridgeToAngularSignal,\n\tsignalToObservable,\n} from \"@securitydept/client-angular\";\nimport {\n\ttype AuthSnapshot,\n\ttype EnsureAuthForResourceOptions,\n\ttype EnsureAuthForResourceResult,\n\ttype EnsureAuthorizationHeaderOptions,\n\ttype EnsureFreshAuthStateOptions,\n\tgetTokenFreshness,\n\tTokenFreshnessState,\n\ttype TokenSetAuthEvent,\n} from \"@securitydept/token-set-context-client/orchestration\";\nimport type { Observable } from \"rxjs\";\nimport type { TokenSetAngularClient } from \"./contracts\";\n\n// ============================================================================\n// 5. TokenSetAuthService — per-client service wrapper\n// ============================================================================\n\n/**\n * Per-client Angular service that bridges a single OIDC mode client to\n * Angular signals + RxJS.\n *\n * Iteration 110 change: lifecycle teardown is now explicit. Construct with\n * `new TokenSetAuthService(client, autoRestore)` and call `.dispose()` when\n * the service owner tears down. The shared registry core handles this\n * automatically — adopters that wire the service directly (rare) must call\n * `dispose()` themselves, typically bound to a `DestroyRef.onDestroy`.\n */\nexport class TokenSetAuthService {\n\t/** Current auth snapshot as an Angular signal. */\n\treadonly authState: WritableSignal<AuthSnapshot | null>;\n\t/** Current auth snapshot as an RxJS Observable. */\n\treadonly authState$: Observable<AuthSnapshot | null>;\n\t/** Promise that resolves when initial state restore completes (or null if skipped). */\n\treadonly restorePromise: Promise<AuthSnapshot | null> | null;\n\treadonly authEvents: EventStreamTrait<TokenSetAuthEvent>;\n\n\tprivate readonly cleanup: () => void;\n\tprivate disposed = false;\n\n\tconstructor(\n\t\t/** The underlying SDK client instance. */\n\t\treadonly client: TokenSetAngularClient,\n\t\tautoRestore: boolean,\n\t) {\n\t\tthis.authState = signal<AuthSnapshot | null>(null);\n\t\tthis.cleanup = bridgeToAngularSignal(client.state, this.authState);\n\t\tthis.authState$ = signalToObservable(client.state);\n\t\tthis.authEvents = client.authEvents;\n\t\tthis.restorePromise = autoRestore ? client.restorePersistedState() : null;\n\t}\n\n\t/** Whether the user is currently authenticated. */\n\tisAuthenticated(): boolean {\n\t\treturn this.accessToken() !== null;\n\t}\n\n\t/** Current access token, or null. */\n\taccessToken(): string | null {\n\t\tconst snapshot = this.authState();\n\t\tif (!isFreshOrUsable(snapshot)) {\n\t\t\treturn null;\n\t\t}\n\t\treturn snapshot?.tokens.accessToken ?? null;\n\t}\n\n\tasync ensureFreshAuthState(\n\t\toptions?: EnsureFreshAuthStateOptions,\n\t): Promise<AuthSnapshot | null> {\n\t\treturn await this.client.ensureFreshAuthState(options);\n\t}\n\n\tasync ensureAuthForResource(\n\t\toptions?: EnsureAuthForResourceOptions,\n\t): Promise<EnsureAuthForResourceResult> {\n\t\treturn await this.client.ensureAuthForResource(options);\n\t}\n\n\tasync ensureAccessToken(\n\t\toptions?: EnsureFreshAuthStateOptions,\n\t): Promise<string | null> {\n\t\treturn (\n\t\t\t(await this.ensureFreshAuthState(options))?.tokens.accessToken ?? null\n\t\t);\n\t}\n\n\tasync ensureAuthorizationHeader(\n\t\toptions?: EnsureAuthorizationHeaderOptions,\n\t): Promise<string | null> {\n\t\treturn await this.client.ensureAuthorizationHeader(options);\n\t}\n\n\t/**\n\t * Release all resources owned by this service.\n\t *\n\t * - Unsubscribes the SDK state bridge\n\t * - Calls `client.dispose()`\n\t *\n\t * Idempotent: subsequent calls are no-ops. Called by the shared registry\n\t * at teardown; adopters invoking TokenSetAuthService directly must call\n\t * this themselves (typically bound to `DestroyRef.onDestroy`).\n\t */\n\tdispose(): void {\n\t\tif (this.disposed) return;\n\t\tthis.disposed = true;\n\t\ttry {\n\t\t\tthis.cleanup();\n\t\t} catch {\n\t\t\t// Swallow — best effort.\n\t\t}\n\t\ttry {\n\t\t\tthis.client.dispose();\n\t\t} catch {\n\t\t\t// Swallow — best effort.\n\t\t}\n\t}\n}\n\nfunction isFreshOrUsable(snapshot: AuthSnapshot | null): boolean {\n\tconst freshness = getTokenFreshness(snapshot, {\n\t\tnow: Date.now(),\n\t\tclockSkewMs: 30_000,\n\t\trefreshWindowMs: 0,\n\t});\n\treturn (\n\t\tfreshness === TokenFreshnessState.Fresh ||\n\t\tfreshness === TokenFreshnessState.RefreshDue ||\n\t\tfreshness === TokenFreshnessState.NoExpiry\n\t);\n}\n","import { DestroyRef, Injectable, inject } from \"@angular/core\";\nimport type { EventStreamTrait } from \"@securitydept/client\";\nimport type { ClientReadinessState } from \"@securitydept/token-set-context-client/frontend-oidc-mode\";\nimport type {\n\tEnsureAuthForResourceResult,\n\tTokenSetAuthEvent,\n} from \"@securitydept/token-set-context-client/orchestration\";\nimport { attachTokenSetResumeReconciliation } from \"@securitydept/token-set-context-client/orchestration\";\nimport {\n\ttype ClientFilter,\n\ttype ClientKeySelector,\n\ttype ClientMeta,\n\ttype ClientQueryOptions,\n\ttype TokenSetAuthRegistry as CoreTokenSetAuthRegistry,\n\ttype TokenSetClientEntry as CoreTokenSetClientEntry,\n\tcreateTokenSetAuthRegistry,\n\ttype EnsureRegistryAuthForResourceOptions,\n} from \"@securitydept/token-set-context-client/registry\";\nimport type { TokenSetAngularClient, TokenSetClientEntry } from \"./contracts\";\nimport { TokenSetAuthService } from \"./token-set-auth.service\";\n\n// Re-export core registry types so existing adopter imports from\n// @securitydept/token-set-context-client-angular keep working.\nexport type {\n\tClientFilter,\n\tClientKeySelector,\n\tClientMeta,\n\tClientQueryOptions,\n\tEnsureRegistryAuthForResourceOptions,\n} from \"@securitydept/token-set-context-client/registry\";\nexport { ClientInitializationPriority } from \"@securitydept/token-set-context-client/registry\";\n\n// ============================================================================\n// TokenSetAuthRegistry — thin Angular DI wrapper around the framework-neutral\n// core at @securitydept/token-set-context-client/registry\n//\n// Iteration 110: the Angular registry used to own all multi-client state\n// directly. It now delegates to a `createTokenSetAuthRegistry()` core and\n// supplies Angular-idiomatic glue:\n// - Injectable scope (root or provider-level)\n// - DestroyRef-bound dispose (envelopes `coreRegistry.dispose()`)\n// - Materialize callback wraps raw clients in `TokenSetAuthService`\n// - access-token sugar routed through the service's signal\n// ============================================================================\n\ntype AngularClient = TokenSetAngularClient;\n\n@Injectable()\nexport class TokenSetAuthRegistry {\n\t/**\n\t * Shared framework-neutral registry core. Angular consumers rarely need\n\t * direct access, but this is exposed so adapter-internal helpers\n\t * (`CallbackResumeService`, `bearer-interceptor`) can query the core\n\t * directly when they don't need Angular-specific behaviour.\n\t */\n\treadonly core: CoreTokenSetAuthRegistry<AngularClient, TokenSetAuthService>;\n\treadonly authEvents: EventStreamTrait<TokenSetAuthEvent>;\n\n\tconstructor() {\n\t\tthis.core = createTokenSetAuthRegistry<AngularClient, TokenSetAuthService>({\n\t\t\tmaterialize: (client, entry) => {\n\t\t\t\tconst angularEntry = entry as TokenSetClientEntry;\n\t\t\t\tconst managedClient = attachTokenSetResumeReconciliation(client, {\n\t\t\t\t\tresumeReconciliation: angularEntry.resumeReconciliation,\n\t\t\t\t\tresumeReconciliationOptions: angularEntry.resumeReconciliationOptions,\n\t\t\t\t});\n\t\t\t\treturn new TokenSetAuthService(\n\t\t\t\t\tmanagedClient,\n\t\t\t\t\tentry.autoRestore ?? true,\n\t\t\t\t);\n\t\t\t},\n\t\t\tdispose: (service) => service.dispose(),\n\t\t\taccessTokenOf: (service) => service.accessToken(),\n\t\t\tensureAccessTokenOf: (service) => service.ensureAccessToken(),\n\t\t\tensureAuthorizationHeaderOf: (service) =>\n\t\t\t\tservice.ensureAuthorizationHeader(),\n\t\t\tensureAuthForResourceOf: (service, options) =>\n\t\t\t\tservice.ensureAuthForResource(options),\n\t\t\tauthEventsOf: (service) => service.authEvents,\n\t\t});\n\t\tthis.authEvents = this.core.authEvents;\n\t\t// Try to bind core.dispose() to the current injection context's\n\t\t// DestroyRef. Registry is typically constructed via DI (where this\n\t\t// always succeeds); pure unit tests instantiate directly and must\n\t\t// call `.dispose()` manually.\n\t\ttry {\n\t\t\tconst destroyRef = inject(DestroyRef);\n\t\t\tdestroyRef.onDestroy(() => this.core.dispose());\n\t\t} catch {\n\t\t\t// Not in an injection context — adopter is expected to call\n\t\t\t// registry.dispose() manually (or never, for short-lived tests).\n\t\t}\n\t}\n\n\t/**\n\t * Register a client entry. Supports sync / async / lazy priority.\n\t *\n\t * Iteration 110 change: the second `destroyRef` argument is no longer\n\t * required — the registry pulls its own `DestroyRef` via injection and\n\t * binds teardown once per Angular scope.\n\t */\n\tregister(\n\t\tentry: TokenSetClientEntry & {\n\t\t\tpriority?: \"primary\" | \"lazy\";\n\t\t\tclientFactory: () => AngularClient;\n\t\t},\n\t): TokenSetAuthService;\n\tregister(\n\t\tentry: TokenSetClientEntry & {\n\t\t\tpriority?: \"primary\" | \"lazy\";\n\t\t\tclientFactory: () => Promise<AngularClient>;\n\t\t},\n\t): Promise<TokenSetAuthService>;\n\tregister(\n\t\tentry: TokenSetClientEntry,\n\t): TokenSetAuthService | Promise<TokenSetAuthService> | undefined;\n\tregister(\n\t\tentry: TokenSetClientEntry,\n\t): TokenSetAuthService | Promise<TokenSetAuthService> | undefined {\n\t\treturn this.core.register(entry as CoreTokenSetClientEntry<AngularClient>);\n\t}\n\n\t/**\n\t * Manually dispose the registry. Normally bound to the ambient\n\t * `DestroyRef` at construction; call this explicitly when instantiating\n\t * the registry outside of an Angular injection context (e.g. in unit\n\t * tests).\n\t */\n\tdispose(): void {\n\t\tthis.core.dispose();\n\t}\n\n\t// --------------------------------------------------------------------------\n\t// Pass-through API (backwards-compatible with iteration 109 surface)\n\t// --------------------------------------------------------------------------\n\n\tisReady(key: string): boolean {\n\t\treturn this.core.isReady(key);\n\t}\n\n\treadinessState(key: string): ClientReadinessState {\n\t\treturn this.core.readinessState(key);\n\t}\n\n\tasync whenReady(key: string): Promise<TokenSetAuthService> {\n\t\treturn this.core.whenReady(key);\n\t}\n\n\t/**\n\t * Preload a lazy client without throwing on rejection. Callers are\n\t * expected to attach `.catch` handlers for fire-and-forget usage.\n\t */\n\tpreload(key: string): Promise<TokenSetAuthService> {\n\t\treturn this.core.preload(key);\n\t}\n\n\t/**\n\t * Schedule preload for every lazy+not-initialized client using\n\t * `requestIdleCallback` / `setTimeout` fallback.\n\t */\n\tidleWarmup(): () => void {\n\t\treturn this.core.idleWarmup();\n\t}\n\n\treset(key: string): void {\n\t\tthis.core.reset(key);\n\t}\n\n\tmetaFor(clientKey: string): ClientMeta | undefined {\n\t\treturn this.core.metaFor(clientKey);\n\t}\n\n\tget(key: string): TokenSetAuthService | undefined {\n\t\treturn this.core.get(key);\n\t}\n\n\trequire(key: string): TokenSetAuthService {\n\t\treturn this.core.require(key);\n\t}\n\n\tkeys(): string[] {\n\t\treturn this.core.keys();\n\t}\n\n\tentries(): Array<[string, TokenSetAuthService]> {\n\t\treturn this.core.entriesSnapshot();\n\t}\n\n\t// ---- URL / callback / requirement / provider-family discrimination -----\n\n\t*clientKeyGenForUrl(url: string): Generator<string, void, unknown> {\n\t\tyield* this.core.clientKeyGenForUrl(url);\n\t}\n\n\t*clientKeyGenForCallback(url: string): Generator<string, void, unknown> {\n\t\tyield* this.core.clientKeyGenForCallback(url);\n\t}\n\n\t*clientKeyGenForRequirement(\n\t\trequirementKind: string,\n\t): Generator<string, void, unknown> {\n\t\tyield* this.core.clientKeyGenForRequirement(requirementKind);\n\t}\n\n\t*clientKeyGenForProviderFamily(\n\t\tproviderFamily: string,\n\t): Generator<string, void, unknown> {\n\t\tyield* this.core.clientKeyGenForProviderFamily(providerFamily);\n\t}\n\n\tclientKeyListForUrl(url: string): string[] {\n\t\treturn this.core.clientKeyListForUrl(url);\n\t}\n\n\tclientKeyListForCallback(url: string): string[] {\n\t\treturn this.core.clientKeyListForCallback(url);\n\t}\n\n\tclientKeyListForRequirement(requirementKind: string): string[] {\n\t\treturn this.core.clientKeyListForRequirement(requirementKind);\n\t}\n\n\tclientKeyListForProviderFamily(providerFamily: string): string[] {\n\t\treturn this.core.clientKeyListForProviderFamily(providerFamily);\n\t}\n\n\tclientKeyForUrl(\n\t\turl: string,\n\t\tselector?: ClientKeySelector,\n\t): string | undefined {\n\t\treturn this.core.clientKeyForUrl(url, selector);\n\t}\n\n\tclientKeyForCallback(\n\t\turl: string,\n\t\tselector?: ClientKeySelector,\n\t): string | undefined {\n\t\treturn this.core.clientKeyForCallback(url, selector);\n\t}\n\n\tclientKeyForRequirement(\n\t\trequirementKind: string,\n\t\tselector?: ClientKeySelector,\n\t): string | undefined {\n\t\treturn this.core.clientKeyForRequirement(requirementKind, selector);\n\t}\n\n\tclientKeyForProviderFamily(\n\t\tproviderFamily: string,\n\t\tselector?: ClientKeySelector,\n\t): string | undefined {\n\t\treturn this.core.clientKeyForProviderFamily(providerFamily, selector);\n\t}\n\n\trequireForRequirement(\n\t\trequirementKind: string,\n\t\tselector?: ClientKeySelector,\n\t): TokenSetAuthService {\n\t\treturn this.core.requireForRequirement(requirementKind, selector);\n\t}\n\n\trequireForProviderFamily(\n\t\tproviderFamily: string,\n\t\tselector?: ClientKeySelector,\n\t): TokenSetAuthService {\n\t\treturn this.core.requireForProviderFamily(providerFamily, selector);\n\t}\n\n\t*clientKeyGenForFilter(\n\t\tfilter: ClientFilter,\n\t): Generator<string, void, unknown> {\n\t\tyield* this.core.clientKeyGenForFilter(filter);\n\t}\n\n\t*clientKeyGenForOptions(\n\t\toptions: ClientQueryOptions,\n\t): Generator<string, void, unknown> {\n\t\tyield* this.core.clientKeyGenForOptions(options);\n\t}\n\n\tclientKeysForOptions(options: ClientQueryOptions): string[] {\n\t\treturn this.core.clientKeysForOptions(options);\n\t}\n\n\taccessToken(key?: string): string | null {\n\t\treturn this.core.accessToken(key);\n\t}\n\n\tasync ensureAccessToken(key?: string): Promise<string | null> {\n\t\treturn await this.core.ensureAccessToken(key);\n\t}\n\n\tasync ensureAuthorizationHeader(key?: string): Promise<string | null> {\n\t\treturn await this.core.ensureAuthorizationHeader(key);\n\t}\n\n\tasync ensureAuthForResource(\n\t\toptions: EnsureRegistryAuthForResourceOptions = {},\n\t): Promise<EnsureAuthForResourceResult | null> {\n\t\treturn await this.core.ensureAuthForResource(options);\n\t}\n}\n","import {\n\tHTTP_INTERCEPTORS,\n\ttype HttpEvent,\n\ttype HttpHandler,\n\ttype HttpInterceptor,\n\ttype HttpRequest,\n} from \"@angular/common/http\";\nimport {\n\tInjectable,\n\tInjectionToken,\n\tinject,\n\ttype Provider,\n} from \"@angular/core\";\nimport { TokenSetAuthFlowSource } from \"@securitydept/token-set-context-client/orchestration\";\nimport { from, type Observable, switchMap } from \"rxjs\";\nimport { TokenSetAuthRegistry } from \"./token-set-auth-registry\";\n\n// ============================================================================\n// 7. Class-based HTTP interceptor + provider factory\n// ============================================================================\n\n/**\n * Adopter-tunable behaviour for the token-set bearer interceptor.\n *\n * Iteration 150 (review 1) lifted `strictUrlMatch` out of the implicit\n * single-client convenience path into an explicit option, so multi-backend\n * adopters can guarantee that bearer tokens never leak outside the URL\n * patterns they registered with `provideTokenSetAuth({ clients: [...] })`.\n */\nexport interface BearerInterceptorOptions {\n\t/**\n\t * When `true`, the interceptor injects a bearer header ONLY for requests\n\t * whose URL matches a registered client's `urlPatterns`. Requests with no\n\t * matching client receive no `Authorization` header even when a token is\n\t * available elsewhere in the registry.\n\t *\n\t * When `false` (default, single-client convenience), the interceptor\n\t * falls back to `registry.accessToken()` for unmatched URLs — useful only\n\t * when the host issues all HTTP traffic to one already-known backend.\n\t *\n\t * Adopters with more than one backend, more than one OIDC audience, or\n\t * any third-party HTTP traffic from the same Angular host MUST set this\n\t * to `true` to prevent cross-origin token leakage.\n\t *\n\t * @default false\n\t */\n\tstrictUrlMatch?: boolean;\n}\n\n/**\n * DI token for {@link BearerInterceptorOptions}. Adopters normally configure\n * options via `provideTokenSetBearerInterceptor({ ... })`; the token is\n * exported for advanced cases where the options need to be supplied or\n * overridden separately (for example, in tests).\n */\nexport const TOKEN_SET_BEARER_INTERCEPTOR_OPTIONS =\n\tnew InjectionToken<BearerInterceptorOptions>(\n\t\t\"TOKEN_SET_BEARER_INTERCEPTOR_OPTIONS\",\n\t);\n\n/**\n * Angular class-based bearer-token interceptor (`HTTP_INTERCEPTORS` style).\n *\n * Selects the correct access token from the multi-client registry by URL\n * pattern. Behaviour for unmatched URLs is governed by\n * {@link BearerInterceptorOptions.strictUrlMatch}.\n *\n * Prefer the functional version via `createTokenSetBearerInterceptor()` +\n * `withInterceptors()` when using `provideHttpClient`. Use this class-based\n * version when you need classic `HTTP_INTERCEPTORS` multi-provider style\n * (e.g. NgModule apps).\n *\n * @example\n * ```ts\n * // NgModule providers:\n * provideTokenSetBearerInterceptor({ strictUrlMatch: true })\n * ```\n */\n@Injectable()\nexport class TokenSetBearerInterceptor implements HttpInterceptor {\n\tprivate readonly registry = inject(TokenSetAuthRegistry);\n\tprivate readonly options: BearerInterceptorOptions =\n\t\tinject(TOKEN_SET_BEARER_INTERCEPTOR_OPTIONS, { optional: true }) ?? {};\n\n\tintercept(\n\t\treq: HttpRequest<unknown>,\n\t\tnext: HttpHandler,\n\t): Observable<HttpEvent<unknown>> {\n\t\tconst key = this.registry.clientKeyForUrl(req.url);\n\t\t// When a client key matches but the client is still initializing\n\t\t// (async clientFactory not yet resolved), registry.get() returns\n\t\t// undefined. This is an explicit design decision: interceptors do NOT\n\t\t// block the HTTP request waiting for client initialization.\n\t\t// Responsibility for \"ensure client is ready before making requests\"\n\t\t// belongs to route guards (which use whenReady() to await readiness).\n\t\t// A not-yet-ready client correctly produces no token → request proceeds\n\t\t// without Authorization header.\n\t\treturn from(\n\t\t\tresolveAuthorizationForRequest(this.registry, req.url, key, {\n\t\t\t\tstrictUrlMatch: this.options.strictUrlMatch,\n\t\t\t}),\n\t\t).pipe(\n\t\t\tswitchMap((authorization) => {\n\t\t\t\tif (!authorization) {\n\t\t\t\t\treturn next.handle(req);\n\t\t\t\t}\n\t\t\t\treturn next.handle(\n\t\t\t\t\treq.clone({ setHeaders: { Authorization: authorization } }),\n\t\t\t\t);\n\t\t\t}),\n\t\t);\n\t}\n}\n\n/**\n * Create an `HTTP_INTERCEPTORS` multi-provider entry for bearer token injection.\n *\n * Returns a single `Provider` object that registers {@link TokenSetBearerInterceptor}\n * as a class-based HTTP interceptor. Suitable for NgModule `providers` arrays.\n *\n * For functional-interceptor style (standalone apps using `provideHttpClient`),\n * use `createTokenSetBearerInterceptor()` with `withInterceptors()` instead.\n *\n * @example\n * ```ts\n * @NgModule({\n * providers: [\n * provideTokenSetAuth({ clients: [...] }),\n * provideTokenSetBearerInterceptor(), // <— replaces hand-written AuthInterceptor\n * provideHttpClient(withInterceptorsFromDi()),\n * ],\n * })\n * export class AppModule {}\n * ```\n */\n/**\n * Create an `HTTP_INTERCEPTORS` multi-provider entry for bearer token injection.\n *\n * Returns Angular providers that register {@link TokenSetBearerInterceptor}\n * as a class-based HTTP interceptor and (optionally) the\n * {@link BearerInterceptorOptions} that govern its URL-match behaviour.\n * Suitable for NgModule `providers` arrays.\n *\n * For functional-interceptor style (standalone apps using `provideHttpClient`),\n * use `createTokenSetBearerInterceptor()` with `withInterceptors()` instead.\n *\n * @example\n * ```ts\n * @NgModule({\n * providers: [\n * provideTokenSetAuth({ clients: [...] }),\n * // Adopters with multiple backends or any third-party HTTP traffic\n * // MUST opt into strictUrlMatch to prevent token leakage.\n * provideTokenSetBearerInterceptor({ strictUrlMatch: true }),\n * provideHttpClient(withInterceptorsFromDi()),\n * ],\n * })\n * export class AppModule {}\n * ```\n */\nexport function provideTokenSetBearerInterceptor(\n\toptions?: BearerInterceptorOptions,\n): Provider[] {\n\tconst providers: Provider[] = [\n\t\t{\n\t\t\tprovide: HTTP_INTERCEPTORS,\n\t\t\tuseClass: TokenSetBearerInterceptor,\n\t\t\tmulti: true,\n\t\t},\n\t];\n\tif (options) {\n\t\tproviders.push({\n\t\t\tprovide: TOKEN_SET_BEARER_INTERCEPTOR_OPTIONS,\n\t\t\tuseValue: options,\n\t\t});\n\t}\n\treturn providers;\n}\n\n/**\n * Create an Angular `HttpInterceptorFn` that injects bearer authorization\n * headers, selecting the correct token from the multi-client registry\n * based on URL pattern matching.\n *\n * If no URL pattern matches, falls back to the first available token\n * (single-client convenience).\n *\n * ## Async client readiness\n *\n * The interceptor does **not** block or delay the HTTP request waiting for\n * an async client to materialize. If a client is still initializing when the\n * request fires, the request proceeds without an Authorization header.\n *\n * This is intentional: blocking HTTP for client initialization would deadlock\n * apps that make HTTP requests during initialization itself. Route guards\n * (which use `registry.whenReady()`) are the correct place to enforce\n * \"client must be ready before user reaches this route\".\n *\n * For NgModule-style apps using `HTTP_INTERCEPTORS`, use\n * `provideTokenSetBearerInterceptor()` instead.\n *\n * @example\n * ```ts\n * import { provideHttpClient, withInterceptors } from \"@angular/common/http\";\n * import { createTokenSetBearerInterceptor } from \"@securitydept/token-set-context-client-angular\";\n *\n * provideHttpClient(withInterceptors([\n * createTokenSetBearerInterceptor(registry),\n * ]));\n * ```\n */\n/**\n * Create an Angular `HttpInterceptorFn` that injects bearer authorization\n * headers, selecting the correct token from the multi-client registry\n * based on URL pattern matching.\n *\n * If no URL pattern matches, the unmatched-URL behaviour is governed by\n * {@link BearerInterceptorOptions.strictUrlMatch}: the default falls back\n * to `registry.accessToken()` (single-client convenience), while\n * `strictUrlMatch: true` returns no token, ensuring bearer headers are\n * never injected for URLs outside the registered patterns.\n *\n * ## Async client readiness\n *\n * The interceptor does **not** block or delay the HTTP request waiting for\n * an async client to materialize. If a client is still initializing when the\n * request fires, the request proceeds without an Authorization header.\n *\n * This is intentional: blocking HTTP for client initialization would deadlock\n * apps that make HTTP requests during initialization itself. Route guards\n * (which use `registry.whenReady()`) are the correct place to enforce\n * \"client must be ready before user reaches this route\".\n *\n * For NgModule-style apps using `HTTP_INTERCEPTORS`, use\n * `provideTokenSetBearerInterceptor()` instead.\n *\n * @example\n * ```ts\n * import { provideHttpClient, withInterceptors } from \"@angular/common/http\";\n * import { createTokenSetBearerInterceptor } from \"@securitydept/token-set-context-client-angular\";\n *\n * provideHttpClient(withInterceptors([\n * createTokenSetBearerInterceptor(registry, { strictUrlMatch: true }),\n * ]));\n * ```\n */\nexport function createTokenSetBearerInterceptor(\n\tregistry: TokenSetAuthRegistry,\n\toptions: BearerInterceptorOptions = {},\n) {\n\treturn (\n\t\treq: {\n\t\t\turl: string;\n\t\t\tclone(update: { setHeaders?: Record<string, string> }): unknown;\n\t\t},\n\t\tnext: (req: unknown) => Observable<unknown>,\n\t): Observable<unknown> => {\n\t\t// Try URL-pattern-based client selection first.\n\t\tconst key = registry.clientKeyForUrl(req.url);\n\t\t// Explicit not-yet-ready semantics: if the client exists in metadata\n\t\t// (via clientKeyForUrl) but registry.get() returns undefined because the\n\t\t// async clientFactory has not resolved yet, the request proceeds without\n\t\t// a token. See class-based interceptor above for full rationale.\n\t\treturn from(\n\t\t\tresolveAuthorizationForRequest(registry, req.url, key, options),\n\t\t).pipe(\n\t\t\tswitchMap((authorization) => {\n\t\t\t\tif (!authorization) {\n\t\t\t\t\treturn next(req);\n\t\t\t\t}\n\t\t\t\treturn next(\n\t\t\t\t\treq.clone({ setHeaders: { Authorization: authorization } }),\n\t\t\t\t);\n\t\t\t}),\n\t\t);\n\t};\n}\n\nasync function resolveAuthorizationForRequest(\n\tregistry: TokenSetAuthRegistry,\n\turl: string,\n\tkey: string | undefined,\n\toptions: BearerInterceptorOptions,\n): Promise<string | null> {\n\tif (!key && options.strictUrlMatch) {\n\t\treturn null;\n\t}\n\n\tconst result = await registry.ensureAuthForResource({\n\t\tkey,\n\t\twaitForReady: false,\n\t\tsource: TokenSetAuthFlowSource.HttpInterceptor,\n\t\tneedsAuthorizationHeader: true,\n\t\tforceRefreshWhenDue: true,\n\t\turl,\n\t});\n\treturn result?.authorizationHeader ?? null;\n}\n","import { Injectable, inject } from \"@angular/core\";\nimport type { AuthSnapshot } from \"@securitydept/token-set-context-client/orchestration\";\nimport { TokenSetAuthRegistry } from \"./token-set-auth-registry\";\n\n/**\n * Angular-native service for handling OIDC redirect callbacks,\n * with multi-client discrimination support.\n *\n * The post-authentication redirect URI is propagated automatically through\n * the core client's pending state (set via `loginWithRedirect({ postAuthRedirectUri })`\n * and recovered in `handleCallback()`). No manual URL storage is required.\n *\n * @example\n * ```ts\n * @Component({ ... })\n * export class AuthCallbackComponent {\n * private readonly callbackService = inject(CallbackResumeService);\n * private readonly router = inject(Router);\n *\n * async ngOnInit() {\n * const result = await this.callbackService.handleCallback(\n * window.location.href,\n * );\n * this.router.navigateByUrl(result.resumeUrl, { replaceUrl: true });\n * }\n * }\n * ```\n */\n@Injectable()\nexport class CallbackResumeService {\n\tprivate readonly registry = inject(TokenSetAuthRegistry);\n\n\t/**\n\t * Check whether a URL is an OIDC authorization callback for any registered\n\t * client in this registry.\n\t *\n\t * This is a convenience wrapper over `clientKeyForCallback()`. Use it for\n\t * programmatic early-exit guards (e.g. in a service constructor) before\n\t * calling `handleCallback()`.\n\t *\n\t * @param url - The full URL to check (e.g. `window.location.href`).\n\t * @returns `true` when the URL matches a registered callback path and\n\t * contains an `code` or `error` query parameter.\n\t */\n\tisCallback(url: string): boolean {\n\t\treturn this.registry.clientKeyForCallback(url) !== undefined;\n\t}\n\n\t/**\n\t * Handle the OIDC callback, auto-detecting which client the callback\n\t * belongs to via registered callback paths.\n\t *\n\t * The `resumeUrl` in the result is taken directly from the core client's\n\t * pending state (`postAuthRedirectUri` set at `loginWithRedirect()` time),\n\t * falling back to `\"/\"` when no redirect URI was recorded.\n\t *\n\t * @param callbackUrl - The full current URL (e.g. `window.location.href`).\n\t * @param explicitClientKey - Optional: force-select a client key instead of\n\t * auto-detecting from registered callback paths.\n\t */\n\tasync handleCallback(\n\t\tcallbackUrl: string,\n\t\texplicitClientKey?: string,\n\t): Promise<{\n\t\tclientKey: string;\n\t\tsnapshot: AuthSnapshot;\n\t\t/** The URL to navigate to after a successful callback. */\n\t\tresumeUrl: string;\n\t}> {\n\t\t// Determine which client this callback is for.\n\t\tconst clientKey =\n\t\t\texplicitClientKey ?? this.registry.clientKeyForCallback(callbackUrl);\n\t\tif (!clientKey) {\n\t\t\tthrow new Error(\n\t\t\t\t`[CallbackResumeService] Cannot determine which client this callback belongs to. ` +\n\t\t\t\t\t`URL: ${callbackUrl}. Register callbackPath in the client entry.`,\n\t\t\t);\n\t\t}\n\n\t\t// Use whenReady() — not require() — so that if the client's async\n\t\t// clientFactory is still in-flight when the callback page first loads,\n\t\t// we wait for it to materialize rather than throwing.\n\t\tconst service = await this.registry.whenReady(clientKey);\n\t\tconst result = await service.client.handleCallback(callbackUrl);\n\n\t\t// postAuthRedirectUri is recovered from the core client's pending state,\n\t\t// which was stored by authorizeUrl() when loginWithRedirect() was called.\n\t\treturn {\n\t\t\tclientKey,\n\t\t\tsnapshot: result.snapshot,\n\t\t\tresumeUrl: result.postAuthRedirectUri ?? \"/\",\n\t\t};\n\t}\n}\n","// ============================================================================\n// 8. Callback helper — OIDC redirect callback with multi-client discrimination\n// ============================================================================\n\n/**\n * Check whether the current URL is an OIDC authorization callback.\n */\nexport function isOidcCallback(options: {\n\tcurrentUrl: string;\n\tcallbackPath: string;\n}): boolean {\n\ttry {\n\t\tconst url = new URL(options.currentUrl);\n\t\tconst callbackUrl = new URL(options.callbackPath, url.origin);\n\t\treturn (\n\t\t\turl.pathname === callbackUrl.pathname &&\n\t\t\t(url.searchParams.has(\"code\") || url.searchParams.has(\"error\"))\n\t\t);\n\t} catch {\n\t\treturn false;\n\t}\n}\n","import { InjectionToken } from \"@angular/core\";\nimport type { TokenSetAuthRegistry } from \"./token-set-auth-registry\";\n\n/** InjectionToken for the multi-client auth registry. */\nexport const TOKEN_SET_AUTH_REGISTRY = new InjectionToken<TokenSetAuthRegistry>(\n\t\"TOKEN_SET_AUTH_REGISTRY\",\n);\n","import {\n\ttype EnvironmentProviders,\n\tinject,\n\ttype Provider,\n\tprovideEnvironmentInitializer,\n} from \"@angular/core\";\nimport { CallbackResumeService } from \"./callback-resume.service\";\nimport type { TokenSetClientEntry } from \"./contracts\";\nimport { TokenSetAuthRegistry } from \"./token-set-auth-registry\";\nimport { TOKEN_SET_AUTH_REGISTRY } from \"./tokens\";\n\n/**\n * Options for {@link provideTokenSetAuth}.\n */\nexport interface ProvideTokenSetAuthOptions {\n\t/**\n\t * Adapter/host registration entries.\n\t *\n\t * Each entry must have a unique `key`. The entry itself owns how one client\n\t * composes auth-context config and runtime capabilities; Angular DI here only\n\t * owns host registration, readiness wiring, and lifecycle.\n\t */\n\tclients: TokenSetClientEntry[];\n\t/**\n\t * When true (default), schedule `registry.idleWarmup()` during Angular\n\t * environment initialization so `priority: \"lazy\"` clients get\n\t * preloaded in the browser's idle callback.\n\t *\n\t * Disable for tests that want deterministic, manual materialization\n\t * control.\n\t *\n\t * @default true\n\t */\n\tidleWarmup?: boolean;\n}\n\n/**\n * Create Angular providers for the multi-client token-set auth integration.\n *\n * Registers all services into Angular DI so adopters can directly inject\n * `TokenSetAuthRegistry`, `CallbackResumeService`, and look up individual\n * `TokenSetAuthService` instances by key.\n *\n * ## Async client initialization\n *\n * When a client's `clientFactory` returns a `Promise` (e.g. because it\n * needs to fetch a config projection from a backend endpoint), the\n * registry tracks its initialization state automatically. Guards and\n * interceptors should use `registry.whenReady(key)` to await\n * materialization before accessing the service.\n *\n * Adapter-managed browser clients also install token-set page-resume\n * reconciliation by default during materialization. Set\n * `resumeReconciliation: false` on an individual client entry only when the\n * host intentionally owns equivalent browser lifecycle wiring.\n *\n * @example\n * ```ts\n * import { provideTokenSetAuth } from \"@securitydept/token-set-context-client-angular\";\n * import { resolveConfigProjection, networkConfigSource, createFrontendOidcModeClient }\n * from \"@securitydept/token-set-context-client/frontend-oidc-mode\";\n *\n * export const appConfig = {\n * providers: [\n * provideTokenSetAuth({\n * clients: [\n * {\n * key: \"main\",\n * // Async clientFactory: fetches config projection from backend\n * clientFactory: async () => {\n * const resolved = await resolveConfigProjection([\n * networkConfigSource({\n * apiEndpoint: \"https://api.example.com/api\",\n * redirectUri: `${location.origin}/auth/callback`,\n * }),\n * ]);\n * return createFrontendOidcModeClient(resolved.config, runtime);\n * },\n * urlPatterns: [\"/api/\"],\n * callbackPath: \"/auth/callback\",\n * // Optional: opt out only if the host already wires equivalent\n * // resume reconciliation for this browser client.\n * // resumeReconciliation: false,\n * },\n * ],\n * }),\n * ],\n * };\n * ```\n */\nexport function provideTokenSetAuth(\n\toptions: ProvideTokenSetAuthOptions,\n): (Provider | EnvironmentProviders)[] {\n\treturn [\n\t\t// Registry is a singleton — all clients share one registry.\n\t\t{\n\t\t\tprovide: TokenSetAuthRegistry,\n\t\t\tuseFactory: () => new TokenSetAuthRegistry(),\n\t\t},\n\t\t// Eagerly register all client entries during environment initialization.\n\t\t// provideEnvironmentInitializer runs in injection context, so inject() works.\n\t\t// Note: async clientFactories create promises that the registry tracks;\n\t\t// the initializer does not block Angular bootstrap on them (guards/interceptors\n\t\t// use registry.whenReady() instead).\n\t\tprovideEnvironmentInitializer(() => {\n\t\t\tconst registry = inject(TokenSetAuthRegistry);\n\t\t\tfor (const entry of options.clients) {\n\t\t\t\tregistry.register(entry);\n\t\t\t}\n\t\t\tif (options.idleWarmup !== false) {\n\t\t\t\t// Kick off idle-time preload for any lazy clients. The cancel\n\t\t\t\t// handle is intentionally discarded — teardown happens through\n\t\t\t\t// the registry's own DestroyRef binding at dispose time.\n\t\t\t\tregistry.idleWarmup();\n\t\t\t}\n\t\t}),\n\t\t// Token for direct registry access.\n\t\t{\n\t\t\tprovide: TOKEN_SET_AUTH_REGISTRY,\n\t\t\tuseExisting: TokenSetAuthRegistry,\n\t\t},\n\t\t// Callback resume service.\n\t\tCallbackResumeService,\n\t];\n}\n","import {\n\tEnvironmentInjector,\n\tinject,\n\trunInInjectionContext,\n} from \"@angular/core\";\nimport type {\n\tActivatedRouteSnapshot,\n\tCanActivateFn,\n\tRouterStateSnapshot,\n\tUrlTree,\n} from \"@angular/router\";\nimport { Router } from \"@angular/router\";\nimport type {\n\tAuthGuardClientOption,\n\tAuthRequirement,\n\tPlannerHost,\n} from \"@securitydept/client/auth-coordination\";\nimport {\n\tAUTH_PLANNER_HOST,\n\textractFullRouteRequirements,\n} from \"@securitydept/client-angular\";\nimport {\n\ttype AuthSnapshot,\n\tTokenSetAuthFlowSource,\n} from \"@securitydept/token-set-context-client/orchestration\";\nimport { firstValueFrom, from, switchMap, take } from \"rxjs\";\nimport type { UnauthenticatedEntry } from \"./guard-types\";\nimport type { ClientMeta, ClientQueryOptions } from \"./token-set-auth-registry\";\nimport { TokenSetAuthRegistry } from \"./token-set-auth-registry\";\n\n// ============================================================================\n// Guard factory — createTokenSetRouteAggregationGuard() [LOWER-LEVEL ADVANCED]\n//\n// Canonical Angular Router auth pattern for multi-requirement coordination:\n//\n// 1. Declaration layer — withRouteRequirements([...]) in route data\n// 2. Aggregation layer — this guard walks pathFromRoot once and collects\n// the full requirement chain\n// 3. Decision layer — planner host evaluates all candidates in one call\n// 4. Enforcement layer — registry maps requirement.kind → client → action\n//\n// Fine-grained control is available via requirementPolicies[requirementId]:\n// - selector.clientKey / selector.query override the default kind→client mapping\n// - per-requirement onUnauthenticated takes precedence over kind handlers\n//\n// secureRouteRoot() / secureRoute() are the canonical adopter-facing path.\n// This guard remains available as the lower-level route-tree primitive.\n// ============================================================================\n\n// ---------------------------------------------------------------------------\n// Selector type — used in requirementPolicies to override client resolution\n// ---------------------------------------------------------------------------\n\n/**\n * How to select a token-set client from the registry for a specific requirement.\n *\n * Choose exactly one:\n * - `clientKey` — direct key lookup (explicit, single client)\n * - `query` — composite filter query ({@link ClientQueryOptions})\n */\nexport type TokenSetClientSelector =\n\t| { clientKey: string; query?: never }\n\t| { clientKey?: never; query: ClientQueryOptions };\n\n/**\n * Runtime context passed to unauthenticated route handlers.\n *\n * `attemptedUrl` is the Angular Router target URL for the navigation being\n * guarded. Use it as `postAuthRedirectUri`; do not read `Router.url` for this,\n * because Angular still exposes the currently active URL while a guard is\n * deciding whether the attempted navigation may proceed.\n */\nexport interface TokenSetRouteUnauthenticatedContext {\n\treadonly route: ActivatedRouteSnapshot;\n\treadonly state: RouterStateSnapshot;\n\treadonly attemptedUrl: string;\n}\n\nexport type TokenSetRouteUnauthenticatedHandler = (\n\tunauthenticated: ReadonlyArray<UnauthenticatedEntry>,\n\trequirement: AuthRequirement,\n\tcontext: TokenSetRouteUnauthenticatedContext,\n) => boolean | string | UrlTree | Promise<boolean | string | UrlTree>;\n\n// ---------------------------------------------------------------------------\n// Per-requirement policy\n// ---------------------------------------------------------------------------\n\n/**\n * Policy override for a specific requirement ID in\n * {@link CreateTokenSetRouteAggregationGuardOptions.requirementPolicies}.\n *\n * Allows fine-grained control per declared requirement:\n * - `selector` overrides the default kind→client registry lookup (use when\n * you need a specific client key or a query instead of all clients of that kind).\n * - `onUnauthenticated` overrides both `requirementHandlers[kind]` and\n * `defaultOnUnauthenticated` for this exact requirement.\n *\n * @example\n * ```ts\n * createTokenSetRouteAggregationGuard({\n * requirementPolicies: {\n * \"confluence-oidc\": {\n * selector: { clientKey: \"confluence\" },\n * onUnauthenticated: (_failing, _req, context) => {\n * inject(AuthService)\n * .redirectToLogin(\"confluence\", context.attemptedUrl)\n * .subscribe();\n * return false;\n * },\n * },\n * \"admin-oidc\": {\n * selector: { query: { providerFamily: \"internal-sso\" } },\n * onUnauthenticated: () => \"/admin/login\",\n * },\n * },\n * })\n * ```\n */\nexport interface TokenSetRequirementPolicy {\n\t/**\n\t * Override the default kind→client registry lookup for this requirement.\n\t *\n\t * When omitted, the guard resolves clients via\n\t * `registry.clientKeyListForRequirement(requirement.kind)`.\n\t */\n\tselector?: TokenSetClientSelector;\n\n\t/**\n\t * Handler called when this requirement is selected as pending by the planner.\n\t *\n\t * Takes precedence over `requirementHandlers[kind]` and\n\t * `defaultOnUnauthenticated`.\n\t *\n\t * Return:\n\t * - `true` — allow navigation\n\t * - `false` — block navigation\n\t * - `string` — redirect URL path\n\t * - `UrlTree` — Angular router tree\n\t */\n\tonUnauthenticated: TokenSetRouteUnauthenticatedHandler;\n}\n\n// ---------------------------------------------------------------------------\n// Guard options\n// ---------------------------------------------------------------------------\n\n/**\n * Options for {@link createTokenSetRouteAggregationGuard}.\n */\nexport interface CreateTokenSetRouteAggregationGuardOptions {\n\t/**\n\t * Per-requirement-id policies.\n\t *\n\t * Keys are `AuthRequirement.id` strings (not kind). Policies declared here\n\t * take precedence over {@link requirementHandlers} and\n\t * {@link defaultOnUnauthenticated}.\n\t *\n\t * Use this for fine-grained control:\n\t * - override client selection (specific key or composite query)\n\t * - per-requirement redirect behaviour\n\t * - multi-client / providerFamily selection per requirement\n\t *\n\t * @example\n\t * ```ts\n\t * createTokenSetRouteAggregationGuard({\n\t * requirementPolicies: {\n\t * \"confluence-oidc\": {\n\t * selector: { clientKey: \"confluence\" },\n\t * onUnauthenticated: () => \"/auth/confluence\",\n\t * },\n\t * \"admin-oidc\": {\n\t * selector: { query: { providerFamily: \"internal-sso\" } },\n\t * onUnauthenticated: () => \"/auth/admin\",\n\t * },\n\t * },\n\t * })\n\t * ```\n\t */\n\trequirementPolicies?: Record<string, TokenSetRequirementPolicy>;\n\n\t/**\n\t * Per-requirement-kind handlers.\n\t *\n\t * When the planner selects a pending requirement whose `kind` maps to an\n\t * entry here (and no `requirementPolicies[id]` is set), that handler is\n\t * called instead of `defaultOnUnauthenticated`.\n\t *\n\t * Keys are `AuthRequirement.kind` strings.\n\t *\n\t * @example\n\t * ```ts\n\t * createTokenSetRouteAggregationGuard({\n\t * requirementHandlers: {\n\t * frontend_oidc: (failing, _req, context) => {\n\t * inject(AuthService)\n\t * .redirectToLogin(failing[0].clientKey, context.attemptedUrl)\n\t * .subscribe();\n\t * return false;\n\t * },\n\t * },\n\t * })\n\t * ```\n\t */\n\trequirementHandlers?: Record<string, TokenSetRouteUnauthenticatedHandler>;\n\n\t/**\n\t * Fallback handler used when no `requirementPolicies[id]` or\n\t * `requirementHandlers[kind]` is found for the pending requirement.\n\t */\n\tdefaultOnUnauthenticated?: TokenSetRouteUnauthenticatedHandler;\n\n\t/**\n\t * Custom route data key for reading requirements from route segments.\n\t * @default \"authRequirements\"\n\t */\n\trequirementsKey?: string;\n\n\t/**\n\t * Inline planner-host instance. If provided, used instead of injecting\n\t * `AUTH_PLANNER_HOST` from Angular DI.\n\t */\n\tplannerHost?: PlannerHost;\n\n\t/**\n\t * Resolve the planner-host for the current target route chain.\n\t *\n\t * Use this when planner-host selection depends on route metadata or other\n\t * route-tree state. Takes precedence over `plannerHost` and DI lookup.\n\t */\n\tplannerHostResolver?: (\n\t\troute: ActivatedRouteSnapshot,\n\t\tstate: RouterStateSnapshot,\n\t) => PlannerHost | undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Guard factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create an Angular `CanActivateFn` that performs full-route requirement\n * aggregation and single-pass planner evaluation.\n *\n * **Canonical Angular Router auth pattern** (route-metadata driven):\n * 1. Requirements are declared in route `data` using `withRouteRequirements()`.\n * 2. When the guard runs, it walks `route.pathFromRoot` to collect ALL\n * requirements from every ancestor segment in a single pass.\n * 3. Each requirement's `kind` is mapped to one or more registry clients,\n * unless overridden by a `requirementPolicies[id].selector`.\n * 4. The complete candidate set is handed to the `PlannerHost` for a\n * single `evaluate()` call — the planner selects the first unresolved\n * requirement and calls the matching handler.\n *\n * **Handler resolution order** (most specific wins):\n * 1. `requirementPolicies[requirement.id].onUnauthenticated`\n * 2. `requirementHandlers[requirement.kind]`\n * 3. `defaultOnUnauthenticated`\n * 4. Block navigation (`false`) if none of the above is provided.\n *\n * **Client resolution order** for a requirement:\n * 1. `requirementPolicies[requirement.id].selector.clientKey` — single explicit key\n * 2. `requirementPolicies[requirement.id].selector.query` — composite query\n * 3. `registry.clientKeyListForRequirement(requirement.kind)` — default mapping\n *\n * This model enables:\n * - One-pass requirement collection (no per-segment DI scope accumulation)\n * - One-call planner evaluation (chooser sees the full set at once)\n * - One redirect decision (no per-segment guard coordination needed)\n * - Fine-grained per-requirement policy via `requirementPolicies`\n *\n * @example\n * ```ts\n * // routes.ts — declare requirements in route data\n * import { withRouteRequirements } from \"@securitydept/client-angular\";\n * import {\n * createFrontendOidcLoginRedirectHandler,\n * createTokenSetRouteAggregationGuard,\n * } from \"@securitydept/token-set-context-client-angular\";\n *\n * // Simple — kind-level handler\n * const guard = createTokenSetRouteAggregationGuard({\n * requirementHandlers: {\n * frontend_oidc: createFrontendOidcLoginRedirectHandler({ clientKey: \"confluence\" }),\n * },\n * });\n *\n * // Fine-grained — per-requirement policy\n * const guard = createTokenSetRouteAggregationGuard({\n * requirementPolicies: {\n * \"confluence-oidc\": {\n * selector: { clientKey: \"confluence\" },\n * onUnauthenticated: () => \"/auth/confluence\",\n * },\n * \"admin-oidc\": {\n * selector: { query: { providerFamily: \"internal-sso\" } },\n * onUnauthenticated: (failing) => \"/auth/admin\",\n * },\n * },\n * });\n *\n * export const routes: Routes = [\n * {\n * path: \"\",\n * data: withRouteRequirements([{ id: \"session\", kind: \"session\" }]),\n * children: [\n * {\n * path: \"confluence\",\n * data: withRouteRequirements([{ id: \"confluence-oidc\", kind: \"frontend_oidc\" }]),\n * canActivate: [guard],\n * },\n * ],\n * },\n * ];\n * ```\n */\nexport function createTokenSetRouteAggregationGuard(\n\toptions: CreateTokenSetRouteAggregationGuardOptions = {},\n): CanActivateFn {\n\treturn async (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {\n\t\tconst injector = inject(EnvironmentInjector);\n\t\tconst registry = inject(TokenSetAuthRegistry);\n\t\tconst router = inject(Router);\n\t\tconst handlerContext: TokenSetRouteUnauthenticatedContext = {\n\t\t\troute,\n\t\t\tstate,\n\t\t\tattemptedUrl: state.url,\n\t\t};\n\n\t\t// ── Resolve planner-host ────────────────────────────────────────────\n\t\tconst plannerHost =\n\t\t\toptions.plannerHostResolver?.(route, state) ??\n\t\t\toptions.plannerHost ??\n\t\t\tinject(AUTH_PLANNER_HOST, { optional: true });\n\n\t\tif (!plannerHost) {\n\t\t\tthrow new Error(\n\t\t\t\t\"[createTokenSetRouteAggregationGuard] No PlannerHost found. \" +\n\t\t\t\t\t\"Provide AUTH_PLANNER_HOST via provideAuthPlannerHost() \" +\n\t\t\t\t\t\"in your app/route providers, or pass plannerHost inline.\",\n\t\t\t);\n\t\t}\n\n\t\t// ── Step 1: Full-route requirement aggregation ───────────────────────\n\t\tconst allRequirements = extractFullRouteRequirements(\n\t\t\troute,\n\t\t\toptions.requirementsKey,\n\t\t);\n\n\t\tif (allRequirements.length === 0) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// ── Step 2: Map requirements → registry clients ──────────────────────\n\t\t// Per-requirement policy may override the default kind→client mapping.\n\t\tinterface ResolvedRequirementEntry {\n\t\t\trequirement: AuthRequirement;\n\t\t\tentries: UnauthenticatedEntry[];\n\t\t}\n\n\t\tconst resolvedRequirements: ResolvedRequirementEntry[] = [];\n\n\t\tfor (const req of allRequirements) {\n\t\t\tconst policy = options.requirementPolicies?.[req.id];\n\t\t\tlet clientKeys: string[];\n\n\t\t\tif (policy?.selector) {\n\t\t\t\t// Policy provides an explicit selector — use it instead of kind mapping.\n\t\t\t\tif (policy.selector.clientKey !== undefined) {\n\t\t\t\t\tclientKeys = [policy.selector.clientKey];\n\t\t\t\t} else {\n\t\t\t\t\tclientKeys = registry.clientKeysForOptions(policy.selector.query);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Default: resolve by requirement kind.\n\t\t\t\tclientKeys = registry.clientKeyListForRequirement(req.kind);\n\t\t\t}\n\n\t\t\tif (clientKeys.length === 0) {\n\t\t\t\t// No client for this requirement — skip (lets non-token-set requirements\n\t\t\t\t// coexist in the same route chain without breaking the guard).\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst entries: UnauthenticatedEntry[] = await Promise.all(\n\t\t\t\tclientKeys.map(async (key) => ({\n\t\t\t\t\t// Use whenReady() — not require() — so that if the client's\n\t\t\t\t\t// async clientFactory is still in-flight when the guard first\n\t\t\t\t\t// fires, we block here until it materializes rather than\n\t\t\t\t\t// crashing or silently bypassing auth.\n\t\t\t\t\tservice: await registry.whenReady(key),\n\t\t\t\t\tclientKey: key,\n\t\t\t\t\tmeta: registry.metaFor(key) as ClientMeta,\n\t\t\t\t})),\n\t\t\t);\n\n\t\t\tresolvedRequirements.push({ requirement: req, entries });\n\t\t}\n\n\t\tif (resolvedRequirements.length === 0) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// ── Step 3: Build planner candidates ────────────────────────────────\n\t\t// De-duplicate by requirementId (last declaration for the same id wins).\n\t\tconst seenIds = new Map<string, ResolvedRequirementEntry>();\n\t\tfor (const r of resolvedRequirements) {\n\t\t\tseenIds.set(r.requirement.id, r);\n\t\t}\n\t\tconst deduped = [...seenIds.values()];\n\n\t\tconst candidates: Array<\n\t\t\tAuthGuardClientOption & { _resolved: ResolvedRequirementEntry }\n\t\t> = deduped.map((r) => ({\n\t\t\trequirementId: r.requirement.id,\n\t\t\trequirementKind: r.requirement.kind,\n\t\t\tlabel: r.requirement.label,\n\t\t\tattributes: r.requirement.attributes,\n\t\t\tcheckAuthenticated: () =>\n\t\t\t\tr.entries.every(({ service }) => service.isAuthenticated()),\n\t\t\tonUnauthenticated: async (): Promise<boolean | string> => {\n\t\t\t\tconst failing = r.entries.filter(\n\t\t\t\t\t({ service }) => !service.isAuthenticated(),\n\t\t\t\t);\n\t\t\t\t// Handler resolution: policy > kind handler > default\n\t\t\t\tconst handler =\n\t\t\t\t\toptions.requirementPolicies?.[r.requirement.id]?.onUnauthenticated ??\n\t\t\t\t\toptions.requirementHandlers?.[r.requirement.kind] ??\n\t\t\t\t\toptions.defaultOnUnauthenticated;\n\n\t\t\t\tif (!handler) return false;\n\n\t\t\t\tconst result = runUnauthenticatedHandlerInContext(\n\t\t\t\t\tinjector,\n\t\t\t\t\thandler,\n\t\t\t\t\tfailing,\n\t\t\t\t\tr.requirement,\n\t\t\t\t\thandlerContext,\n\t\t\t\t);\n\t\t\t\tconst resolved = await result;\n\t\t\t\tif (typeof resolved === \"string\") return resolved;\n\t\t\t\tif (typeof resolved === \"boolean\") return resolved;\n\t\t\t\treturn resolved.toString();\n\t\t\t},\n\t\t\t_resolved: r,\n\t\t}));\n\n\t\t// ── Step 4: Single planner evaluation ───────────────────────────────\n\t\tconst decide = async (): Promise<boolean | UrlTree> => {\n\t\t\tconst result = await plannerHost.evaluate(candidates);\n\n\t\t\tif (result.allAuthenticated) return true;\n\n\t\t\tif (result.pendingCandidate) {\n\t\t\t\tconst candidate = result.pendingCandidate as AuthGuardClientOption & {\n\t\t\t\t\t_resolved: ResolvedRequirementEntry;\n\t\t\t\t};\n\t\t\t\tconst failing = candidate._resolved.entries.filter(\n\t\t\t\t\t({ service }) => !service.isAuthenticated(),\n\t\t\t\t);\n\t\t\t\t// Handler resolution: policy > kind handler > default\n\t\t\t\tconst handler =\n\t\t\t\t\toptions.requirementPolicies?.[candidate._resolved.requirement.id]\n\t\t\t\t\t\t?.onUnauthenticated ??\n\t\t\t\t\toptions.requirementHandlers?.[candidate._resolved.requirement.kind] ??\n\t\t\t\t\toptions.defaultOnUnauthenticated;\n\n\t\t\t\tif (!handler) return false;\n\n\t\t\t\tconst action = await runUnauthenticatedHandlerInContext(\n\t\t\t\t\tinjector,\n\t\t\t\t\thandler,\n\t\t\t\t\tfailing,\n\t\t\t\t\tcandidate._resolved.requirement,\n\t\t\t\t\thandlerContext,\n\t\t\t\t);\n\t\t\t\tif (typeof action === \"string\") return router.parseUrl(action);\n\t\t\t\tif (typeof action === \"boolean\") return action;\n\t\t\t\treturn action;\n\t\t\t}\n\n\t\t\treturn false;\n\t\t};\n\n\t\t// ── Restore-aware: wait for all pending restores ─────────────────────\n\t\tconst pendingRestores = deduped\n\t\t\t.flatMap((r) => r.entries)\n\t\t\t.map(({ service }) => service.restorePromise)\n\t\t\t.filter((p): p is Promise<AuthSnapshot | null> => p !== null);\n\t\tasync function awaitAuthMaterial(): Promise<void> {\n\t\t\tawait Promise.all(pendingRestores);\n\t\t\tconst freshnessBarriers = deduped\n\t\t\t\t.flatMap((r) =>\n\t\t\t\t\tr.entries.map((entry) => ({\n\t\t\t\t\t\t...entry,\n\t\t\t\t\t\trequirement: r.requirement,\n\t\t\t\t\t})),\n\t\t\t\t)\n\t\t\t\t.map(({ service, clientKey, meta, requirement }) =>\n\t\t\t\t\tservice.ensureAuthForResource({\n\t\t\t\t\t\tsource: TokenSetAuthFlowSource.RouteGuard,\n\t\t\t\t\t\tclientKey,\n\t\t\t\t\t\trequirement: { id: requirement.id, kind: requirement.kind },\n\t\t\t\t\t\tproviderFamily: meta.providerFamily,\n\t\t\t\t\t\turl: state.url,\n\t\t\t\t\t\tforceRefreshWhenDue: true,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\tawait Promise.all(freshnessBarriers);\n\t\t}\n\n\t\tif (deduped.every((r) => r.entries.length === 0)) {\n\t\t\treturn decide();\n\t\t}\n\n\t\t// await the restore-phase Observable before running planner evaluation\n\t\t// so the async guard always returns a Promise, not an Observable.\n\t\treturn firstValueFrom(\n\t\t\tfrom(awaitAuthMaterial()).pipe(\n\t\t\t\tswitchMap(() => from(decide())),\n\t\t\t\ttake(1),\n\t\t\t),\n\t\t);\n\t};\n}\n\nfunction runUnauthenticatedHandlerInContext(\n\tinjector: EnvironmentInjector,\n\thandler: TokenSetRouteUnauthenticatedHandler,\n\tunauthenticated: ReadonlyArray<UnauthenticatedEntry>,\n\trequirement: AuthRequirement,\n\tcontext: TokenSetRouteUnauthenticatedContext,\n): boolean | string | UrlTree | Promise<boolean | string | UrlTree> {\n\treturn runInInjectionContext(injector, () =>\n\t\thandler(unauthenticated, requirement, context),\n\t);\n}\n\n/**\n * Build an Angular route-security handler that starts frontend-oidc login and\n * records the attempted navigation URL as `postAuthRedirectUri`.\n *\n * Use this instead of reading `Router.url` inside a guard handler. During guard\n * execution Angular has not committed the attempted navigation yet, so\n * `Router.url` still points at the previously active page.\n *\n * After the browser redirect is started, the returned guard result intentionally\n * never settles. That keeps Angular from finalizing the rejected in-app\n * navigation while the page is already leaving for an external IdP.\n */\nexport function createFrontendOidcLoginRedirectHandler(options?: {\n\t/**\n\t * Explicit client key. When omitted, the first failing registry entry is\n\t * used, which is the common one-client-per-requirement case.\n\t */\n\tclientKey?: string;\n\t/**\n\t * Fallback used only when Angular does not provide a target URL.\n\t * @default \"/\"\n\t */\n\tfallbackPostAuthRedirectUri?: string;\n}): TokenSetRouteUnauthenticatedHandler {\n\treturn async (unauthenticated, _requirement, context) => {\n\t\tconst clientKey = options?.clientKey ?? unauthenticated[0]?.clientKey;\n\t\tif (!clientKey) return false;\n\n\t\tconst registry = inject(TokenSetAuthRegistry);\n\t\tconst service = await registry.whenReady(clientKey);\n\t\tif (!isLoginWithRedirectClient(service.client)) return false;\n\n\t\tawait service.client.loginWithRedirect({\n\t\t\tpostAuthRedirectUri:\n\t\t\t\tcontext.attemptedUrl || options?.fallbackPostAuthRedirectUri || \"/\",\n\t\t});\n\t\treturn await neverSettlingRedirectGuardResult();\n\t};\n}\n\nfunction neverSettlingRedirectGuardResult(): Promise<never> {\n\treturn new Promise<never>(() => {\n\t\t// Intentionally never resolves: a full-page browser redirect is in progress.\n\t});\n}\n\ninterface LoginWithRedirectClient {\n\tloginWithRedirect(options?: { postAuthRedirectUri?: string }): Promise<void>;\n}\n\nfunction isLoginWithRedirectClient(\n\tclient: unknown,\n): client is LoginWithRedirectClient {\n\treturn (\n\t\ttypeof client === \"object\" &&\n\t\tclient !== null &&\n\t\t\"loginWithRedirect\" in client &&\n\t\ttypeof client.loginWithRedirect === \"function\"\n\t);\n}\n","import type {\n\tActivatedRouteSnapshot,\n\tCanActivateChildFn,\n\tRoute,\n\tRouterStateSnapshot,\n} from \"@angular/router\";\nimport type {\n\tAuthRequirement,\n\tPlannerHost,\n\tRequirementsClientSetComposition,\n} from \"@securitydept/client/auth-coordination\";\nimport { withRouteRequirements } from \"@securitydept/client-angular\";\nimport type {\n\tCreateTokenSetRouteAggregationGuardOptions,\n\tTokenSetRequirementPolicy,\n} from \"./route-aggregation-guard\";\nimport { createTokenSetRouteAggregationGuard } from \"./route-aggregation-guard\";\n\n/**\n * Route data key for choosing a planner-host registered at the route root.\n */\nexport const TOKEN_SET_ROUTE_PLANNER_HOST_KEY_DATA_KEY =\n\t\"tokenSetPlannerHostKey\";\n\n/**\n * Serializable route-level security declaration.\n *\n * This object is safe to embed in Angular route metadata. Non-serializable\n * runtime policy (planner host instances, callbacks, chooser implementations)\n * belongs in {@link SecureRouteRootSecurityOptions}.\n */\nexport interface SecureRouteSecurityOptions {\n\t/**\n\t * Requirements declared at this route segment.\n\t * @default []\n\t */\n\trequirements?: AuthRequirement[];\n\n\t/**\n\t * How this segment composes with ancestor route requirements.\n\t * @default merge\n\t */\n\tcomposition?: RequirementsClientSetComposition;\n\n\t/**\n\t * Serializable lookup key for a planner-host provided by the root route.\n\t *\n\t * The nearest declared key on the active route chain wins.\n\t */\n\tplannerHostKey?: string;\n}\n\n/**\n * Root-level runtime policy for a secured route tree.\n *\n * Everything here is intentionally non-serializable and lives outside route\n * metadata. Child `secureRoute()` calls only reference these policies by\n * serializable keys / requirement ids.\n */\nexport interface SecureRouteRootSecurityOptions\n\textends SecureRouteSecurityOptions,\n\t\tOmit<\n\t\t\tCreateTokenSetRouteAggregationGuardOptions,\n\t\t\t\"plannerHost\" | \"plannerHostResolver\"\n\t\t> {\n\t/**\n\t * Named planner-host instances available to the secured route tree.\n\t */\n\tplannerHosts?: Record<string, PlannerHost>;\n\n\t/**\n\t * Fallback planner-host key when no route segment declares one.\n\t */\n\tdefaultPlannerHostKey?: string;\n}\n\ntype SecureRouteOptions = Omit<Route, \"path\" | \"data\"> & {\n\tdata?: Record<string, unknown>;\n};\n\n/**\n * Build a secured child route declaration.\n *\n * The returned route contains only serializable metadata:\n * requirements, composition strategy, planner-host lookup key, and any extra\n * route data the caller provides.\n */\nexport function secureRoute(\n\tpath: string,\n\tsecurityOptions: SecureRouteSecurityOptions = {},\n\trouteOptions: SecureRouteOptions = {},\n): Route {\n\tconst data = withRouteRequirements(securityOptions.requirements ?? [], {\n\t\tcomposition: securityOptions.composition,\n\t\textra: {\n\t\t\t...(routeOptions.data ?? {}),\n\t\t\t...(securityOptions.plannerHostKey\n\t\t\t\t? {\n\t\t\t\t\t\t[TOKEN_SET_ROUTE_PLANNER_HOST_KEY_DATA_KEY]:\n\t\t\t\t\t\t\tsecurityOptions.plannerHostKey,\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t},\n\t});\n\n\treturn {\n\t\t...routeOptions,\n\t\tpath,\n\t\tdata,\n\t};\n}\n\n/**\n * Build a secured route-tree root.\n *\n * This is the canonical Angular adopter entry for token-set route security:\n * root-level runtime policy stays here, child routes use {@link secureRoute}\n * for serializable declarations only.\n */\nexport function secureRouteRoot(\n\tpath: string,\n\tsecurityOptions: SecureRouteRootSecurityOptions = {},\n\trouteOptions: SecureRouteOptions = {},\n): Route {\n\tconst canActivate = createTokenSetRouteAggregationGuard({\n\t\trequirementPolicies: securityOptions.requirementPolicies,\n\t\trequirementHandlers: securityOptions.requirementHandlers,\n\t\tdefaultOnUnauthenticated: securityOptions.defaultOnUnauthenticated,\n\t\trequirementsKey: securityOptions.requirementsKey,\n\t\tplannerHostResolver: (route, state) =>\n\t\t\tresolvePlannerHostForRoute(route, state, securityOptions),\n\t});\n\tconst canActivateChild = createCanActivateChildAdapter(canActivate);\n\tconst baseRoute = secureRoute(path, securityOptions, routeOptions);\n\n\treturn {\n\t\t...baseRoute,\n\t\tcanActivate: [canActivate, ...(routeOptions.canActivate ?? [])],\n\t\tcanActivateChild: [\n\t\t\tcanActivateChild,\n\t\t\t...(routeOptions.canActivateChild ?? []),\n\t\t],\n\t};\n}\n\n/**\n * Alias with a token-set-specific name for callers who prefer explicit imports.\n */\nexport const secureTokenSetRoute = secureRoute;\n\n/**\n * Alias with a token-set-specific name for callers who prefer explicit imports.\n */\nexport const secureTokenSetRouteRoot = secureRouteRoot;\n\nfunction createCanActivateChildAdapter(\n\tcanActivate: ReturnType<typeof createTokenSetRouteAggregationGuard>,\n): CanActivateChildFn {\n\treturn (childRoute, state) => canActivate(childRoute, state);\n}\n\nfunction resolvePlannerHostForRoute(\n\troute: ActivatedRouteSnapshot,\n\t_state: RouterStateSnapshot,\n\tsecurityOptions: SecureRouteRootSecurityOptions,\n): PlannerHost | undefined {\n\tconst plannerHostKey =\n\t\tfindNearestPlannerHostKey(route) ?? securityOptions.defaultPlannerHostKey;\n\n\tif (plannerHostKey) {\n\t\tconst resolved = securityOptions.plannerHosts?.[plannerHostKey];\n\t\tif (!resolved) {\n\t\t\tthrow new Error(\n\t\t\t\t`[secureRouteRoot] No planner host registered for key \"${plannerHostKey}\"`,\n\t\t\t);\n\t\t}\n\t\treturn resolved;\n\t}\n\n\treturn undefined;\n}\n\nfunction findNearestPlannerHostKey(\n\troute: ActivatedRouteSnapshot,\n): string | undefined {\n\tconst chain = route.pathFromRoot ?? [route];\n\tfor (let index = chain.length - 1; index >= 0; index -= 1) {\n\t\tconst segment = chain[index];\n\t\tif (!segment) continue;\n\t\tconst raw =\n\t\t\tsegment.data[TOKEN_SET_ROUTE_PLANNER_HOST_KEY_DATA_KEY] ??\n\t\t\tsegment.routeConfig?.data?.[TOKEN_SET_ROUTE_PLANNER_HOST_KEY_DATA_KEY];\n\t\tif (typeof raw === \"string\" && raw.length > 0) {\n\t\t\treturn raw;\n\t\t}\n\t}\n\treturn undefined;\n}\n\nexport type { TokenSetRequirementPolicy };\n","import { Component, inject, type OnInit } from \"@angular/core\";\nimport { Router } from \"@angular/router\";\nimport { CallbackResumeService } from \"./callback-resume.service\";\n\n// ============================================================================\n// 9. TokenSetCallbackComponent — drop-in standalone callback route component\n// ============================================================================\n\n/**\n * Drop-in standalone Angular component for OIDC redirect callback routes.\n *\n * Handles the full callback lifecycle on initialization:\n * 1. Calls `CallbackResumeService.handleCallback()` with the current URL.\n * 2. Navigates to `result.resumeUrl` (the post-auth redirect set at login time).\n * 3. On error: logs and falls back to navigating to `\"/\"`.\n *\n * Register as a route component — no additional glue needed:\n * ```ts\n * import { TokenSetCallbackComponent } from \"@securitydept/token-set-context-client-angular\";\n *\n * export const routes: Routes = [\n * { path: \"auth/callback\", component: TokenSetCallbackComponent },\n * ];\n * ```\n *\n * For custom error handling or UI, subclass this component or build your own\n * using `CallbackResumeService` directly.\n */\n@Component({\n\tselector: \"token-set-callback\",\n\tstandalone: true,\n\ttemplate: \"\",\n})\nexport class TokenSetCallbackComponent implements OnInit {\n\tprivate readonly callbackService = inject(CallbackResumeService);\n\tprivate readonly router = inject(Router);\n\n\tngOnInit(): void {\n\t\tconst currentUrl = window.location.href;\n\t\tif (!this.callbackService.isCallback(currentUrl)) {\n\t\t\t// Not a callback URL — navigate home to avoid processing errors.\n\t\t\tthis.router.navigateByUrl(\"/\", { replaceUrl: true });\n\t\t\treturn;\n\t\t}\n\n\t\tthis.callbackService\n\t\t\t.handleCallback(currentUrl)\n\t\t\t.then((result) => {\n\t\t\t\tthis.router.navigateByUrl(result.resumeUrl, { replaceUrl: true });\n\t\t\t})\n\t\t\t.catch((error: unknown) => {\n\t\t\t\tconsole.error(\n\t\t\t\t\t\"[TokenSetCallbackComponent] Callback handling failed:\",\n\t\t\t\t\terror,\n\t\t\t\t);\n\t\t\t\tthis.router.navigateByUrl(\"/\", { replaceUrl: true });\n\t\t\t});\n\t}\n}\n","// Angular integration family for @securitydept/token-set-context-client\n//\n// Canonical import path:\n// import { ... } from \"@securitydept/token-set-context-client-angular\"\n//\n// Multi-client-first Angular-native adapter surface:\n// - Keyed client registration via provideTokenSetAuth()\n// - Multi-client lookup via TokenSetAuthRegistry\n// - Per-client TokenSetAuthService with signal + RxJS bridge\n// - Multi-client bearer interceptor (functional + class-based)\n// - provideTokenSetBearerInterceptor() for NgModule HTTP_INTERCEPTORS setup\n// - OIDC callback resume with client key discrimination\n// - CallbackResumeService.isCallback() for programmatic callback detection\n// - TokenSetCallbackComponent standalone component (drop-in callback route)\n// - Requirement/provider-family → client key mapping\n// - secureRouteRoot() / secureRoute() — canonical Angular Router route-security\n// builders: keep non-serializable runtime policy at the root, let child\n// routes declare serializable requirement metadata only, and wire both\n// canActivate + canActivateChild to the full-route aggregation guard.\n// - createTokenSetRouteAggregationGuard() — lower-level advanced guard for\n// direct route-tree evaluation when adopters intentionally bypass the route\n// builder helpers.\n// - Signal/Observable bridge utilities live in @securitydept/client-angular\n//\n// Built by ng-packagr (APF / FESM2022). Decorators are fully supported.\n//\n// Stability: provisional (framework adapter)\n\nexport * from \"./bearer-interceptor\";\nexport * from \"./callback-resume.service\";\nexport * from \"./contracts\";\nexport * from \"./guard-types\";\nexport * from \"./oidc-callback-url\";\nexport * from \"./provide-token-set-auth\";\nexport * from \"./route-aggregation-guard\";\nexport * from \"./secure-routes\";\nexport * from \"./token-set-auth.service\";\nexport * from \"./token-set-auth-registry\";\nexport * from \"./token-set-callback.component\";\nexport * from \"./tokens\";\n","/*\n * Public API Surface of @securitydept/token-set-context-client-angular\n */\n\nexport * from \"./lib\";\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":"securitydept-token-set-context-client-angular.mjs","sources":["../../src/lib/token-set-auth.service.ts","../../src/lib/token-set-auth-registry.ts","../../src/lib/bearer-interceptor.ts","../../src/lib/callback-resume.service.ts","../../src/lib/oidc-callback-url.ts","../../src/lib/tokens.ts","../../src/lib/provide-token-set-auth.ts","../../src/lib/route-aggregation-guard.ts","../../src/lib/secure-routes.ts","../../src/lib/token-set-callback.component.ts","../../src/lib/index.ts","../../src/public-api.ts","../../src/securitydept-token-set-context-client-angular.ts"],"sourcesContent":["import { signal, type WritableSignal } from \"@angular/core\";\nimport type { EventStreamTrait } from \"@securitydept/client\";\nimport {\n\tbridgeToAngularSignal,\n\tsignalToObservable,\n} from \"@securitydept/client-angular\";\nimport {\n\ttype AuthSnapshot,\n\ttype EnsureAuthForResourceOptions,\n\ttype EnsureAuthForResourceResult,\n\ttype EnsureAuthorizationHeaderOptions,\n\ttype EnsureFreshAuthStateOptions,\n\tgetTokenFreshness,\n\tTokenFreshnessState,\n\ttype TokenSetAuthEvent,\n} from \"@securitydept/token-set-context-client/orchestration\";\nimport type { Observable } from \"rxjs\";\nimport type { TokenSetAngularClient } from \"./contracts\";\n\n// ============================================================================\n// 5. TokenSetAuthService — per-client service wrapper\n// ============================================================================\n\n/**\n * Per-client Angular service that bridges a single OIDC mode client to\n * Angular signals + RxJS.\n *\n * Lifecycle teardown is explicit. Construct with\n * `new TokenSetAuthService(client, autoRestore)` and call `.dispose()` when\n * the service owner tears down. The shared registry core handles this\n * automatically — adopters that wire the service directly (rare) must call\n * `dispose()` themselves, typically bound to a `DestroyRef.onDestroy`.\n */\nexport class TokenSetAuthService {\n\t/** Current auth snapshot as an Angular signal. */\n\treadonly authState: WritableSignal<AuthSnapshot | null>;\n\t/** Current auth snapshot as an RxJS Observable. */\n\treadonly authState$: Observable<AuthSnapshot | null>;\n\t/** Promise that resolves when initial state restore completes (or null if skipped). */\n\treadonly restorePromise: Promise<AuthSnapshot | null> | null;\n\treadonly authEvents: EventStreamTrait<TokenSetAuthEvent>;\n\n\tprivate readonly cleanup: () => void;\n\tprivate disposed = false;\n\n\tconstructor(\n\t\t/** The underlying SDK client instance. */\n\t\treadonly client: TokenSetAngularClient,\n\t\tautoRestore: boolean,\n\t) {\n\t\tthis.authState = signal<AuthSnapshot | null>(null);\n\t\tthis.cleanup = bridgeToAngularSignal(client.state, this.authState);\n\t\tthis.authState$ = signalToObservable(client.state);\n\t\tthis.authEvents = client.authEvents;\n\t\tthis.restorePromise = autoRestore ? client.restorePersistedState() : null;\n\t}\n\n\t/** Whether the user is currently authenticated. */\n\tisAuthenticated(): boolean {\n\t\treturn this.accessToken() !== null;\n\t}\n\n\t/** Current access token, or null. */\n\taccessToken(): string | null {\n\t\tconst snapshot = this.authState();\n\t\tif (!isFreshOrUsable(snapshot)) {\n\t\t\treturn null;\n\t\t}\n\t\treturn snapshot?.tokens.accessToken ?? null;\n\t}\n\n\tasync ensureFreshAuthState(\n\t\toptions?: EnsureFreshAuthStateOptions,\n\t): Promise<AuthSnapshot | null> {\n\t\treturn await this.client.ensureFreshAuthState(options);\n\t}\n\n\tasync ensureAuthForResource(\n\t\toptions?: EnsureAuthForResourceOptions,\n\t): Promise<EnsureAuthForResourceResult> {\n\t\treturn await this.client.ensureAuthForResource(options);\n\t}\n\n\tasync ensureAccessToken(\n\t\toptions?: EnsureFreshAuthStateOptions,\n\t): Promise<string | null> {\n\t\treturn (\n\t\t\t(await this.ensureFreshAuthState(options))?.tokens.accessToken ?? null\n\t\t);\n\t}\n\n\tasync ensureAuthorizationHeader(\n\t\toptions?: EnsureAuthorizationHeaderOptions,\n\t): Promise<string | null> {\n\t\treturn await this.client.ensureAuthorizationHeader(options);\n\t}\n\n\t/**\n\t * Release all resources owned by this service.\n\t *\n\t * - Unsubscribes the SDK state bridge\n\t * - Calls `client.dispose()`\n\t *\n\t * Idempotent: subsequent calls are no-ops. Called by the shared registry\n\t * at teardown; adopters invoking TokenSetAuthService directly must call\n\t * this themselves (typically bound to `DestroyRef.onDestroy`).\n\t */\n\tdispose(): void {\n\t\tif (this.disposed) return;\n\t\tthis.disposed = true;\n\t\ttry {\n\t\t\tthis.cleanup();\n\t\t} catch {\n\t\t\t// Swallow — best effort.\n\t\t}\n\t\ttry {\n\t\t\tthis.client.dispose();\n\t\t} catch {\n\t\t\t// Swallow — best effort.\n\t\t}\n\t}\n}\n\nfunction isFreshOrUsable(snapshot: AuthSnapshot | null): boolean {\n\tconst freshness = getTokenFreshness(snapshot, {\n\t\tnow: Date.now(),\n\t\tclockSkewMs: 30_000,\n\t\trefreshWindowMs: 0,\n\t});\n\treturn (\n\t\tfreshness === TokenFreshnessState.Fresh ||\n\t\tfreshness === TokenFreshnessState.RefreshDue ||\n\t\tfreshness === TokenFreshnessState.NoExpiry\n\t);\n}\n","import { DestroyRef, Injectable, inject } from \"@angular/core\";\nimport type { EventStreamTrait } from \"@securitydept/client\";\nimport type { ClientReadinessState } from \"@securitydept/token-set-context-client/frontend-oidc-mode\";\nimport type {\n\tEnsureAuthForResourceResult,\n\tTokenSetAuthEvent,\n} from \"@securitydept/token-set-context-client/orchestration\";\nimport { attachTokenSetResumeReconciliation } from \"@securitydept/token-set-context-client/orchestration\";\nimport {\n\ttype ClientFilter,\n\ttype ClientKeySelector,\n\ttype ClientMeta,\n\ttype ClientQueryOptions,\n\ttype TokenSetAuthRegistry as CoreTokenSetAuthRegistry,\n\ttype TokenSetClientEntry as CoreTokenSetClientEntry,\n\tcreateTokenSetAuthRegistry,\n\ttype EnsureRegistryAuthForResourceOptions,\n} from \"@securitydept/token-set-context-client/registry\";\nimport type { TokenSetAngularClient, TokenSetClientEntry } from \"./contracts\";\nimport { TokenSetAuthService } from \"./token-set-auth.service\";\n\n// Re-export core registry types so existing adopter imports from\n// @securitydept/token-set-context-client-angular keep working.\nexport type {\n\tClientFilter,\n\tClientKeySelector,\n\tClientMeta,\n\tClientQueryOptions,\n\tEnsureRegistryAuthForResourceOptions,\n} from \"@securitydept/token-set-context-client/registry\";\nexport { ClientInitializationPriority } from \"@securitydept/token-set-context-client/registry\";\n\n// ============================================================================\n// TokenSetAuthRegistry — thin Angular DI wrapper around the framework-neutral\n// core at @securitydept/token-set-context-client/registry\n//\n// The Angular registry delegates multi-client state to a\n// `createTokenSetAuthRegistry()` core and\n// supplies Angular-idiomatic glue:\n// - Injectable scope (root or provider-level)\n// - DestroyRef-bound dispose (envelopes `coreRegistry.dispose()`)\n// - Materialize callback wraps raw clients in `TokenSetAuthService`\n// - access-token sugar routed through the service's signal\n// ============================================================================\n\ntype AngularClient = TokenSetAngularClient;\n\n@Injectable()\nexport class TokenSetAuthRegistry {\n\t/**\n\t * Shared framework-neutral registry core. Angular consumers rarely need\n\t * direct access, but this is exposed so adapter-internal helpers\n\t * (`CallbackResumeService`, `bearer-interceptor`) can query the core\n\t * directly when they don't need Angular-specific behaviour.\n\t */\n\treadonly core: CoreTokenSetAuthRegistry<AngularClient, TokenSetAuthService>;\n\treadonly authEvents: EventStreamTrait<TokenSetAuthEvent>;\n\n\tconstructor() {\n\t\tthis.core = createTokenSetAuthRegistry<AngularClient, TokenSetAuthService>({\n\t\t\tmaterialize: (client, entry) => {\n\t\t\t\tconst angularEntry = entry as TokenSetClientEntry;\n\t\t\t\tconst managedClient = attachTokenSetResumeReconciliation(client, {\n\t\t\t\t\tresumeReconciliation: angularEntry.resumeReconciliation,\n\t\t\t\t\tresumeReconciliationOptions: angularEntry.resumeReconciliationOptions,\n\t\t\t\t});\n\t\t\t\treturn new TokenSetAuthService(\n\t\t\t\t\tmanagedClient,\n\t\t\t\t\tentry.autoRestore ?? true,\n\t\t\t\t);\n\t\t\t},\n\t\t\tdispose: (service) => service.dispose(),\n\t\t\taccessTokenOf: (service) => service.accessToken(),\n\t\t\tensureAccessTokenOf: (service) => service.ensureAccessToken(),\n\t\t\tensureAuthorizationHeaderOf: (service) =>\n\t\t\t\tservice.ensureAuthorizationHeader(),\n\t\t\tensureAuthForResourceOf: (service, options) =>\n\t\t\t\tservice.ensureAuthForResource(options),\n\t\t\tauthEventsOf: (service) => service.authEvents,\n\t\t});\n\t\tthis.authEvents = this.core.authEvents;\n\t\t// Try to bind core.dispose() to the current injection context's\n\t\t// DestroyRef. Registry is typically constructed via DI (where this\n\t\t// always succeeds); pure unit tests instantiate directly and must\n\t\t// call `.dispose()` manually.\n\t\ttry {\n\t\t\tconst destroyRef = inject(DestroyRef);\n\t\t\tdestroyRef.onDestroy(() => this.core.dispose());\n\t\t} catch {\n\t\t\t// Not in an injection context — adopter is expected to call\n\t\t\t// registry.dispose() manually (or never, for short-lived tests).\n\t\t}\n\t}\n\n\t/**\n\t * Register a client entry. Supports sync / async / lazy priority.\n\t *\n\t * The second `destroyRef` argument is no longer\n\t * required — the registry pulls its own `DestroyRef` via injection and\n\t * binds teardown once per Angular scope.\n\t */\n\tregister(\n\t\tentry: TokenSetClientEntry & {\n\t\t\tpriority?: \"primary\" | \"lazy\";\n\t\t\tclientFactory: () => AngularClient;\n\t\t},\n\t): TokenSetAuthService;\n\tregister(\n\t\tentry: TokenSetClientEntry & {\n\t\t\tpriority?: \"primary\" | \"lazy\";\n\t\t\tclientFactory: () => Promise<AngularClient>;\n\t\t},\n\t): Promise<TokenSetAuthService>;\n\tregister(\n\t\tentry: TokenSetClientEntry,\n\t): TokenSetAuthService | Promise<TokenSetAuthService> | undefined;\n\tregister(\n\t\tentry: TokenSetClientEntry,\n\t): TokenSetAuthService | Promise<TokenSetAuthService> | undefined {\n\t\treturn this.core.register(entry as CoreTokenSetClientEntry<AngularClient>);\n\t}\n\n\t/**\n\t * Manually dispose the registry. Normally bound to the ambient\n\t * `DestroyRef` at construction; call this explicitly when instantiating\n\t * the registry outside of an Angular injection context (e.g. in unit\n\t * tests).\n\t */\n\tdispose(): void {\n\t\tthis.core.dispose();\n\t}\n\n\t// --------------------------------------------------------------------------\n\t// Pass-through API for the earlier wrapper surface\n\t// --------------------------------------------------------------------------\n\n\tisReady(key: string): boolean {\n\t\treturn this.core.isReady(key);\n\t}\n\n\treadinessState(key: string): ClientReadinessState {\n\t\treturn this.core.readinessState(key);\n\t}\n\n\tasync whenReady(key: string): Promise<TokenSetAuthService> {\n\t\treturn this.core.whenReady(key);\n\t}\n\n\t/**\n\t * Preload a lazy client without throwing on rejection. Callers are\n\t * expected to attach `.catch` handlers for fire-and-forget usage.\n\t */\n\tpreload(key: string): Promise<TokenSetAuthService> {\n\t\treturn this.core.preload(key);\n\t}\n\n\t/**\n\t * Schedule preload for every lazy+not-initialized client using\n\t * `requestIdleCallback` / `setTimeout` fallback.\n\t */\n\tidleWarmup(): () => void {\n\t\treturn this.core.idleWarmup();\n\t}\n\n\treset(key: string): void {\n\t\tthis.core.reset(key);\n\t}\n\n\tmetaFor(clientKey: string): ClientMeta | undefined {\n\t\treturn this.core.metaFor(clientKey);\n\t}\n\n\tget(key: string): TokenSetAuthService | undefined {\n\t\treturn this.core.get(key);\n\t}\n\n\trequire(key: string): TokenSetAuthService {\n\t\treturn this.core.require(key);\n\t}\n\n\tkeys(): string[] {\n\t\treturn this.core.keys();\n\t}\n\n\tentries(): Array<[string, TokenSetAuthService]> {\n\t\treturn this.core.entriesSnapshot();\n\t}\n\n\t// ---- URL / callback / requirement / provider-family discrimination -----\n\n\t*clientKeyGenForUrl(url: string): Generator<string, void, unknown> {\n\t\tyield* this.core.clientKeyGenForUrl(url);\n\t}\n\n\t*clientKeyGenForCallback(url: string): Generator<string, void, unknown> {\n\t\tyield* this.core.clientKeyGenForCallback(url);\n\t}\n\n\t*clientKeyGenForRequirement(\n\t\trequirementKind: string,\n\t): Generator<string, void, unknown> {\n\t\tyield* this.core.clientKeyGenForRequirement(requirementKind);\n\t}\n\n\t*clientKeyGenForProviderFamily(\n\t\tproviderFamily: string,\n\t): Generator<string, void, unknown> {\n\t\tyield* this.core.clientKeyGenForProviderFamily(providerFamily);\n\t}\n\n\tclientKeyListForUrl(url: string): string[] {\n\t\treturn this.core.clientKeyListForUrl(url);\n\t}\n\n\tclientKeyListForCallback(url: string): string[] {\n\t\treturn this.core.clientKeyListForCallback(url);\n\t}\n\n\tclientKeyListForRequirement(requirementKind: string): string[] {\n\t\treturn this.core.clientKeyListForRequirement(requirementKind);\n\t}\n\n\tclientKeyListForProviderFamily(providerFamily: string): string[] {\n\t\treturn this.core.clientKeyListForProviderFamily(providerFamily);\n\t}\n\n\tclientKeyForUrl(\n\t\turl: string,\n\t\tselector?: ClientKeySelector,\n\t): string | undefined {\n\t\treturn this.core.clientKeyForUrl(url, selector);\n\t}\n\n\tclientKeyForCallback(\n\t\turl: string,\n\t\tselector?: ClientKeySelector,\n\t): string | undefined {\n\t\treturn this.core.clientKeyForCallback(url, selector);\n\t}\n\n\tclientKeyForRequirement(\n\t\trequirementKind: string,\n\t\tselector?: ClientKeySelector,\n\t): string | undefined {\n\t\treturn this.core.clientKeyForRequirement(requirementKind, selector);\n\t}\n\n\tclientKeyForProviderFamily(\n\t\tproviderFamily: string,\n\t\tselector?: ClientKeySelector,\n\t): string | undefined {\n\t\treturn this.core.clientKeyForProviderFamily(providerFamily, selector);\n\t}\n\n\trequireForRequirement(\n\t\trequirementKind: string,\n\t\tselector?: ClientKeySelector,\n\t): TokenSetAuthService {\n\t\treturn this.core.requireForRequirement(requirementKind, selector);\n\t}\n\n\trequireForProviderFamily(\n\t\tproviderFamily: string,\n\t\tselector?: ClientKeySelector,\n\t): TokenSetAuthService {\n\t\treturn this.core.requireForProviderFamily(providerFamily, selector);\n\t}\n\n\t*clientKeyGenForFilter(\n\t\tfilter: ClientFilter,\n\t): Generator<string, void, unknown> {\n\t\tyield* this.core.clientKeyGenForFilter(filter);\n\t}\n\n\t*clientKeyGenForOptions(\n\t\toptions: ClientQueryOptions,\n\t): Generator<string, void, unknown> {\n\t\tyield* this.core.clientKeyGenForOptions(options);\n\t}\n\n\tclientKeysForOptions(options: ClientQueryOptions): string[] {\n\t\treturn this.core.clientKeysForOptions(options);\n\t}\n\n\taccessToken(key?: string): string | null {\n\t\treturn this.core.accessToken(key);\n\t}\n\n\tasync ensureAccessToken(key?: string): Promise<string | null> {\n\t\treturn await this.core.ensureAccessToken(key);\n\t}\n\n\tasync ensureAuthorizationHeader(key?: string): Promise<string | null> {\n\t\treturn await this.core.ensureAuthorizationHeader(key);\n\t}\n\n\tasync ensureAuthForResource(\n\t\toptions: EnsureRegistryAuthForResourceOptions = {},\n\t): Promise<EnsureAuthForResourceResult | null> {\n\t\treturn await this.core.ensureAuthForResource(options);\n\t}\n}\n","import {\n\tHTTP_INTERCEPTORS,\n\ttype HttpEvent,\n\ttype HttpHandler,\n\ttype HttpInterceptor,\n\ttype HttpRequest,\n} from \"@angular/common/http\";\nimport {\n\tInjectable,\n\tInjectionToken,\n\tinject,\n\ttype Provider,\n} from \"@angular/core\";\nimport { TokenSetAuthFlowSource } from \"@securitydept/token-set-context-client/orchestration\";\nimport { from, type Observable, switchMap } from \"rxjs\";\nimport { TokenSetAuthRegistry } from \"./token-set-auth-registry\";\n\n// ============================================================================\n// 7. Class-based HTTP interceptor + provider factory\n// ============================================================================\n\n/**\n * Adopter-tunable behaviour for the token-set bearer interceptor.\n *\n * `strictUrlMatch` is an explicit option instead of living on the implicit\n * single-client convenience path into an explicit option, so multi-backend\n * adopters can guarantee that bearer tokens never leak outside the URL\n * patterns they registered with `provideTokenSetAuth({ clients: [...] })`.\n */\nexport interface BearerInterceptorOptions {\n\t/**\n\t * When `true`, the interceptor injects a bearer header ONLY for requests\n\t * whose URL matches a registered client's `urlPatterns`. Requests with no\n\t * matching client receive no `Authorization` header even when a token is\n\t * available elsewhere in the registry.\n\t *\n\t * When `false` (default, single-client convenience), the interceptor\n\t * falls back to `registry.accessToken()` for unmatched URLs — useful only\n\t * when the host issues all HTTP traffic to one already-known backend.\n\t *\n\t * Adopters with more than one backend, more than one OIDC audience, or\n\t * any third-party HTTP traffic from the same Angular host MUST set this\n\t * to `true` to prevent cross-origin token leakage.\n\t *\n\t * @default false\n\t */\n\tstrictUrlMatch?: boolean;\n}\n\n/**\n * DI token for {@link BearerInterceptorOptions}. Adopters normally configure\n * options via `provideTokenSetBearerInterceptor({ ... })`; the token is\n * exported for advanced cases where the options need to be supplied or\n * overridden separately (for example, in tests).\n */\nexport const TOKEN_SET_BEARER_INTERCEPTOR_OPTIONS =\n\tnew InjectionToken<BearerInterceptorOptions>(\n\t\t\"TOKEN_SET_BEARER_INTERCEPTOR_OPTIONS\",\n\t);\n\n/**\n * Angular class-based bearer-token interceptor (`HTTP_INTERCEPTORS` style).\n *\n * Selects the correct access token from the multi-client registry by URL\n * pattern. Behaviour for unmatched URLs is governed by\n * {@link BearerInterceptorOptions.strictUrlMatch}.\n *\n * Prefer the functional version via `createTokenSetBearerInterceptor()` +\n * `withInterceptors()` when using `provideHttpClient`. Use this class-based\n * version when you need classic `HTTP_INTERCEPTORS` multi-provider style\n * (e.g. NgModule apps).\n *\n * @example\n * ```ts\n * // NgModule providers:\n * provideTokenSetBearerInterceptor({ strictUrlMatch: true })\n * ```\n */\n@Injectable()\nexport class TokenSetBearerInterceptor implements HttpInterceptor {\n\tprivate readonly registry = inject(TokenSetAuthRegistry);\n\tprivate readonly options: BearerInterceptorOptions =\n\t\tinject(TOKEN_SET_BEARER_INTERCEPTOR_OPTIONS, { optional: true }) ?? {};\n\n\tintercept(\n\t\treq: HttpRequest<unknown>,\n\t\tnext: HttpHandler,\n\t): Observable<HttpEvent<unknown>> {\n\t\tconst key = this.registry.clientKeyForUrl(req.url);\n\t\t// When a client key matches but the client is still initializing\n\t\t// (async clientFactory not yet resolved), registry.get() returns\n\t\t// undefined. This is an explicit design decision: interceptors do NOT\n\t\t// block the HTTP request waiting for client initialization.\n\t\t// Responsibility for \"ensure client is ready before making requests\"\n\t\t// belongs to route guards (which use whenReady() to await readiness).\n\t\t// A not-yet-ready client correctly produces no token → request proceeds\n\t\t// without Authorization header.\n\t\treturn from(\n\t\t\tresolveAuthorizationForRequest(this.registry, req.url, key, {\n\t\t\t\tstrictUrlMatch: this.options.strictUrlMatch,\n\t\t\t}),\n\t\t).pipe(\n\t\t\tswitchMap((authorization) => {\n\t\t\t\tif (!authorization) {\n\t\t\t\t\treturn next.handle(req);\n\t\t\t\t}\n\t\t\t\treturn next.handle(\n\t\t\t\t\treq.clone({ setHeaders: { Authorization: authorization } }),\n\t\t\t\t);\n\t\t\t}),\n\t\t);\n\t}\n}\n\n/**\n * Create an `HTTP_INTERCEPTORS` multi-provider entry for bearer token injection.\n *\n * Returns a single `Provider` object that registers {@link TokenSetBearerInterceptor}\n * as a class-based HTTP interceptor. Suitable for NgModule `providers` arrays.\n *\n * For functional-interceptor style (standalone apps using `provideHttpClient`),\n * use `createTokenSetBearerInterceptor()` with `withInterceptors()` instead.\n *\n * @example\n * ```ts\n * @NgModule({\n * providers: [\n * provideTokenSetAuth({ clients: [...] }),\n * provideTokenSetBearerInterceptor(), // <— replaces hand-written AuthInterceptor\n * provideHttpClient(withInterceptorsFromDi()),\n * ],\n * })\n * export class AppModule {}\n * ```\n */\n/**\n * Create an `HTTP_INTERCEPTORS` multi-provider entry for bearer token injection.\n *\n * Returns Angular providers that register {@link TokenSetBearerInterceptor}\n * as a class-based HTTP interceptor and (optionally) the\n * {@link BearerInterceptorOptions} that govern its URL-match behaviour.\n * Suitable for NgModule `providers` arrays.\n *\n * For functional-interceptor style (standalone apps using `provideHttpClient`),\n * use `createTokenSetBearerInterceptor()` with `withInterceptors()` instead.\n *\n * @example\n * ```ts\n * @NgModule({\n * providers: [\n * provideTokenSetAuth({ clients: [...] }),\n * // Adopters with multiple backends or any third-party HTTP traffic\n * // MUST opt into strictUrlMatch to prevent token leakage.\n * provideTokenSetBearerInterceptor({ strictUrlMatch: true }),\n * provideHttpClient(withInterceptorsFromDi()),\n * ],\n * })\n * export class AppModule {}\n * ```\n */\nexport function provideTokenSetBearerInterceptor(\n\toptions?: BearerInterceptorOptions,\n): Provider[] {\n\tconst providers: Provider[] = [\n\t\t{\n\t\t\tprovide: HTTP_INTERCEPTORS,\n\t\t\tuseClass: TokenSetBearerInterceptor,\n\t\t\tmulti: true,\n\t\t},\n\t];\n\tif (options) {\n\t\tproviders.push({\n\t\t\tprovide: TOKEN_SET_BEARER_INTERCEPTOR_OPTIONS,\n\t\t\tuseValue: options,\n\t\t});\n\t}\n\treturn providers;\n}\n\n/**\n * Create an Angular `HttpInterceptorFn` that injects bearer authorization\n * headers, selecting the correct token from the multi-client registry\n * based on URL pattern matching.\n *\n * If no URL pattern matches, falls back to the first available token\n * (single-client convenience).\n *\n * ## Async client readiness\n *\n * The interceptor does **not** block or delay the HTTP request waiting for\n * an async client to materialize. If a client is still initializing when the\n * request fires, the request proceeds without an Authorization header.\n *\n * This is intentional: blocking HTTP for client initialization would deadlock\n * apps that make HTTP requests during initialization itself. Route guards\n * (which use `registry.whenReady()`) are the correct place to enforce\n * \"client must be ready before user reaches this route\".\n *\n * For NgModule-style apps using `HTTP_INTERCEPTORS`, use\n * `provideTokenSetBearerInterceptor()` instead.\n *\n * @example\n * ```ts\n * import { provideHttpClient, withInterceptors } from \"@angular/common/http\";\n * import { createTokenSetBearerInterceptor } from \"@securitydept/token-set-context-client-angular\";\n *\n * provideHttpClient(withInterceptors([\n * createTokenSetBearerInterceptor(registry),\n * ]));\n * ```\n */\n/**\n * Create an Angular `HttpInterceptorFn` that injects bearer authorization\n * headers, selecting the correct token from the multi-client registry\n * based on URL pattern matching.\n *\n * If no URL pattern matches, the unmatched-URL behaviour is governed by\n * {@link BearerInterceptorOptions.strictUrlMatch}: the default falls back\n * to `registry.accessToken()` (single-client convenience), while\n * `strictUrlMatch: true` returns no token, ensuring bearer headers are\n * never injected for URLs outside the registered patterns.\n *\n * ## Async client readiness\n *\n * The interceptor does **not** block or delay the HTTP request waiting for\n * an async client to materialize. If a client is still initializing when the\n * request fires, the request proceeds without an Authorization header.\n *\n * This is intentional: blocking HTTP for client initialization would deadlock\n * apps that make HTTP requests during initialization itself. Route guards\n * (which use `registry.whenReady()`) are the correct place to enforce\n * \"client must be ready before user reaches this route\".\n *\n * For NgModule-style apps using `HTTP_INTERCEPTORS`, use\n * `provideTokenSetBearerInterceptor()` instead.\n *\n * @example\n * ```ts\n * import { provideHttpClient, withInterceptors } from \"@angular/common/http\";\n * import { createTokenSetBearerInterceptor } from \"@securitydept/token-set-context-client-angular\";\n *\n * provideHttpClient(withInterceptors([\n * createTokenSetBearerInterceptor(registry, { strictUrlMatch: true }),\n * ]));\n * ```\n */\nexport function createTokenSetBearerInterceptor(\n\tregistry: TokenSetAuthRegistry,\n\toptions: BearerInterceptorOptions = {},\n) {\n\treturn (\n\t\treq: {\n\t\t\turl: string;\n\t\t\tclone(update: { setHeaders?: Record<string, string> }): unknown;\n\t\t},\n\t\tnext: (req: unknown) => Observable<unknown>,\n\t): Observable<unknown> => {\n\t\t// Try URL-pattern-based client selection first.\n\t\tconst key = registry.clientKeyForUrl(req.url);\n\t\t// Explicit not-yet-ready semantics: if the client exists in metadata\n\t\t// (via clientKeyForUrl) but registry.get() returns undefined because the\n\t\t// async clientFactory has not resolved yet, the request proceeds without\n\t\t// a token. See class-based interceptor above for full rationale.\n\t\treturn from(\n\t\t\tresolveAuthorizationForRequest(registry, req.url, key, options),\n\t\t).pipe(\n\t\t\tswitchMap((authorization) => {\n\t\t\t\tif (!authorization) {\n\t\t\t\t\treturn next(req);\n\t\t\t\t}\n\t\t\t\treturn next(\n\t\t\t\t\treq.clone({ setHeaders: { Authorization: authorization } }),\n\t\t\t\t);\n\t\t\t}),\n\t\t);\n\t};\n}\n\nasync function resolveAuthorizationForRequest(\n\tregistry: TokenSetAuthRegistry,\n\turl: string,\n\tkey: string | undefined,\n\toptions: BearerInterceptorOptions,\n): Promise<string | null> {\n\tif (!key && options.strictUrlMatch) {\n\t\treturn null;\n\t}\n\n\tconst result = await registry.ensureAuthForResource({\n\t\tkey,\n\t\twaitForReady: false,\n\t\tsource: TokenSetAuthFlowSource.HttpInterceptor,\n\t\tneedsAuthorizationHeader: true,\n\t\tforceRefreshWhenDue: true,\n\t\turl,\n\t});\n\treturn result?.authorizationHeader ?? null;\n}\n","import {\n\tDestroyRef,\n\tInjectable,\n\tinject,\n\tsignal,\n\ttype WritableSignal,\n} from \"@angular/core\";\nimport {\n\tbridgeToAngularSignal,\n\tsignalToObservable,\n} from \"@securitydept/client-angular\";\nimport type { AuthSnapshot } from \"@securitydept/token-set-context-client/orchestration\";\nimport {\n\tTokenSetCallbackResumeController,\n\ttype TokenSetCallbackResumeOptions,\n\ttype TokenSetCallbackResumeResult,\n\ttype TokenSetCallbackResumeState,\n} from \"@securitydept/token-set-context-client/registry\";\nimport type { Observable } from \"rxjs\";\nimport type { TokenSetAuthService } from \"./token-set-auth.service\";\nimport { TokenSetAuthRegistry } from \"./token-set-auth-registry\";\n\n/**\n * Angular-native service for handling OIDC redirect callbacks,\n * with multi-client discrimination support.\n *\n * The post-authentication redirect URI is propagated automatically through\n * the core client's pending state (set via `loginWithRedirect({ postAuthRedirectUri })`\n * and recovered in `handleCallback()`). No manual URL storage is required.\n *\n * @example\n * ```ts\n * @Component({ ... })\n * export class AuthCallbackComponent {\n * private readonly callbackService = inject(CallbackResumeService);\n * private readonly router = inject(Router);\n *\n * async ngOnInit() {\n * const result = await this.callbackService.handleCallback(\n * window.location.href,\n * );\n * this.router.navigateByUrl(result.resumeUrl, { replaceUrl: true });\n * }\n * }\n * ```\n */\n@Injectable()\nexport class CallbackResumeService {\n\tprivate readonly registry = inject(TokenSetAuthRegistry);\n\tprivate readonly destroyRef = inject(DestroyRef, { optional: true });\n\tprivate readonly controller =\n\t\tnew TokenSetCallbackResumeController<TokenSetAuthService>({\n\t\t\tregistry: this.registry.core,\n\t\t\tgetCallbackClient: (service) => service.client,\n\t\t});\n\treadonly state: WritableSignal<TokenSetCallbackResumeState> = signal(\n\t\tthis.controller.getState(),\n\t);\n\treadonly state$: Observable<TokenSetCallbackResumeState> = signalToObservable(\n\t\tthis.controller.state,\n\t);\n\n\tconstructor() {\n\t\tconst cleanup = bridgeToAngularSignal(this.controller.state, this.state);\n\t\tthis.destroyRef?.onDestroy(() => {\n\t\t\tcleanup();\n\t\t\tthis.controller.dispose();\n\t\t});\n\t}\n\n\t/**\n\t * Check whether a URL is an OIDC authorization callback for any registered\n\t * client in this registry.\n\t *\n\t * This is a convenience wrapper over `clientKeyForCallback()`. Use it for\n\t * programmatic early-exit guards (e.g. in a service constructor) before\n\t * calling `handleCallback()`.\n\t *\n\t * @param url - The full URL to check (e.g. `window.location.href`).\n\t * @returns `true` when the URL matches a registered callback path and\n\t * contains an `code` or `error` query parameter.\n\t */\n\tisCallback(url: string): boolean {\n\t\treturn this.controller.isCallback(url);\n\t}\n\n\tgetState(): TokenSetCallbackResumeState {\n\t\treturn this.controller.getState();\n\t}\n\n\tresume(\n\t\toptions: TokenSetCallbackResumeOptions,\n\t): Promise<TokenSetCallbackResumeResult>;\n\tresume(\n\t\tcallbackUrl: string,\n\t\texplicitClientKey?: string,\n\t): Promise<TokenSetCallbackResumeResult>;\n\tresume(\n\t\toptionsOrCallbackUrl: TokenSetCallbackResumeOptions | string,\n\t\texplicitClientKey?: string,\n\t): Promise<TokenSetCallbackResumeResult> {\n\t\treturn this.controller.resume(\n\t\t\ttypeof optionsOrCallbackUrl === \"string\"\n\t\t\t\t? { currentUrl: optionsOrCallbackUrl, clientKey: explicitClientKey }\n\t\t\t\t: optionsOrCallbackUrl,\n\t\t);\n\t}\n\n\t/**\n\t * Handle the OIDC callback, auto-detecting which client the callback\n\t * belongs to via registered callback paths.\n\t *\n\t * The `resumeUrl` in the result is taken directly from the core client's\n\t * pending state (`postAuthRedirectUri` set at `loginWithRedirect()` time),\n\t * falling back to `\"/\"` when no redirect URI was recorded.\n\t *\n\t * @param callbackUrl - The full current URL (e.g. `window.location.href`).\n\t * @param explicitClientKey - Optional: force-select a client key instead of\n\t * auto-detecting from registered callback paths.\n\t */\n\tasync handleCallback(\n\t\tcallbackUrl: string,\n\t\texplicitClientKey?: string,\n\t): Promise<{\n\t\tclientKey: string;\n\t\tsnapshot: AuthSnapshot;\n\t\t/** The URL to navigate to after a successful callback. */\n\t\tresumeUrl: string;\n\t}> {\n\t\tconst result = await this.resume(callbackUrl, explicitClientKey);\n\t\treturn {\n\t\t\tclientKey: result.clientKey,\n\t\t\tsnapshot: result.snapshot,\n\t\t\tresumeUrl: result.postAuthRedirectUri ?? \"/\",\n\t\t};\n\t}\n}\n","// ============================================================================\n// 8. Callback helper — OIDC redirect callback with multi-client discrimination\n// ============================================================================\n\n/**\n * Check whether the current URL is an OIDC authorization callback.\n */\nexport function isOidcCallback(options: {\n\tcurrentUrl: string;\n\tcallbackPath: string;\n}): boolean {\n\ttry {\n\t\tconst url = new URL(options.currentUrl);\n\t\tconst callbackUrl = new URL(options.callbackPath, url.origin);\n\t\treturn (\n\t\t\turl.pathname === callbackUrl.pathname &&\n\t\t\t(url.searchParams.has(\"code\") || url.searchParams.has(\"error\"))\n\t\t);\n\t} catch {\n\t\treturn false;\n\t}\n}\n","import { InjectionToken } from \"@angular/core\";\nimport type { TokenSetAuthRegistry } from \"./token-set-auth-registry\";\n\n/** InjectionToken for the multi-client auth registry. */\nexport const TOKEN_SET_AUTH_REGISTRY = new InjectionToken<TokenSetAuthRegistry>(\n\t\"TOKEN_SET_AUTH_REGISTRY\",\n);\n\nexport type TokenSetCallbackUrlSource = () => string | undefined;\n\nexport interface TokenSetCallbackComponentOptions {\n\tfallbackUrl?: string;\n\terrorRedirectUrl?: string;\n\tonError?: (error: unknown) => void;\n}\n\n/**\n * InjectionToken for the current callback URL used by the drop-in callback component.\n *\n * Defaults to `window.location.href` in real page hosts and `undefined` in\n * non-page or test hosts unless explicitly overridden.\n */\nexport const TOKEN_SET_CALLBACK_CURRENT_URL =\n\tnew InjectionToken<TokenSetCallbackUrlSource>(\n\t\t\"TOKEN_SET_CALLBACK_CURRENT_URL\",\n\t\t{\n\t\t\tprovidedIn: \"root\",\n\t\t\tfactory: () => () => {\n\t\t\t\tconst windowLike = (\n\t\t\t\t\tglobalThis as { window?: { location?: { href?: string } } }\n\t\t\t\t).window;\n\t\t\t\treturn windowLike?.location?.href;\n\t\t\t},\n\t\t},\n\t);\n\nexport const TOKEN_SET_CALLBACK_COMPONENT_OPTIONS =\n\tnew InjectionToken<TokenSetCallbackComponentOptions>(\n\t\t\"TOKEN_SET_CALLBACK_COMPONENT_OPTIONS\",\n\t\t{\n\t\t\tprovidedIn: \"root\",\n\t\t\tfactory: () => ({\n\t\t\t\tfallbackUrl: \"/\",\n\t\t\t\terrorRedirectUrl: \"/\",\n\t\t\t}),\n\t\t},\n\t);\n","import {\n\ttype EnvironmentProviders,\n\tinject,\n\ttype Provider,\n\tprovideEnvironmentInitializer,\n} from \"@angular/core\";\nimport { CallbackResumeService } from \"./callback-resume.service\";\nimport type { TokenSetClientEntry } from \"./contracts\";\nimport { TokenSetAuthRegistry } from \"./token-set-auth-registry\";\nimport { TOKEN_SET_AUTH_REGISTRY } from \"./tokens\";\n\n/**\n * Options for {@link provideTokenSetAuth}.\n */\nexport interface ProvideTokenSetAuthOptions {\n\t/**\n\t * Adapter/host registration entries.\n\t *\n\t * Each entry must have a unique `key`. The entry itself owns how one client\n\t * composes auth-context config and runtime capabilities; Angular DI here only\n\t * owns host registration, readiness wiring, and lifecycle.\n\t */\n\tclients: TokenSetClientEntry[];\n\t/**\n\t * When true (default), schedule `registry.idleWarmup()` during Angular\n\t * environment initialization so `priority: \"lazy\"` clients get\n\t * preloaded in the browser's idle callback.\n\t *\n\t * Disable for tests that want deterministic, manual materialization\n\t * control.\n\t *\n\t * @default true\n\t */\n\tidleWarmup?: boolean;\n}\n\n/**\n * Create Angular providers for the multi-client token-set auth integration.\n *\n * Registers all services into Angular DI so adopters can directly inject\n * `TokenSetAuthRegistry`, `CallbackResumeService`, and look up individual\n * `TokenSetAuthService` instances by key.\n *\n * ## Async client initialization\n *\n * When a client's `clientFactory` returns a `Promise` (e.g. because it\n * needs to fetch a config projection from a backend endpoint), the\n * registry tracks its initialization state automatically. Guards and\n * interceptors should use `registry.whenReady(key)` to await\n * materialization before accessing the service.\n *\n * Adapter-managed browser clients also install token-set page-resume\n * reconciliation by default during materialization. Set\n * `resumeReconciliation: false` on an individual client entry only when the\n * host intentionally owns equivalent browser lifecycle wiring.\n *\n * @example\n * ```ts\n * import { provideTokenSetAuth } from \"@securitydept/token-set-context-client-angular\";\n * import { resolveConfigProjection, networkConfigSource, createFrontendOidcModeClient }\n * from \"@securitydept/token-set-context-client/frontend-oidc-mode\";\n *\n * export const appConfig = {\n * providers: [\n * provideTokenSetAuth({\n * clients: [\n * {\n * key: \"main\",\n * // Async clientFactory: fetches config projection from backend\n * clientFactory: async () => {\n * const resolved = await resolveConfigProjection([\n * networkConfigSource({\n * apiEndpoint: \"https://api.example.com/api\",\n * redirectUri: `${location.origin}/auth/callback`,\n * }),\n * ]);\n * return createFrontendOidcModeClient(resolved.config, runtime);\n * },\n * urlPatterns: [\"/api/\"],\n * callbackPath: \"/auth/callback\",\n * // Optional: opt out only if the host already wires equivalent\n * // resume reconciliation for this browser client.\n * // resumeReconciliation: false,\n * },\n * ],\n * }),\n * ],\n * };\n * ```\n */\nexport function provideTokenSetAuth(\n\toptions: ProvideTokenSetAuthOptions,\n): (Provider | EnvironmentProviders)[] {\n\treturn [\n\t\t// Registry is a singleton — all clients share one registry.\n\t\t{\n\t\t\tprovide: TokenSetAuthRegistry,\n\t\t\tuseFactory: () => new TokenSetAuthRegistry(),\n\t\t},\n\t\t// Eagerly register all client entries during environment initialization.\n\t\t// provideEnvironmentInitializer runs in injection context, so inject() works.\n\t\t// Note: async clientFactories create promises that the registry tracks;\n\t\t// the initializer does not block Angular bootstrap on them (guards/interceptors\n\t\t// use registry.whenReady() instead).\n\t\tprovideEnvironmentInitializer(() => {\n\t\t\tconst registry = inject(TokenSetAuthRegistry);\n\t\t\tfor (const entry of options.clients) {\n\t\t\t\tregistry.register(entry);\n\t\t\t}\n\t\t\tif (options.idleWarmup !== false) {\n\t\t\t\t// Kick off idle-time preload for any lazy clients. The cancel\n\t\t\t\t// handle is intentionally discarded — teardown happens through\n\t\t\t\t// the registry's own DestroyRef binding at dispose time.\n\t\t\t\tregistry.idleWarmup();\n\t\t\t}\n\t\t}),\n\t\t// Token for direct registry access.\n\t\t{\n\t\t\tprovide: TOKEN_SET_AUTH_REGISTRY,\n\t\t\tuseExisting: TokenSetAuthRegistry,\n\t\t},\n\t\t// Callback resume service.\n\t\tCallbackResumeService,\n\t];\n}\n","import {\n\tEnvironmentInjector,\n\tinject,\n\trunInInjectionContext,\n} from \"@angular/core\";\nimport {\n\ttype ActivatedRouteSnapshot,\n\ttype CanActivateFn,\n\tRouter,\n\ttype RouterStateSnapshot,\n\ttype UrlTree,\n} from \"@angular/router\";\nimport type {\n\tAuthGuardClientOption,\n\tAuthRequirement,\n\tPlannerHost,\n} from \"@securitydept/client/auth-coordination\";\nimport { assertResolveEnvironment } from \"@securitydept/client/web\";\nimport {\n\tAUTH_PLANNER_HOST,\n\textractFullRouteRequirements,\n\tPAGE_CLIENT_ENVIRONMENT,\n\ttype PageClientEnvironmentSource,\n\tresolvePageClientEnvironmentSource,\n} from \"@securitydept/client-angular\";\nimport {\n\ttype AuthSnapshot,\n\tTokenSetAuthFlowSource,\n} from \"@securitydept/token-set-context-client/orchestration\";\nimport type {\n\tOidcRedirectLoginClient,\n\tOidcRedirectLoginOptions,\n} from \"@securitydept/token-set-context-client/registry\";\nimport { firstValueFrom, from, switchMap, take } from \"rxjs\";\nimport type { UnauthenticatedEntry } from \"./guard-types\";\nimport type { ClientMeta, ClientQueryOptions } from \"./token-set-auth-registry\";\nimport { TokenSetAuthRegistry } from \"./token-set-auth-registry\";\n\n// ============================================================================\n// Guard factory — createTokenSetRouteAggregationGuard() [LOWER-LEVEL ADVANCED]\n//\n// Canonical Angular Router auth pattern for multi-requirement coordination:\n//\n// 1. Declaration layer — withRouteRequirements([...]) in route data\n// 2. Aggregation layer — this guard walks pathFromRoot once and collects\n// the full requirement chain\n// 3. Decision layer — planner host evaluates all candidates in one call\n// 4. Enforcement layer — registry maps requirement.kind → client → action\n//\n// Fine-grained control is available via requirementPolicies[requirementId]:\n// - selector.clientKey / selector.query override the default kind→client mapping\n// - per-requirement onUnauthenticated takes precedence over kind handlers\n//\n// secureRouteRoot() / secureRoute() are the canonical adopter-facing path.\n// This guard remains available as the lower-level route-tree primitive.\n// ============================================================================\n\n// ---------------------------------------------------------------------------\n// Selector type — used in requirementPolicies to override client resolution\n// ---------------------------------------------------------------------------\n\n/**\n * How to select a token-set client from the registry for a specific requirement.\n *\n * Choose exactly one:\n * - `clientKey` — direct key lookup (explicit, single client)\n * - `query` — composite filter query ({@link ClientQueryOptions})\n */\nexport type TokenSetClientSelector =\n\t| { clientKey: string; query?: never }\n\t| { clientKey?: never; query: ClientQueryOptions };\n\n/**\n * Runtime context passed to unauthenticated route handlers.\n *\n * `attemptedUrl` is the Angular Router target URL for the navigation being\n * guarded. Use it as `postAuthRedirectUri`; do not read `Router.url` for this,\n * because Angular still exposes the currently active URL while a guard is\n * deciding whether the attempted navigation may proceed.\n */\nexport interface TokenSetRouteUnauthenticatedContext {\n\treadonly route: ActivatedRouteSnapshot;\n\treadonly state: RouterStateSnapshot;\n\treadonly attemptedUrl: string;\n}\n\nexport type TokenSetRouteUnauthenticatedHandler = (\n\tunauthenticated: ReadonlyArray<UnauthenticatedEntry>,\n\trequirement: AuthRequirement,\n\tcontext: TokenSetRouteUnauthenticatedContext,\n) => boolean | string | UrlTree | Promise<boolean | string | UrlTree>;\n\n// ---------------------------------------------------------------------------\n// Per-requirement policy\n// ---------------------------------------------------------------------------\n\n/**\n * Policy override for a specific requirement ID in\n * {@link CreateTokenSetRouteAggregationGuardOptions.requirementPolicies}.\n *\n * Allows fine-grained control per declared requirement:\n * - `selector` overrides the default kind→client registry lookup (use when\n * you need a specific client key or a query instead of all clients of that kind).\n * - `onUnauthenticated` overrides both `requirementHandlers[kind]` and\n * `defaultOnUnauthenticated` for this exact requirement.\n *\n * @example\n * ```ts\n * createTokenSetRouteAggregationGuard({\n * requirementPolicies: {\n * \"confluence-oidc\": {\n * selector: { clientKey: \"confluence\" },\n * onUnauthenticated: (_failing, _req, context) => {\n * inject(AuthService)\n * .redirectToLogin(\"confluence\", context.attemptedUrl)\n * .subscribe();\n * return false;\n * },\n * },\n * \"admin-oidc\": {\n * selector: { query: { providerFamily: \"internal-sso\" } },\n * onUnauthenticated: () => \"/admin/login\",\n * },\n * },\n * })\n * ```\n */\nexport interface TokenSetRequirementPolicy {\n\t/**\n\t * Override the default kind→client registry lookup for this requirement.\n\t *\n\t * When omitted, the guard resolves clients via\n\t * `registry.clientKeyListForRequirement(requirement.kind)`.\n\t */\n\tselector?: TokenSetClientSelector;\n\n\t/**\n\t * Handler called when this requirement is selected as pending by the planner.\n\t *\n\t * Takes precedence over `requirementHandlers[kind]` and\n\t * `defaultOnUnauthenticated`.\n\t *\n\t * Return:\n\t * - `true` — allow navigation\n\t * - `false` — block navigation\n\t * - `string` — redirect URL path\n\t * - `UrlTree` — Angular router tree\n\t */\n\tonUnauthenticated: TokenSetRouteUnauthenticatedHandler;\n}\n\n// ---------------------------------------------------------------------------\n// Guard options\n// ---------------------------------------------------------------------------\n\n/**\n * Options for {@link createTokenSetRouteAggregationGuard}.\n */\nexport interface CreateTokenSetRouteAggregationGuardOptions {\n\t/**\n\t * Per-requirement-id policies.\n\t *\n\t * Keys are `AuthRequirement.id` strings (not kind). Policies declared here\n\t * take precedence over {@link requirementHandlers} and\n\t * {@link defaultOnUnauthenticated}.\n\t *\n\t * Use this for fine-grained control:\n\t * - override client selection (specific key or composite query)\n\t * - per-requirement redirect behaviour\n\t * - multi-client / providerFamily selection per requirement\n\t *\n\t * @example\n\t * ```ts\n\t * createTokenSetRouteAggregationGuard({\n\t * requirementPolicies: {\n\t * \"confluence-oidc\": {\n\t * selector: { clientKey: \"confluence\" },\n\t * onUnauthenticated: () => \"/auth/confluence\",\n\t * },\n\t * \"admin-oidc\": {\n\t * selector: { query: { providerFamily: \"internal-sso\" } },\n\t * onUnauthenticated: () => \"/auth/admin\",\n\t * },\n\t * },\n\t * })\n\t * ```\n\t */\n\trequirementPolicies?: Record<string, TokenSetRequirementPolicy>;\n\n\t/**\n\t * Per-requirement-kind handlers.\n\t *\n\t * When the planner selects a pending requirement whose `kind` maps to an\n\t * entry here (and no `requirementPolicies[id]` is set), that handler is\n\t * called instead of `defaultOnUnauthenticated`.\n\t *\n\t * Keys are `AuthRequirement.kind` strings.\n\t *\n\t * @example\n\t * ```ts\n\t * createTokenSetRouteAggregationGuard({\n\t * requirementHandlers: {\n\t * frontend_oidc: (failing, _req, context) => {\n\t * inject(AuthService)\n\t * .redirectToLogin(failing[0].clientKey, context.attemptedUrl)\n\t * .subscribe();\n\t * return false;\n\t * },\n\t * },\n\t * })\n\t * ```\n\t */\n\trequirementHandlers?: Record<string, TokenSetRouteUnauthenticatedHandler>;\n\n\t/**\n\t * Fallback handler used when no `requirementPolicies[id]` or\n\t * `requirementHandlers[kind]` is found for the pending requirement.\n\t */\n\tdefaultOnUnauthenticated?: TokenSetRouteUnauthenticatedHandler;\n\n\t/**\n\t * Custom route data key for reading requirements from route segments.\n\t * @default \"authRequirements\"\n\t */\n\trequirementsKey?: string;\n\n\t/**\n\t * Inline planner-host instance. If provided, used instead of injecting\n\t * `AUTH_PLANNER_HOST` from Angular DI.\n\t */\n\tplannerHost?: PlannerHost;\n\n\t/**\n\t * Resolve the planner-host for the current target route chain.\n\t *\n\t * Use this when planner-host selection depends on route metadata or other\n\t * route-tree state. Takes precedence over `plannerHost` and DI lookup.\n\t */\n\tplannerHostResolver?: (\n\t\troute: ActivatedRouteSnapshot,\n\t\tstate: RouterStateSnapshot,\n\t) => PlannerHost | undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Guard factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create an Angular `CanActivateFn` that performs full-route requirement\n * aggregation and single-pass planner evaluation.\n *\n * **Canonical Angular Router auth pattern** (route-metadata driven):\n * 1. Requirements are declared in route `data` using `withRouteRequirements()`.\n * 2. When the guard runs, it walks `route.pathFromRoot` to collect ALL\n * requirements from every ancestor segment in a single pass.\n * 3. Each requirement's `kind` is mapped to one or more registry clients,\n * unless overridden by a `requirementPolicies[id].selector`.\n * 4. The complete candidate set is handed to the `PlannerHost` for a\n * single `evaluate()` call — the planner selects the first unresolved\n * requirement and calls the matching handler.\n *\n * **Handler resolution order** (most specific wins):\n * 1. `requirementPolicies[requirement.id].onUnauthenticated`\n * 2. `requirementHandlers[requirement.kind]`\n * 3. `defaultOnUnauthenticated`\n * 4. Block navigation (`false`) if none of the above is provided.\n *\n * **Client resolution order** for a requirement:\n * 1. `requirementPolicies[requirement.id].selector.clientKey` — single explicit key\n * 2. `requirementPolicies[requirement.id].selector.query` — composite query\n * 3. `registry.clientKeyListForRequirement(requirement.kind)` — default mapping\n *\n * This model enables:\n * - One-pass requirement collection (no per-segment DI scope accumulation)\n * - One-call planner evaluation (chooser sees the full set at once)\n * - One redirect decision (no per-segment guard coordination needed)\n * - Fine-grained per-requirement policy via `requirementPolicies`\n *\n * @example\n * ```ts\n * // routes.ts — declare requirements in route data\n * import { createBrowserPageClientEnvironment } from \"@securitydept/client/web\";\n * import {\n * providePageClientEnvironment,\n * withRouteRequirements,\n * } from \"@securitydept/client-angular\";\n * import {\n * createTokenSetOidcLoginRedirectHandler,\n * createTokenSetRouteAggregationGuard,\n * } from \"@securitydept/token-set-context-client-angular\";\n *\n * export const appConfig = {\n * providers: [\n * providePageClientEnvironment({\n * environment: createBrowserPageClientEnvironment(),\n * }),\n * ],\n * };\n *\n * // Simple — kind-level handler\n * const guard = createTokenSetRouteAggregationGuard({\n * requirementHandlers: {\n * frontend_oidc: createTokenSetOidcLoginRedirectHandler({\n * clientKey: \"confluence\",\n * }),\n * },\n * });\n *\n * // Fine-grained — per-requirement policy\n * const guard = createTokenSetRouteAggregationGuard({\n * requirementPolicies: {\n * \"confluence-oidc\": {\n * selector: { clientKey: \"confluence\" },\n * onUnauthenticated: () => \"/auth/confluence\",\n * },\n * \"admin-oidc\": {\n * selector: { query: { providerFamily: \"internal-sso\" } },\n * onUnauthenticated: (failing) => \"/auth/admin\",\n * },\n * },\n * });\n *\n * export const routes: Routes = [\n * {\n * path: \"\",\n * data: withRouteRequirements([{ id: \"session\", kind: \"session\" }]),\n * children: [\n * {\n * path: \"confluence\",\n * data: withRouteRequirements([{ id: \"confluence-oidc\", kind: \"frontend_oidc\" }]),\n * canActivate: [guard],\n * },\n * ],\n * },\n * ];\n * ```\n */\nexport function createTokenSetRouteAggregationGuard(\n\toptions: CreateTokenSetRouteAggregationGuardOptions = {},\n): CanActivateFn {\n\treturn async (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {\n\t\tconst injector = inject(EnvironmentInjector);\n\t\tconst registry = inject(TokenSetAuthRegistry);\n\t\tconst router = inject(Router);\n\t\tconst handlerContext: TokenSetRouteUnauthenticatedContext = {\n\t\t\troute,\n\t\t\tstate,\n\t\t\tattemptedUrl: state.url,\n\t\t};\n\n\t\t// ── Resolve planner-host ────────────────────────────────────────────\n\t\tconst plannerHost =\n\t\t\toptions.plannerHostResolver?.(route, state) ??\n\t\t\toptions.plannerHost ??\n\t\t\tinject(AUTH_PLANNER_HOST, { optional: true });\n\n\t\tif (!plannerHost) {\n\t\t\tthrow new Error(\n\t\t\t\t\"[createTokenSetRouteAggregationGuard] No PlannerHost found. \" +\n\t\t\t\t\t\"Provide AUTH_PLANNER_HOST via provideAuthPlannerHost() \" +\n\t\t\t\t\t\"in your app/route providers, or pass plannerHost inline.\",\n\t\t\t);\n\t\t}\n\n\t\t// ── Step 1: Full-route requirement aggregation ───────────────────────\n\t\tconst allRequirements = extractFullRouteRequirements(\n\t\t\troute,\n\t\t\toptions.requirementsKey,\n\t\t);\n\n\t\tif (allRequirements.length === 0) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Per-requirement policy may override the default kind→client mapping.\n\t\tinterface ResolvedRequirementEntry {\n\t\t\trequirement: AuthRequirement;\n\t\t\tentries: UnauthenticatedEntry[];\n\t\t}\n\n\t\tconst resolvedRequirements: ResolvedRequirementEntry[] = [];\n\t\tfor (const req of allRequirements) {\n\t\t\tconst policy = options.requirementPolicies?.[req.id];\n\t\t\tlet clientKeys: string[];\n\n\t\t\tif (policy?.selector) {\n\t\t\t\tif (policy.selector.clientKey) {\n\t\t\t\t\tclientKeys = [policy.selector.clientKey];\n\t\t\t\t} else if (policy.selector.query) {\n\t\t\t\t\tclientKeys = registry.clientKeysForOptions(policy.selector.query);\n\t\t\t\t} else {\n\t\t\t\t\tclientKeys = [];\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Default: resolve by requirement kind.\n\t\t\t\tclientKeys = registry.clientKeyListForRequirement(req.kind);\n\t\t\t}\n\n\t\t\tif (clientKeys.length === 0) {\n\t\t\t\t// Unmapped requirement: leave it unresolved so other requirements can\n\t\t\t\t// coexist in the same route chain without breaking the guard.\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst entries: UnauthenticatedEntry[] = await Promise.all(\n\t\t\t\tclientKeys.map(async (key) => ({\n\t\t\t\t\t// Use whenReady() — not require() — so that if the client's\n\t\t\t\t\t// async clientFactory is still in-flight when the guard first\n\t\t\t\t\t// fires, we block here until it materializes rather than\n\t\t\t\t\t// crashing or silently bypassing auth.\n\t\t\t\t\tservice: await registry.whenReady(key),\n\t\t\t\t\tclientKey: key,\n\t\t\t\t\tmeta: registry.metaFor(key) as ClientMeta,\n\t\t\t\t})),\n\t\t\t);\n\n\t\t\tresolvedRequirements.push({ requirement: req, entries });\n\t\t}\n\n\t\tif (resolvedRequirements.length === 0) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// ── Step 3: Build planner candidates ────────────────────────────────\n\t\t// De-duplicate by requirementId (last declaration for the same id wins).\n\t\tconst seenIds = new Map<string, ResolvedRequirementEntry>();\n\t\tfor (const resolvedRequirement of resolvedRequirements) {\n\t\t\tseenIds.set(resolvedRequirement.requirement.id, resolvedRequirement);\n\t\t}\n\t\tconst deduped = [...seenIds.values()];\n\n\t\tconst candidates: Array<\n\t\t\tAuthGuardClientOption & { _resolved: ResolvedRequirementEntry }\n\t\t> = deduped.map((resolvedRequirement) => ({\n\t\t\trequirementId: resolvedRequirement.requirement.id,\n\t\t\trequirementKind: resolvedRequirement.requirement.kind,\n\t\t\tlabel: resolvedRequirement.requirement.label,\n\t\t\tattributes: resolvedRequirement.requirement.attributes,\n\t\t\tcheckAuthenticated: () =>\n\t\t\t\tresolvedRequirement.entries.every(({ service }) =>\n\t\t\t\t\tservice.isAuthenticated(),\n\t\t\t\t),\n\t\t\tonUnauthenticated: async (): Promise<boolean | string> => {\n\t\t\t\tconst failing = resolvedRequirement.entries.filter(\n\t\t\t\t\t({ service }) => !service.isAuthenticated(),\n\t\t\t\t);\n\t\t\t\tconst handler =\n\t\t\t\t\toptions.requirementPolicies?.[resolvedRequirement.requirement.id]\n\t\t\t\t\t\t?.onUnauthenticated ??\n\t\t\t\t\toptions.requirementHandlers?.[resolvedRequirement.requirement.kind] ??\n\t\t\t\t\toptions.defaultOnUnauthenticated;\n\n\t\t\t\tif (!handler) return false;\n\n\t\t\t\tconst result = runUnauthenticatedHandlerInContext(\n\t\t\t\t\tinjector,\n\t\t\t\t\thandler,\n\t\t\t\t\tfailing,\n\t\t\t\t\tresolvedRequirement.requirement,\n\t\t\t\t\thandlerContext,\n\t\t\t\t);\n\t\t\t\tconst resolved = await result;\n\t\t\t\tif (typeof resolved === \"string\") return resolved;\n\t\t\t\tif (typeof resolved === \"boolean\") return resolved;\n\t\t\t\treturn resolved.toString();\n\t\t\t},\n\t\t\t_resolved: resolvedRequirement,\n\t\t}));\n\n\t\t// ── Step 4: Single planner evaluation ───────────────────────────────\n\t\tconst decide = async (): Promise<boolean | UrlTree> => {\n\t\t\tconst result = await plannerHost.evaluate(candidates);\n\n\t\t\tif (result.allAuthenticated) return true;\n\n\t\t\tif (result.pendingCandidate) {\n\t\t\t\tconst candidate = result.pendingCandidate as AuthGuardClientOption & {\n\t\t\t\t\t_resolved: ResolvedRequirementEntry;\n\t\t\t\t};\n\t\t\t\tconst failing = candidate._resolved.entries.filter(\n\t\t\t\t\t({ service }) => !service.isAuthenticated(),\n\t\t\t\t);\n\t\t\t\t// Handler resolution: policy > kind handler > default\n\t\t\t\tconst handler =\n\t\t\t\t\toptions.requirementPolicies?.[candidate._resolved.requirement.id]\n\t\t\t\t\t\t?.onUnauthenticated ??\n\t\t\t\t\toptions.requirementHandlers?.[candidate._resolved.requirement.kind] ??\n\t\t\t\t\toptions.defaultOnUnauthenticated;\n\n\t\t\t\tif (!handler) return false;\n\n\t\t\t\tconst action = await runUnauthenticatedHandlerInContext(\n\t\t\t\t\tinjector,\n\t\t\t\t\thandler,\n\t\t\t\t\tfailing,\n\t\t\t\t\tcandidate._resolved.requirement,\n\t\t\t\t\thandlerContext,\n\t\t\t\t);\n\t\t\t\tif (typeof action === \"string\") return router.parseUrl(action);\n\t\t\t\tif (typeof action === \"boolean\") return action;\n\t\t\t\treturn action;\n\t\t\t}\n\n\t\t\treturn false;\n\t\t};\n\n\t\t// ── Restore-aware: wait for all pending restores ─────────────────────\n\t\tconst pendingRestores = deduped\n\t\t\t.flatMap((r) => r.entries)\n\t\t\t.map(({ service }) => service.restorePromise)\n\t\t\t.filter((p): p is Promise<AuthSnapshot | null> => p !== null);\n\t\tasync function awaitAuthMaterial(): Promise<void> {\n\t\t\tawait Promise.all(pendingRestores);\n\t\t\tconst freshnessBarriers = deduped\n\t\t\t\t.flatMap((r) =>\n\t\t\t\t\tr.entries.map((entry) => ({\n\t\t\t\t\t\t...entry,\n\t\t\t\t\t\trequirement: r.requirement,\n\t\t\t\t\t})),\n\t\t\t\t)\n\t\t\t\t.map(({ service, clientKey, meta, requirement }) =>\n\t\t\t\t\tservice.ensureAuthForResource({\n\t\t\t\t\t\tsource: TokenSetAuthFlowSource.RouteGuard,\n\t\t\t\t\t\tclientKey,\n\t\t\t\t\t\trequirement: { id: requirement.id, kind: requirement.kind },\n\t\t\t\t\t\tproviderFamily: meta.providerFamily,\n\t\t\t\t\t\turl: state.url,\n\t\t\t\t\t\tforceRefreshWhenDue: true,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\tawait Promise.all(freshnessBarriers);\n\t\t}\n\n\t\tif (deduped.every((r) => r.entries.length === 0)) {\n\t\t\treturn decide();\n\t\t}\n\n\t\t// await the restore-phase Observable before running planner evaluation\n\t\t// so the async guard always returns a Promise, not an Observable.\n\t\treturn firstValueFrom(\n\t\t\tfrom(awaitAuthMaterial()).pipe(\n\t\t\t\tswitchMap(() => from(decide())),\n\t\t\t\ttake(1),\n\t\t\t),\n\t\t);\n\t};\n}\n\nfunction runUnauthenticatedHandlerInContext(\n\tinjector: EnvironmentInjector,\n\thandler: TokenSetRouteUnauthenticatedHandler,\n\tunauthenticated: ReadonlyArray<UnauthenticatedEntry>,\n\trequirement: AuthRequirement,\n\tcontext: TokenSetRouteUnauthenticatedContext,\n): boolean | string | UrlTree | Promise<boolean | string | UrlTree> {\n\treturn runInInjectionContext(injector, () =>\n\t\thandler(unauthenticated, requirement, context),\n\t);\n}\n\n/**\n * Build an Angular route-security handler that starts OIDC redirect login and\n * records the attempted navigation URL as `postAuthRedirectUri`.\n *\n * Use this instead of reading `Router.url` inside a guard handler. During guard\n * execution Angular has not committed the attempted navigation yet, so\n * `Router.url` still points at the previously active page.\n *\n * After the browser redirect is started, the returned guard result intentionally\n * never settles. That keeps Angular from finalizing the rejected in-app\n * navigation while the page is already leaving for an external IdP.\n */\nexport interface CreateTokenSetOidcLoginRedirectHandlerOptions {\n\t/**\n\t * Explicit client key. When omitted, the first failing registry entry is\n\t * used, which is the common one-client-per-requirement case.\n\t */\n\tclientKey?: string;\n\t/**\n\t * Optional stable page environment override.\n\t *\n\t * The canonical Angular path is to provide a provider-scoped\n\t * `ClientEnvironmentService`, or another stable resolver, once from the host\n\t * composition root with `providePageClientEnvironment(...)`, then let the\n\t * redirect helper resolve it from Angular DI. Use this override only when a\n\t * specific handler intentionally needs a different stable page environment\n\t * source.\n\t */\n\tenvironment?: PageClientEnvironmentSource;\n\t/**\n\t * Fallback used only when Angular does not provide a target URL.\n\t * @default \"/\"\n\t */\n\tfallbackPostAuthRedirectUri?: string;\n}\n\nexport function createTokenSetOidcLoginRedirectHandler(\n\toptions: CreateTokenSetOidcLoginRedirectHandlerOptions = {},\n): TokenSetRouteUnauthenticatedHandler {\n\treturn async (unauthenticated, _requirement, context) => {\n\t\tconst clientKey = options?.clientKey ?? unauthenticated[0]?.clientKey;\n\t\tif (!clientKey) return false;\n\t\tconst environmentPromise = resolveOidcRouteEnvironment(\n\t\t\toptions?.environment,\n\t\t);\n\n\t\tconst registry = inject(TokenSetAuthRegistry);\n\t\tconst service = await registry.whenReady(clientKey);\n\t\tif (!isLoginWithRedirectClient(service.client)) {\n\t\t\tfailMissingOidcRedirectLoginCapability(clientKey);\n\t\t}\n\t\tconst environment = await environmentPromise;\n\n\t\tawait service.client.loginWithRedirect({\n\t\t\tenvironment,\n\t\t\tpostAuthRedirectUri:\n\t\t\t\tcontext.attemptedUrl || options?.fallbackPostAuthRedirectUri || \"/\",\n\t\t});\n\t\treturn await neverSettlingRedirectGuardResult();\n\t};\n}\n\nfunction neverSettlingRedirectGuardResult(): Promise<never> {\n\treturn new Promise<never>(() => {\n\t\t// Intentionally never resolves: a full-page browser redirect is in progress.\n\t});\n}\n\nfunction resolveOidcRouteEnvironment(\n\tenvironmentOverride:\n\t\t| CreateTokenSetOidcLoginRedirectHandlerOptions[\"environment\"]\n\t\t| undefined,\n): Promise<OidcRedirectLoginOptions[\"environment\"]> {\n\treturn resolvePageClientEnvironmentSource(\n\t\tassertResolveEnvironment(\n\t\t\tenvironmentOverride ??\n\t\t\t\tinject(PAGE_CLIENT_ENVIRONMENT, { optional: true }),\n\t\t\tfailMissingOidcRouteEnvironment,\n\t\t),\n\t\tfailMissingOidcRouteEnvironment,\n\t);\n}\n\nfunction failMissingOidcRouteEnvironment(): never {\n\tthrow new Error(\n\t\t\"createTokenSetOidcLoginRedirectHandler requires an explicit environment.\\n\" +\n\t\t\t\"Provide it once from the Angular composition root with providePageClientEnvironment({ environment }), where environment is a provider-scoped ClientEnvironmentService or another stable resolver.\\n\" +\n\t\t\t\"or pass a stable environment override with createTokenSetOidcLoginRedirectHandler({ environment: ... }).\",\n\t);\n}\n\nfunction failMissingOidcRedirectLoginCapability(clientKey: string): never {\n\tthrow new Error(\n\t\t`createTokenSetOidcLoginRedirectHandler requires client key \"${clientKey}\" to implement OidcRedirectLoginClient.loginWithRedirect(options).`,\n\t);\n}\n\nfunction isLoginWithRedirectClient(\n\tclient: unknown,\n): client is OidcRedirectLoginClient {\n\treturn (\n\t\ttypeof client === \"object\" &&\n\t\tclient !== null &&\n\t\t\"loginWithRedirect\" in client &&\n\t\ttypeof client.loginWithRedirect === \"function\"\n\t);\n}\n","import type {\n\tActivatedRouteSnapshot,\n\tCanActivateChildFn,\n\tRoute,\n\tRouterStateSnapshot,\n} from \"@angular/router\";\nimport type {\n\tAuthRequirement,\n\tPlannerHost,\n\tRequirementsClientSetComposition,\n} from \"@securitydept/client/auth-coordination\";\nimport { withRouteRequirements } from \"@securitydept/client-angular\";\nimport type {\n\tCreateTokenSetRouteAggregationGuardOptions,\n\tTokenSetRequirementPolicy,\n} from \"./route-aggregation-guard\";\nimport { createTokenSetRouteAggregationGuard } from \"./route-aggregation-guard\";\n\n/**\n * Route data key for choosing a planner-host registered at the route root.\n */\nexport const TOKEN_SET_ROUTE_PLANNER_HOST_KEY_DATA_KEY =\n\t\"tokenSetPlannerHostKey\";\n\n/**\n * Serializable route-level security declaration.\n *\n * This object is safe to embed in Angular route metadata. Non-serializable\n * runtime policy (planner host instances, callbacks, chooser implementations)\n * belongs in {@link SecureRouteRootSecurityOptions}.\n */\nexport interface SecureRouteSecurityOptions {\n\t/**\n\t * Requirements declared at this route segment.\n\t * @default []\n\t */\n\trequirements?: AuthRequirement[];\n\n\t/**\n\t * How this segment composes with ancestor route requirements.\n\t * @default merge\n\t */\n\tcomposition?: RequirementsClientSetComposition;\n\n\t/**\n\t * Serializable lookup key for a planner-host provided by the root route.\n\t *\n\t * The nearest declared key on the active route chain wins.\n\t */\n\tplannerHostKey?: string;\n}\n\n/**\n * Root-level runtime policy for a secured route tree.\n *\n * Everything here is intentionally non-serializable and lives outside route\n * metadata. Child `secureRoute()` calls only reference these policies by\n * serializable keys / requirement ids.\n */\nexport interface SecureRouteRootSecurityOptions\n\textends SecureRouteSecurityOptions,\n\t\tOmit<\n\t\t\tCreateTokenSetRouteAggregationGuardOptions,\n\t\t\t\"plannerHost\" | \"plannerHostResolver\"\n\t\t> {\n\t/**\n\t * Named planner-host instances available to the secured route tree.\n\t */\n\tplannerHosts?: Record<string, PlannerHost>;\n\n\t/**\n\t * Fallback planner-host key when no route segment declares one.\n\t */\n\tdefaultPlannerHostKey?: string;\n}\n\ntype SecureRouteOptions = Omit<Route, \"path\" | \"data\"> & {\n\tdata?: Record<string, unknown>;\n};\n\n/**\n * Build a secured child route declaration.\n *\n * The returned route contains only serializable metadata:\n * requirements, composition strategy, planner-host lookup key, and any extra\n * route data the caller provides.\n */\nexport function secureRoute(\n\tpath: string,\n\tsecurityOptions: SecureRouteSecurityOptions = {},\n\trouteOptions: SecureRouteOptions = {},\n): Route {\n\tconst data = withRouteRequirements(securityOptions.requirements ?? [], {\n\t\tcomposition: securityOptions.composition,\n\t\textra: {\n\t\t\t...(routeOptions.data ?? {}),\n\t\t\t...(securityOptions.plannerHostKey\n\t\t\t\t? {\n\t\t\t\t\t\t[TOKEN_SET_ROUTE_PLANNER_HOST_KEY_DATA_KEY]:\n\t\t\t\t\t\t\tsecurityOptions.plannerHostKey,\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t},\n\t});\n\n\treturn {\n\t\t...routeOptions,\n\t\tpath,\n\t\tdata,\n\t};\n}\n\n/**\n * Build a secured route-tree root.\n *\n * This is the canonical Angular adopter entry for token-set route security:\n * root-level runtime policy stays here, child routes use {@link secureRoute}\n * for serializable declarations only.\n */\nexport function secureRouteRoot(\n\tpath: string,\n\tsecurityOptions: SecureRouteRootSecurityOptions = {},\n\trouteOptions: SecureRouteOptions = {},\n): Route {\n\tconst canActivate = createTokenSetRouteAggregationGuard({\n\t\trequirementPolicies: securityOptions.requirementPolicies,\n\t\trequirementHandlers: securityOptions.requirementHandlers,\n\t\tdefaultOnUnauthenticated: securityOptions.defaultOnUnauthenticated,\n\t\trequirementsKey: securityOptions.requirementsKey,\n\t\tplannerHostResolver: (route, state) =>\n\t\t\tresolvePlannerHostForRoute(route, state, securityOptions),\n\t});\n\tconst canActivateChild = createCanActivateChildAdapter(canActivate);\n\tconst baseRoute = secureRoute(path, securityOptions, routeOptions);\n\n\treturn {\n\t\t...baseRoute,\n\t\tcanActivate: [canActivate, ...(routeOptions.canActivate ?? [])],\n\t\tcanActivateChild: [\n\t\t\tcanActivateChild,\n\t\t\t...(routeOptions.canActivateChild ?? []),\n\t\t],\n\t};\n}\n\n/**\n * Alias with a token-set-specific name for callers who prefer explicit imports.\n */\nexport const secureTokenSetRoute = secureRoute;\n\n/**\n * Alias with a token-set-specific name for callers who prefer explicit imports.\n */\nexport const secureTokenSetRouteRoot = secureRouteRoot;\n\nfunction createCanActivateChildAdapter(\n\tcanActivate: ReturnType<typeof createTokenSetRouteAggregationGuard>,\n): CanActivateChildFn {\n\treturn (childRoute, state) => canActivate(childRoute, state);\n}\n\nfunction resolvePlannerHostForRoute(\n\troute: ActivatedRouteSnapshot,\n\t_state: RouterStateSnapshot,\n\tsecurityOptions: SecureRouteRootSecurityOptions,\n): PlannerHost | undefined {\n\tconst plannerHostKey =\n\t\tfindNearestPlannerHostKey(route) ?? securityOptions.defaultPlannerHostKey;\n\n\tif (plannerHostKey) {\n\t\tconst resolved = securityOptions.plannerHosts?.[plannerHostKey];\n\t\tif (!resolved) {\n\t\t\tthrow new Error(\n\t\t\t\t`[secureRouteRoot] No planner host registered for key \"${plannerHostKey}\"`,\n\t\t\t);\n\t\t}\n\t\treturn resolved;\n\t}\n\n\treturn undefined;\n}\n\nfunction findNearestPlannerHostKey(\n\troute: ActivatedRouteSnapshot,\n): string | undefined {\n\tconst chain = route.pathFromRoot ?? [route];\n\tfor (let index = chain.length - 1; index >= 0; index -= 1) {\n\t\tconst segment = chain[index];\n\t\tif (!segment) continue;\n\t\tconst raw =\n\t\t\tsegment.data[TOKEN_SET_ROUTE_PLANNER_HOST_KEY_DATA_KEY] ??\n\t\t\tsegment.routeConfig?.data?.[TOKEN_SET_ROUTE_PLANNER_HOST_KEY_DATA_KEY];\n\t\tif (typeof raw === \"string\" && raw.length > 0) {\n\t\t\treturn raw;\n\t\t}\n\t}\n\treturn undefined;\n}\n\nexport type { TokenSetRequirementPolicy };\n","import { Component, inject, type OnInit } from \"@angular/core\";\nimport { Router } from \"@angular/router\";\nimport { CallbackResumeService } from \"./callback-resume.service\";\nimport {\n\tTOKEN_SET_CALLBACK_COMPONENT_OPTIONS,\n\tTOKEN_SET_CALLBACK_CURRENT_URL,\n} from \"./tokens\";\n\n// ============================================================================\n// 9. TokenSetCallbackComponent — drop-in standalone callback route component\n// ============================================================================\n\n/**\n * Drop-in standalone Angular component for OIDC redirect callback routes.\n *\n * Handles the full callback lifecycle on initialization:\n * 1. Calls `CallbackResumeService.resume()` with the current URL.\n * 2. Navigates to the post-auth redirect set at login time.\n * 3. On error: calls the optional `onError` hook and navigates to the\n * configured error target (default `\"/\"`).\n *\n * Register as a route component — no additional glue needed:\n * ```ts\n * import { TokenSetCallbackComponent } from \"@securitydept/token-set-context-client-angular\";\n *\n * export const routes: Routes = [\n * { path: \"auth/callback\", component: TokenSetCallbackComponent },\n * ];\n * ```\n *\n * Host policy can override `fallbackUrl`, `errorRedirectUrl`, and `onError`\n * through `TOKEN_SET_CALLBACK_COMPONENT_OPTIONS`. For fully custom UI or\n * orchestration, build your own component around `CallbackResumeService`.\n */\n@Component({\n\tselector: \"token-set-callback\",\n\tstandalone: true,\n\ttemplate: \"\",\n})\nexport class TokenSetCallbackComponent implements OnInit {\n\tprivate readonly callbackService = inject(CallbackResumeService);\n\tprivate readonly router = inject(Router);\n\tprivate readonly getCurrentUrl = inject(TOKEN_SET_CALLBACK_CURRENT_URL);\n\tprivate readonly options = inject(TOKEN_SET_CALLBACK_COMPONENT_OPTIONS, {\n\t\toptional: true,\n\t}) ?? {\n\t\tfallbackUrl: \"/\",\n\t\terrorRedirectUrl: \"/\",\n\t};\n\n\tngOnInit(): void {\n\t\tconst fallbackUrl = this.options.fallbackUrl ?? \"/\";\n\t\tconst errorRedirectUrl = this.options.errorRedirectUrl ?? fallbackUrl;\n\t\tconst currentUrl = this.getCurrentUrl();\n\t\tif (!currentUrl || !this.callbackService.isCallback(currentUrl)) {\n\t\t\tthis.router.navigateByUrl(fallbackUrl, { replaceUrl: true });\n\t\t\treturn;\n\t\t}\n\n\t\tthis.callbackService\n\t\t\t.resume(currentUrl)\n\t\t\t.then((result) => {\n\t\t\t\tthis.router.navigateByUrl(result.postAuthRedirectUri ?? \"/\", {\n\t\t\t\t\treplaceUrl: true,\n\t\t\t\t});\n\t\t\t})\n\t\t\t.catch((error: unknown) => {\n\t\t\t\tthis.options.onError?.(error);\n\t\t\t\tthis.router.navigateByUrl(errorRedirectUrl, { replaceUrl: true });\n\t\t\t});\n\t}\n}\n","// Angular integration family for @securitydept/token-set-context-client\n//\n// Canonical import path:\n// import { ... } from \"@securitydept/token-set-context-client-angular\"\n//\n// Multi-client-first Angular-native adapter surface:\n// - Keyed client registration via provideTokenSetAuth()\n// - Multi-client lookup via TokenSetAuthRegistry\n// - Per-client TokenSetAuthService with signal + RxJS bridge\n// - Multi-client bearer interceptor (functional + class-based)\n// - provideTokenSetBearerInterceptor() for NgModule HTTP_INTERCEPTORS setup\n// - OIDC callback resume with client key discrimination\n// - CallbackResumeService.isCallback() for programmatic callback detection\n// - TokenSetCallbackComponent standalone component (drop-in callback route)\n// - Requirement/provider-family → client key mapping\n// - secureRouteRoot() / secureRoute() — canonical Angular Router route-security\n// builders: keep non-serializable runtime policy at the root, let child\n// routes declare serializable requirement metadata only, and wire both\n// canActivate + canActivateChild to the full-route aggregation guard.\n// - createTokenSetRouteAggregationGuard() — lower-level advanced guard for\n// direct route-tree evaluation when adopters intentionally bypass the route\n// builder helpers.\n// - Signal/Observable bridge utilities live in @securitydept/client-angular\n//\n// Built by ng-packagr (APF / FESM2022). Decorators are fully supported.\n//\n// Stability: provisional (framework adapter)\n\nexport * from \"./bearer-interceptor\";\nexport * from \"./callback-resume.service\";\nexport * from \"./contracts\";\nexport * from \"./guard-types\";\nexport * from \"./oidc-callback-url\";\nexport * from \"./provide-token-set-auth\";\nexport * from \"./route-aggregation-guard\";\nexport * from \"./secure-routes\";\nexport * from \"./token-set-auth.service\";\nexport * from \"./token-set-auth-registry\";\nexport * from \"./token-set-callback.component\";\nexport * from \"./tokens\";\n","/*\n * Public API Surface of @securitydept/token-set-context-client-angular\n */\n\nexport * from \"./lib\";\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": "@securitydept/token-set-context-client-angular",
|
|
3
|
-
"version": "0.3.0-beta.
|
|
3
|
+
"version": "0.3.0-beta.3",
|
|
4
4
|
"description": "Token Set Context Client Angular of the SecurityDept TypeScript SDK, a layered authentication and authorization toolkit built as reusable Rust crates, a TypeScript client SDK workspace, and reference server/web apps.",
|
|
5
5
|
"author": "Zhou Yeheng <root@aitiotekt.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -45,9 +45,9 @@
|
|
|
45
45
|
"@angular/common": ">=19.0.0",
|
|
46
46
|
"@angular/router": ">=19.0.0",
|
|
47
47
|
"rxjs": ">=7.0.0",
|
|
48
|
-
"@securitydept/client": "0.3.0-beta.
|
|
49
|
-
"@securitydept/client
|
|
50
|
-
"@securitydept/
|
|
48
|
+
"@securitydept/client-angular": "0.3.0-beta.3",
|
|
49
|
+
"@securitydept/token-set-context-client": "0.3.0-beta.3",
|
|
50
|
+
"@securitydept/client": "0.3.0-beta.3"
|
|
51
51
|
},
|
|
52
52
|
"module": "fesm2022/securitydept-token-set-context-client-angular.mjs",
|
|
53
53
|
"typings": "types/securitydept-token-set-context-client-angular.d.ts",
|
|
@@ -5,10 +5,11 @@ import { Observable } from 'rxjs';
|
|
|
5
5
|
import { EventStreamTrait } from '@securitydept/client';
|
|
6
6
|
import { ClientReadinessState } from '@securitydept/token-set-context-client/frontend-oidc-mode';
|
|
7
7
|
import { TokenSetResumeReconciliationOptions, AuthSnapshot, TokenSetAuthEvent, EnsureFreshAuthStateOptions, EnsureAuthForResourceOptions, EnsureAuthForResourceResult, EnsureAuthorizationHeaderOptions } from '@securitydept/token-set-context-client/orchestration';
|
|
8
|
-
import { OidcModeClient, OidcCallbackClient, TokenSetClientEntry as TokenSetClientEntry$1, ClientInitializationPriority, TokenSetAuthRegistry as TokenSetAuthRegistry$1, ClientMeta, ClientKeySelector, ClientFilter, ClientQueryOptions, EnsureRegistryAuthForResourceOptions } from '@securitydept/token-set-context-client/registry';
|
|
9
|
-
export { ClientFilter, ClientInitializationPriority, ClientKeySelector, ClientMeta, ClientQueryOptions, EnsureRegistryAuthForResourceOptions, OidcCallbackClient, OidcModeClient } from '@securitydept/token-set-context-client/registry';
|
|
8
|
+
import { OidcModeClient, OidcCallbackClient, TokenSetClientEntry as TokenSetClientEntry$1, ClientInitializationPriority, TokenSetAuthRegistry as TokenSetAuthRegistry$1, ClientMeta, ClientKeySelector, ClientFilter, ClientQueryOptions, EnsureRegistryAuthForResourceOptions, TokenSetCallbackResumeState, TokenSetCallbackResumeOptions, TokenSetCallbackResumeResult } from '@securitydept/token-set-context-client/registry';
|
|
9
|
+
export { ClientFilter, ClientInitializationPriority, ClientKeySelector, ClientMeta, ClientQueryOptions, EnsureRegistryAuthForResourceOptions, OidcCallbackClient, OidcModeClient, OidcRedirectLoginClient, OidcRedirectLoginOptions } from '@securitydept/token-set-context-client/registry';
|
|
10
10
|
import { ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, CanActivateFn, Route } from '@angular/router';
|
|
11
11
|
import { AuthRequirement, PlannerHost, RequirementsClientSetComposition } from '@securitydept/client/auth-coordination';
|
|
12
|
+
import { PageClientEnvironmentSource } from '@securitydept/client-angular';
|
|
12
13
|
|
|
13
14
|
type TokenSetAngularClient = OidcModeClient & OidcCallbackClient;
|
|
14
15
|
/**
|
|
@@ -78,7 +79,7 @@ interface TokenSetClientEntry extends Omit<TokenSetClientEntry$1<TokenSetAngular
|
|
|
78
79
|
* Per-client Angular service that bridges a single OIDC mode client to
|
|
79
80
|
* Angular signals + RxJS.
|
|
80
81
|
*
|
|
81
|
-
*
|
|
82
|
+
* Lifecycle teardown is explicit. Construct with
|
|
82
83
|
* `new TokenSetAuthService(client, autoRestore)` and call `.dispose()` when
|
|
83
84
|
* the service owner tears down. The shared registry core handles this
|
|
84
85
|
* automatically — adopters that wire the service directly (rare) must call
|
|
@@ -134,7 +135,7 @@ declare class TokenSetAuthRegistry {
|
|
|
134
135
|
/**
|
|
135
136
|
* Register a client entry. Supports sync / async / lazy priority.
|
|
136
137
|
*
|
|
137
|
-
*
|
|
138
|
+
* The second `destroyRef` argument is no longer
|
|
138
139
|
* required — the registry pulls its own `DestroyRef` via injection and
|
|
139
140
|
* binds teardown once per Angular scope.
|
|
140
141
|
*/
|
|
@@ -201,7 +202,7 @@ declare class TokenSetAuthRegistry {
|
|
|
201
202
|
/**
|
|
202
203
|
* Adopter-tunable behaviour for the token-set bearer interceptor.
|
|
203
204
|
*
|
|
204
|
-
*
|
|
205
|
+
* `strictUrlMatch` is an explicit option instead of living on the implicit
|
|
205
206
|
* single-client convenience path into an explicit option, so multi-backend
|
|
206
207
|
* adopters can guarantee that bearer tokens never leak outside the URL
|
|
207
208
|
* patterns they registered with `provideTokenSetAuth({ clients: [...] })`.
|
|
@@ -404,6 +405,11 @@ declare function createTokenSetBearerInterceptor(registry: TokenSetAuthRegistry,
|
|
|
404
405
|
*/
|
|
405
406
|
declare class CallbackResumeService {
|
|
406
407
|
private readonly registry;
|
|
408
|
+
private readonly destroyRef;
|
|
409
|
+
private readonly controller;
|
|
410
|
+
readonly state: WritableSignal<TokenSetCallbackResumeState>;
|
|
411
|
+
readonly state$: Observable<TokenSetCallbackResumeState>;
|
|
412
|
+
constructor();
|
|
407
413
|
/**
|
|
408
414
|
* Check whether a URL is an OIDC authorization callback for any registered
|
|
409
415
|
* client in this registry.
|
|
@@ -417,6 +423,9 @@ declare class CallbackResumeService {
|
|
|
417
423
|
* contains an `code` or `error` query parameter.
|
|
418
424
|
*/
|
|
419
425
|
isCallback(url: string): boolean;
|
|
426
|
+
getState(): TokenSetCallbackResumeState;
|
|
427
|
+
resume(options: TokenSetCallbackResumeOptions): Promise<TokenSetCallbackResumeResult>;
|
|
428
|
+
resume(callbackUrl: string, explicitClientKey?: string): Promise<TokenSetCallbackResumeResult>;
|
|
420
429
|
/**
|
|
421
430
|
* Handle the OIDC callback, auto-detecting which client the callback
|
|
422
431
|
* belongs to via registered callback paths.
|
|
@@ -735,16 +744,30 @@ interface CreateTokenSetRouteAggregationGuardOptions {
|
|
|
735
744
|
* @example
|
|
736
745
|
* ```ts
|
|
737
746
|
* // routes.ts — declare requirements in route data
|
|
738
|
-
* import {
|
|
747
|
+
* import { createBrowserPageClientEnvironment } from "@securitydept/client/web";
|
|
748
|
+
* import {
|
|
749
|
+
* providePageClientEnvironment,
|
|
750
|
+
* withRouteRequirements,
|
|
751
|
+
* } from "@securitydept/client-angular";
|
|
739
752
|
* import {
|
|
740
|
-
*
|
|
753
|
+
* createTokenSetOidcLoginRedirectHandler,
|
|
741
754
|
* createTokenSetRouteAggregationGuard,
|
|
742
755
|
* } from "@securitydept/token-set-context-client-angular";
|
|
743
756
|
*
|
|
757
|
+
* export const appConfig = {
|
|
758
|
+
* providers: [
|
|
759
|
+
* providePageClientEnvironment({
|
|
760
|
+
* environment: createBrowserPageClientEnvironment(),
|
|
761
|
+
* }),
|
|
762
|
+
* ],
|
|
763
|
+
* };
|
|
764
|
+
*
|
|
744
765
|
* // Simple — kind-level handler
|
|
745
766
|
* const guard = createTokenSetRouteAggregationGuard({
|
|
746
767
|
* requirementHandlers: {
|
|
747
|
-
* frontend_oidc:
|
|
768
|
+
* frontend_oidc: createTokenSetOidcLoginRedirectHandler({
|
|
769
|
+
* clientKey: "confluence",
|
|
770
|
+
* }),
|
|
748
771
|
* },
|
|
749
772
|
* });
|
|
750
773
|
*
|
|
@@ -779,7 +802,7 @@ interface CreateTokenSetRouteAggregationGuardOptions {
|
|
|
779
802
|
*/
|
|
780
803
|
declare function createTokenSetRouteAggregationGuard(options?: CreateTokenSetRouteAggregationGuardOptions): CanActivateFn;
|
|
781
804
|
/**
|
|
782
|
-
* Build an Angular route-security handler that starts
|
|
805
|
+
* Build an Angular route-security handler that starts OIDC redirect login and
|
|
783
806
|
* records the attempted navigation URL as `postAuthRedirectUri`.
|
|
784
807
|
*
|
|
785
808
|
* Use this instead of reading `Router.url` inside a guard handler. During guard
|
|
@@ -790,18 +813,30 @@ declare function createTokenSetRouteAggregationGuard(options?: CreateTokenSetRou
|
|
|
790
813
|
* never settles. That keeps Angular from finalizing the rejected in-app
|
|
791
814
|
* navigation while the page is already leaving for an external IdP.
|
|
792
815
|
*/
|
|
793
|
-
|
|
816
|
+
interface CreateTokenSetOidcLoginRedirectHandlerOptions {
|
|
794
817
|
/**
|
|
795
818
|
* Explicit client key. When omitted, the first failing registry entry is
|
|
796
819
|
* used, which is the common one-client-per-requirement case.
|
|
797
820
|
*/
|
|
798
821
|
clientKey?: string;
|
|
822
|
+
/**
|
|
823
|
+
* Optional stable page environment override.
|
|
824
|
+
*
|
|
825
|
+
* The canonical Angular path is to provide a provider-scoped
|
|
826
|
+
* `ClientEnvironmentService`, or another stable resolver, once from the host
|
|
827
|
+
* composition root with `providePageClientEnvironment(...)`, then let the
|
|
828
|
+
* redirect helper resolve it from Angular DI. Use this override only when a
|
|
829
|
+
* specific handler intentionally needs a different stable page environment
|
|
830
|
+
* source.
|
|
831
|
+
*/
|
|
832
|
+
environment?: PageClientEnvironmentSource;
|
|
799
833
|
/**
|
|
800
834
|
* Fallback used only when Angular does not provide a target URL.
|
|
801
835
|
* @default "/"
|
|
802
836
|
*/
|
|
803
837
|
fallbackPostAuthRedirectUri?: string;
|
|
804
|
-
}
|
|
838
|
+
}
|
|
839
|
+
declare function createTokenSetOidcLoginRedirectHandler(options?: CreateTokenSetOidcLoginRedirectHandlerOptions): TokenSetRouteUnauthenticatedHandler;
|
|
805
840
|
|
|
806
841
|
/**
|
|
807
842
|
* Route data key for choosing a planner-host registered at the route root.
|
|
@@ -881,9 +916,10 @@ declare const secureTokenSetRouteRoot: typeof secureRouteRoot;
|
|
|
881
916
|
* Drop-in standalone Angular component for OIDC redirect callback routes.
|
|
882
917
|
*
|
|
883
918
|
* Handles the full callback lifecycle on initialization:
|
|
884
|
-
* 1. Calls `CallbackResumeService.
|
|
885
|
-
* 2. Navigates to
|
|
886
|
-
* 3. On error:
|
|
919
|
+
* 1. Calls `CallbackResumeService.resume()` with the current URL.
|
|
920
|
+
* 2. Navigates to the post-auth redirect set at login time.
|
|
921
|
+
* 3. On error: calls the optional `onError` hook and navigates to the
|
|
922
|
+
* configured error target (default `"/"`).
|
|
887
923
|
*
|
|
888
924
|
* Register as a route component — no additional glue needed:
|
|
889
925
|
* ```ts
|
|
@@ -894,12 +930,15 @@ declare const secureTokenSetRouteRoot: typeof secureRouteRoot;
|
|
|
894
930
|
* ];
|
|
895
931
|
* ```
|
|
896
932
|
*
|
|
897
|
-
*
|
|
898
|
-
*
|
|
933
|
+
* Host policy can override `fallbackUrl`, `errorRedirectUrl`, and `onError`
|
|
934
|
+
* through `TOKEN_SET_CALLBACK_COMPONENT_OPTIONS`. For fully custom UI or
|
|
935
|
+
* orchestration, build your own component around `CallbackResumeService`.
|
|
899
936
|
*/
|
|
900
937
|
declare class TokenSetCallbackComponent implements OnInit {
|
|
901
938
|
private readonly callbackService;
|
|
902
939
|
private readonly router;
|
|
940
|
+
private readonly getCurrentUrl;
|
|
941
|
+
private readonly options;
|
|
903
942
|
ngOnInit(): void;
|
|
904
943
|
static ɵfac: i0.ɵɵFactoryDeclaration<TokenSetCallbackComponent, never>;
|
|
905
944
|
static ɵcmp: i0.ɵɵComponentDeclaration<TokenSetCallbackComponent, "token-set-callback", never, {}, {}, never, never, true, never>;
|
|
@@ -907,7 +946,21 @@ declare class TokenSetCallbackComponent implements OnInit {
|
|
|
907
946
|
|
|
908
947
|
/** InjectionToken for the multi-client auth registry. */
|
|
909
948
|
declare const TOKEN_SET_AUTH_REGISTRY: InjectionToken<TokenSetAuthRegistry>;
|
|
949
|
+
type TokenSetCallbackUrlSource = () => string | undefined;
|
|
950
|
+
interface TokenSetCallbackComponentOptions {
|
|
951
|
+
fallbackUrl?: string;
|
|
952
|
+
errorRedirectUrl?: string;
|
|
953
|
+
onError?: (error: unknown) => void;
|
|
954
|
+
}
|
|
955
|
+
/**
|
|
956
|
+
* InjectionToken for the current callback URL used by the drop-in callback component.
|
|
957
|
+
*
|
|
958
|
+
* Defaults to `window.location.href` in real page hosts and `undefined` in
|
|
959
|
+
* non-page or test hosts unless explicitly overridden.
|
|
960
|
+
*/
|
|
961
|
+
declare const TOKEN_SET_CALLBACK_CURRENT_URL: InjectionToken<TokenSetCallbackUrlSource>;
|
|
962
|
+
declare const TOKEN_SET_CALLBACK_COMPONENT_OPTIONS: InjectionToken<TokenSetCallbackComponentOptions>;
|
|
910
963
|
|
|
911
|
-
export { CallbackResumeService, TOKEN_SET_AUTH_REGISTRY, TOKEN_SET_BEARER_INTERCEPTOR_OPTIONS, TOKEN_SET_ROUTE_PLANNER_HOST_KEY_DATA_KEY, TokenSetAuthRegistry, TokenSetAuthService, TokenSetBearerInterceptor, TokenSetCallbackComponent,
|
|
912
|
-
export type { BearerInterceptorOptions, CreateTokenSetRouteAggregationGuardOptions, ProvideTokenSetAuthOptions, SecureRouteRootSecurityOptions, SecureRouteSecurityOptions, TokenSetAngularClient, TokenSetClientEntry, TokenSetClientSelector, TokenSetRequirementPolicy, TokenSetRouteUnauthenticatedContext, TokenSetRouteUnauthenticatedHandler, UnauthenticatedEntry };
|
|
964
|
+
export { CallbackResumeService, TOKEN_SET_AUTH_REGISTRY, TOKEN_SET_BEARER_INTERCEPTOR_OPTIONS, TOKEN_SET_CALLBACK_COMPONENT_OPTIONS, TOKEN_SET_CALLBACK_CURRENT_URL, TOKEN_SET_ROUTE_PLANNER_HOST_KEY_DATA_KEY, TokenSetAuthRegistry, TokenSetAuthService, TokenSetBearerInterceptor, TokenSetCallbackComponent, createTokenSetBearerInterceptor, createTokenSetOidcLoginRedirectHandler, createTokenSetRouteAggregationGuard, isOidcCallback, provideTokenSetAuth, provideTokenSetBearerInterceptor, secureRoute, secureRouteRoot, secureTokenSetRoute, secureTokenSetRouteRoot };
|
|
965
|
+
export type { BearerInterceptorOptions, CreateTokenSetOidcLoginRedirectHandlerOptions, CreateTokenSetRouteAggregationGuardOptions, ProvideTokenSetAuthOptions, SecureRouteRootSecurityOptions, SecureRouteSecurityOptions, TokenSetAngularClient, TokenSetCallbackComponentOptions, TokenSetCallbackUrlSource, TokenSetClientEntry, TokenSetClientSelector, TokenSetRequirementPolicy, TokenSetRouteUnauthenticatedContext, TokenSetRouteUnauthenticatedHandler, UnauthenticatedEntry };
|
|
913
966
|
//# sourceMappingURL=securitydept-token-set-context-client-angular.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"securitydept-token-set-context-client-angular.d.ts","sources":["../../src/lib/contracts.ts","../../src/lib/token-set-auth.service.ts","../../src/lib/token-set-auth-registry.ts","../../src/lib/bearer-interceptor.ts","../../src/lib/callback-resume.service.ts","../../src/lib/guard-types.ts","../../src/lib/oidc-callback-url.ts","../../src/lib/provide-token-set-auth.ts","../../src/lib/route-aggregation-guard.ts","../../src/lib/secure-routes.ts","../../src/lib/token-set-callback.component.ts","../../src/lib/tokens.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"securitydept-token-set-context-client-angular.d.ts","sources":["../../src/lib/contracts.ts","../../src/lib/token-set-auth.service.ts","../../src/lib/token-set-auth-registry.ts","../../src/lib/bearer-interceptor.ts","../../src/lib/callback-resume.service.ts","../../src/lib/guard-types.ts","../../src/lib/oidc-callback-url.ts","../../src/lib/provide-token-set-auth.ts","../../src/lib/route-aggregation-guard.ts","../../src/lib/secure-routes.ts","../../src/lib/token-set-callback.component.ts","../../src/lib/tokens.ts"],"mappings":"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","names":["CoreTokenSetClientEntry","CoreTokenSetAuthRegistry"]}
|