@tidecloak/js 0.13.35-staging → 0.13.37-staging

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/cjs/lib/secureFetchPolicy.js +45 -0
  2. package/dist/cjs/lib/secureFetchPolicy.js.map +1 -0
  3. package/dist/cjs/lib/tidecloak.js +67 -5
  4. package/dist/cjs/lib/tidecloak.js.map +1 -1
  5. package/dist/cjs/src/AdminAPI.js +25 -3
  6. package/dist/cjs/src/AdminAPI.js.map +1 -1
  7. package/dist/cjs/src/IAMService.js +101 -46
  8. package/dist/cjs/src/IAMService.js.map +1 -1
  9. package/dist/cjs/src/utils/index.js +3 -1
  10. package/dist/cjs/src/utils/index.js.map +1 -1
  11. package/dist/cjs/src/utils/initOptions.js +78 -0
  12. package/dist/cjs/src/utils/initOptions.js.map +1 -0
  13. package/dist/esm/lib/secureFetchPolicy.js +42 -0
  14. package/dist/esm/lib/secureFetchPolicy.js.map +1 -0
  15. package/dist/esm/lib/tidecloak.js +65 -4
  16. package/dist/esm/lib/tidecloak.js.map +1 -1
  17. package/dist/esm/src/AdminAPI.js +25 -3
  18. package/dist/esm/src/AdminAPI.js.map +1 -1
  19. package/dist/esm/src/IAMService.js +102 -47
  20. package/dist/esm/src/IAMService.js.map +1 -1
  21. package/dist/esm/src/utils/index.js +1 -0
  22. package/dist/esm/src/utils/index.js.map +1 -1
  23. package/dist/esm/src/utils/initOptions.js +75 -0
  24. package/dist/esm/src/utils/initOptions.js.map +1 -0
  25. package/dist/types/AdminAPI.d.ts +16 -0
  26. package/dist/types/IAMService.d.ts +25 -0
  27. package/dist/types/lib/secureFetchPolicy.d.ts +37 -0
  28. package/dist/types/lib/tidecloak.d.ts +1 -0
  29. package/dist/types/src/AdminAPI.d.ts +16 -0
  30. package/dist/types/src/IAMService.d.ts +25 -0
  31. package/dist/types/src/utils/index.d.ts +1 -0
  32. package/dist/types/src/utils/initOptions.d.ts +69 -0
  33. package/dist/types/utils/index.d.ts +1 -0
  34. package/dist/types/utils/initOptions.d.ts +69 -0
  35. package/package.json +5 -5
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Builds the options object handed to `TideCloak.init()` from a caller's
3
+ * resolved TideCloak config.
4
+ *
5
+ * ## Why this is an ALLOWLIST, deliberately
6
+ *
7
+ * `IAMService.initIAM` has always built these options as an allowlist: it reads
8
+ * a handful of keys off the config and drops everything else on the floor. The
9
+ * config object is NOT a `KeycloakInitOptions` bag - it is an adapter/config
10
+ * snapshot (`tidecloak.json` + Tide fields + app-level OIDC hints), and callers
11
+ * legitimately put keys in it that are meaningful to `IAMService` (or to their
12
+ * own code) but that must NOT be handed to `init()`.
13
+ *
14
+ * PR #91 widened this allowlist to also forward `redirectUri`,
15
+ * `silentCheckSsoFallback`, `silentCheckSsoTimeout`, `scope` and a
16
+ * caller-supplied `pkceMethod`, on the reasoning that "each is inert unless the
17
+ * caller sets it". That reasoning is wrong in one important direction: consumers
18
+ * had been shipping configs with these keys set for as long as the keys were
19
+ * being SWALLOWED, so their behaviour was pinned to the SDK's defaults, not to
20
+ * the value in their config. Forwarding them is therefore a silent behaviour
21
+ * change for every existing consumer - the exact opposite of inert.
22
+ *
23
+ * `scope` is the sharpest edge: forwarding it rewrites the authorization
24
+ * request's scope, which changes the audience/roles on the issued access token
25
+ * and can strip a consumer's admin-API access.
26
+ *
27
+ * So: forward ONLY what the SDK genuinely needs, and keep the previously
28
+ * defaulted behaviour byte-for-byte. Any option that a consumer should be able
29
+ * to set must be added here CONSCIOUSLY, with its prior default preserved.
30
+ *
31
+ * ### Forwarded
32
+ * - `setupRequestEnclave` - passed through from the `initIAM` argument.
33
+ * - `onLoad` - always `"check-sso"`. NOT taken from config: callers (e.g. the
34
+ * admin console) set `onLoad: "login-required"`, and honouring it would send
35
+ * `#processInit` down the full-redirect branch and never run silent check-sso
36
+ * at all. That is a separate product decision.
37
+ * - `silentCheckSsoRedirectUri` - the resolved URI (see ./silentCheckSso.js).
38
+ * This is the ONE option PR #91 needed to make caller-supplied, and it stays.
39
+ * `undefined` => TideCloak skips silent check-sso and falls back to an
40
+ * interactive login rather than emitting a URI that would 400.
41
+ * - `pkceMethod` - always `"S256"`, as it was before PR #91.
42
+ * - `useDPoP` / `checkLoginIframe` - the two options `initIAM` has always
43
+ * forwarded. `useDPoP` is forwarded ONLY when the caller actually set it, and
44
+ * VERBATIM. DPoP is opt-in: nothing upstream may default it on, because
45
+ * enabling it appends `dpop_jkt` to the authorization request and the realm
46
+ * then issues a `cnf.jkt`-BOUND token, which a plain-Bearer consumer cannot
47
+ * use (RFC 9449) - a bare `401` with no explanation. Pinned in
48
+ * test/bootstrapDpop.test.js.
49
+ *
50
+ * ### Deliberately NOT forwarded (regression-tested in test/initOptions.test.js)
51
+ * `scope`, `redirectUri`, `silentCheckSsoFallback`, `silentCheckSsoTimeout`.
52
+ * Consumers depend on the SDK's defaults for these. `redirectUri` in particular
53
+ * is still READ from config by `IAMService.doLogin` / `doLogout` and is used as
54
+ * a derivation source for `silentCheckSsoRedirectUri`, so a sub-path-hosted app
55
+ * still gets a correct login redirect - it just does not have `kc.redirectUri`
56
+ * overridden at init time.
57
+ *
58
+ * @param {Object} args
59
+ * @param {Object|null|undefined} args.config Resolved TideCloak config.
60
+ * @param {boolean} args.setupRequestEnclave
61
+ * @param {string|undefined} args.silentCheckSsoRedirectUri Resolved URI, or
62
+ * `undefined` when no correct URI could be derived.
63
+ * @returns {Object} Options for `TideCloak.init()`.
64
+ */
65
+ export function buildInitOptions({ config, setupRequestEnclave, silentCheckSsoRedirectUri, }) {
66
+ return {
67
+ setupRequestEnclave,
68
+ onLoad: "check-sso",
69
+ silentCheckSsoRedirectUri,
70
+ pkceMethod: "S256",
71
+ ...((config === null || config === void 0 ? void 0 : config.useDPoP) && { useDPoP: config.useDPoP }),
72
+ ...((config === null || config === void 0 ? void 0 : config.checkLoginIframe) === false && { checkLoginIframe: false }),
73
+ };
74
+ }
75
+ //# sourceMappingURL=initOptions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"initOptions.js","sourceRoot":"","sources":["../../../../src/utils/initOptions.js"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+DG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAC/B,MAAM,EACN,mBAAmB,EACnB,yBAAyB,GAC1B;IACC,OAAO;QACL,mBAAmB;QACnB,MAAM,EAAE,WAAW;QACnB,yBAAyB;QACzB,UAAU,EAAE,MAAM;QAClB,GAAG,CAAC,CAAA,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,OAAO,KAAI,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;QACnD,GAAG,CAAC,CAAA,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,gBAAgB,MAAK,KAAK,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,CAAC;KACvE,CAAC;AACJ,CAAC"}
@@ -11,6 +11,22 @@ export class AdminAPI {
11
11
  * Get the current realm
12
12
  */
13
13
  getRealm(): any;
14
+ /**
15
+ * SAFETY INVARIANT: never send this SDK's own access token as a plain `Bearer`.
16
+ *
17
+ * When DPoP is enabled the issued token is sender-constrained (`typ: DPoP`,
18
+ * `cnf.jkt`), and RFC 9449 says a bound token presented as a plain `Bearer` is
19
+ * invalid - the server answers a bare `401` explaining nothing. So every admin
20
+ * call goes through `IAMService.secureFetch`, which:
21
+ *
22
+ * - with a DPoP provider: upgrades `Authorization: Bearer <our token>` to
23
+ * `Authorization: DPoP <our token>` + a `DPoP:` proof (and handles nonces);
24
+ * - with no DPoP provider: falls through to a plain `fetch` - byte-for-byte
25
+ * what this method did before.
26
+ *
27
+ * @private
28
+ */
29
+ private _authFetch;
14
30
  /**
15
31
  * Make an authenticated fetch request
16
32
  */
@@ -137,6 +137,7 @@ declare class IAMService {
137
137
  _nativeEncryptionCallbackUnsubscribe: any;
138
138
  _pendingEncryptionRequests: Map<any, any>;
139
139
  _dpopProvider: import("../lib/tidecloak-dpop.js").DPoPSignatureProvider;
140
+ _frontchannelInitPromise: Promise<boolean>;
140
141
  /**
141
142
  * Register an event listener.
142
143
  * @param {'ready'|'initError'|'authSuccess'|'authError'|'authRefreshSuccess'|'authRefreshError'|'logout'|'tokenExpired'} event
@@ -177,6 +178,15 @@ declare class IAMService {
177
178
  * @returns {Promise<boolean>} true if authenticated, else false.
178
179
  */
179
180
  initIAM(config: any, onReady?: Function): Promise<boolean>;
181
+ /**
182
+ * Drive the one-and-only `TideCloak.init()` for front-channel mode and emit the
183
+ * resulting `ready` state. Split out of {@link initIAM} so the in-flight promise
184
+ * can be shared with concurrent callers.
185
+ * @private
186
+ * @param {Object} config
187
+ * @returns {Promise<boolean>}
188
+ */
189
+ private _initFrontchannel;
180
190
  /** @private */
181
191
  private getTideCloakClient;
182
192
  /**
@@ -200,6 +210,21 @@ declare class IAMService {
200
210
  private _sanitizeReturnUrl;
201
211
  /** @returns {Object} Loaded config */
202
212
  getConfig(): any;
213
+ /**
214
+ * Whether initialization has already run to completion for the current mode.
215
+ *
216
+ * "Settled", not "started": while an init is still in flight this returns
217
+ * `false`, because the auth state it will produce does not exist yet and reading
218
+ * it would report a spurious logged-out state.
219
+ *
220
+ * Consumers that subscribe to events LATE (a React provider re-running its
221
+ * effect and re-subscribing, say) must use this to re-read the current auth
222
+ * state synchronously rather than waiting on a `ready` event that has already
223
+ * been emitted and is gone.
224
+ *
225
+ * @returns {boolean}
226
+ */
227
+ isInitialized(): boolean;
203
228
  /** @returns {boolean} Whether there's a valid token (or session in hybrid/native mode) */
204
229
  isLoggedIn(): boolean;
205
230
  /**
@@ -0,0 +1,37 @@
1
+ /**
2
+ * The decision seam of `TideCloak.secureFetch`: given the caller's `Authorization`
3
+ * header (if any) and the access token this client currently holds, should we sign
4
+ * the request as our own - `Authorization: DPoP <token>` plus a `DPoP:` proof?
5
+ *
6
+ * Kept in its own dependency-free module so it can be unit-tested directly.
7
+ * `lib/tidecloak.js` pulls in `heimdall-tide`, which cannot be loaded under plain
8
+ * node ESM, so the predicate is unreachable from a test if it lives there - and
9
+ * this is precisely the seam where a mistake becomes an unexplainable `401`.
10
+ */
11
+ /**
12
+ * @param {string|null|undefined} existingAuth - the caller's `Authorization` header.
13
+ * @param {string} ourToken - the access token this client currently holds.
14
+ * @returns {boolean} true -> attach `Authorization: DPoP <ourToken>` + a DPoP proof.
15
+ *
16
+ * Three cases:
17
+ *
18
+ * - NO `Authorization` header: YES, attach. "I set no Authorization, use your
19
+ * token" is the entire point of calling `secureFetch` rather than `fetch`, and
20
+ * it is exactly what `secureFetch`'s own stale-token warning tells callers to do
21
+ * ("omit the Authorization header entirely and let secureFetch attach it").
22
+ *
23
+ * This case used to be false, which sent the request as a plain, UNAUTHENTICATED
24
+ * fetch: no token, no proof, no credentials of any kind. A consumer that reads
25
+ * its token from an async state container and calls before that state has
26
+ * settled (React state that has not yet committed, say) would silently ship a
27
+ * credential-less admin request and get back a bare `401` naming none of this.
28
+ * That is the admin-API 401 this closes.
29
+ *
30
+ * - OUR bearer token: YES, attach - upgrade `Bearer` to `DPoP` + proof. Unchanged.
31
+ *
32
+ * - SOMEONE ELSE'S bearer token: NO. A third-party API call is a legitimate
33
+ * pass-through: it goes out verbatim and must never be handed one of our proofs.
34
+ * Unchanged. (A stale copy of one of ours lands here too, and is warned about
35
+ * once by the caller.)
36
+ */
37
+ export function shouldAttachDpopProof(existingAuth: string | null | undefined, ourToken: string): boolean;
@@ -1,3 +1,4 @@
1
+ export { shouldAttachDpopProof } from "./secureFetchPolicy.js";
1
2
  export default class TideCloak {
2
3
  /**
3
4
  * @param {TideCloakConfig} config
@@ -11,6 +11,22 @@ export class AdminAPI {
11
11
  * Get the current realm
12
12
  */
13
13
  getRealm(): any;
14
+ /**
15
+ * SAFETY INVARIANT: never send this SDK's own access token as a plain `Bearer`.
16
+ *
17
+ * When DPoP is enabled the issued token is sender-constrained (`typ: DPoP`,
18
+ * `cnf.jkt`), and RFC 9449 says a bound token presented as a plain `Bearer` is
19
+ * invalid - the server answers a bare `401` explaining nothing. So every admin
20
+ * call goes through `IAMService.secureFetch`, which:
21
+ *
22
+ * - with a DPoP provider: upgrades `Authorization: Bearer <our token>` to
23
+ * `Authorization: DPoP <our token>` + a `DPoP:` proof (and handles nonces);
24
+ * - with no DPoP provider: falls through to a plain `fetch` - byte-for-byte
25
+ * what this method did before.
26
+ *
27
+ * @private
28
+ */
29
+ private _authFetch;
14
30
  /**
15
31
  * Make an authenticated fetch request
16
32
  */
@@ -145,6 +145,7 @@ declare class IAMService {
145
145
  _nativeEncryptionCallbackUnsubscribe: any;
146
146
  _pendingEncryptionRequests: Map<any, any>;
147
147
  _dpopProvider: import("../lib/tidecloak-dpop.js").DPoPSignatureProvider | null;
148
+ _frontchannelInitPromise: Promise<boolean> | null;
148
149
  /**
149
150
  * Register an event listener.
150
151
  * @param {'ready'|'initError'|'authSuccess'|'authError'|'authRefreshSuccess'|'authRefreshError'|'logout'|'tokenExpired'} event
@@ -185,6 +186,15 @@ declare class IAMService {
185
186
  * @returns {Promise<boolean>} true if authenticated, else false.
186
187
  */
187
188
  initIAM(config: Object, onReady?: Function): Promise<boolean>;
189
+ /**
190
+ * Drive the one-and-only `TideCloak.init()` for front-channel mode and emit the
191
+ * resulting `ready` state. Split out of {@link initIAM} so the in-flight promise
192
+ * can be shared with concurrent callers.
193
+ * @private
194
+ * @param {Object} config
195
+ * @returns {Promise<boolean>}
196
+ */
197
+ private _initFrontchannel;
188
198
  /** @private */
189
199
  private getTideCloakClient;
190
200
  /**
@@ -208,6 +218,21 @@ declare class IAMService {
208
218
  private _sanitizeReturnUrl;
209
219
  /** @returns {Object} Loaded config */
210
220
  getConfig(): Object;
221
+ /**
222
+ * Whether initialization has already run to completion for the current mode.
223
+ *
224
+ * "Settled", not "started": while an init is still in flight this returns
225
+ * `false`, because the auth state it will produce does not exist yet and reading
226
+ * it would report a spurious logged-out state.
227
+ *
228
+ * Consumers that subscribe to events LATE (a React provider re-running its
229
+ * effect and re-subscribing, say) must use this to re-read the current auth
230
+ * state synchronously rather than waiting on a `ready` event that has already
231
+ * been emitted and is gone.
232
+ *
233
+ * @returns {boolean}
234
+ */
235
+ isInitialized(): boolean;
211
236
  /** @returns {boolean} Whether there's a valid token (or session in hybrid/native mode) */
212
237
  isLoggedIn(): boolean;
213
238
  /**
@@ -1,3 +1,4 @@
1
1
  export { fetchJson } from "./fetch.js";
2
+ export { buildInitOptions } from "./initOptions.js";
2
3
  export { makePkce, randomPkceVerifier, base64UrlEncode } from "./pkce.js";
3
4
  export { resolveSilentCheckSsoRedirectUri, SILENT_CHECK_SSO_FILENAME } from "./silentCheckSso.js";
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Builds the options object handed to `TideCloak.init()` from a caller's
3
+ * resolved TideCloak config.
4
+ *
5
+ * ## Why this is an ALLOWLIST, deliberately
6
+ *
7
+ * `IAMService.initIAM` has always built these options as an allowlist: it reads
8
+ * a handful of keys off the config and drops everything else on the floor. The
9
+ * config object is NOT a `KeycloakInitOptions` bag - it is an adapter/config
10
+ * snapshot (`tidecloak.json` + Tide fields + app-level OIDC hints), and callers
11
+ * legitimately put keys in it that are meaningful to `IAMService` (or to their
12
+ * own code) but that must NOT be handed to `init()`.
13
+ *
14
+ * PR #91 widened this allowlist to also forward `redirectUri`,
15
+ * `silentCheckSsoFallback`, `silentCheckSsoTimeout`, `scope` and a
16
+ * caller-supplied `pkceMethod`, on the reasoning that "each is inert unless the
17
+ * caller sets it". That reasoning is wrong in one important direction: consumers
18
+ * had been shipping configs with these keys set for as long as the keys were
19
+ * being SWALLOWED, so their behaviour was pinned to the SDK's defaults, not to
20
+ * the value in their config. Forwarding them is therefore a silent behaviour
21
+ * change for every existing consumer - the exact opposite of inert.
22
+ *
23
+ * `scope` is the sharpest edge: forwarding it rewrites the authorization
24
+ * request's scope, which changes the audience/roles on the issued access token
25
+ * and can strip a consumer's admin-API access.
26
+ *
27
+ * So: forward ONLY what the SDK genuinely needs, and keep the previously
28
+ * defaulted behaviour byte-for-byte. Any option that a consumer should be able
29
+ * to set must be added here CONSCIOUSLY, with its prior default preserved.
30
+ *
31
+ * ### Forwarded
32
+ * - `setupRequestEnclave` - passed through from the `initIAM` argument.
33
+ * - `onLoad` - always `"check-sso"`. NOT taken from config: callers (e.g. the
34
+ * admin console) set `onLoad: "login-required"`, and honouring it would send
35
+ * `#processInit` down the full-redirect branch and never run silent check-sso
36
+ * at all. That is a separate product decision.
37
+ * - `silentCheckSsoRedirectUri` - the resolved URI (see ./silentCheckSso.js).
38
+ * This is the ONE option PR #91 needed to make caller-supplied, and it stays.
39
+ * `undefined` => TideCloak skips silent check-sso and falls back to an
40
+ * interactive login rather than emitting a URI that would 400.
41
+ * - `pkceMethod` - always `"S256"`, as it was before PR #91.
42
+ * - `useDPoP` / `checkLoginIframe` - the two options `initIAM` has always
43
+ * forwarded. `useDPoP` is forwarded ONLY when the caller actually set it, and
44
+ * VERBATIM. DPoP is opt-in: nothing upstream may default it on, because
45
+ * enabling it appends `dpop_jkt` to the authorization request and the realm
46
+ * then issues a `cnf.jkt`-BOUND token, which a plain-Bearer consumer cannot
47
+ * use (RFC 9449) - a bare `401` with no explanation. Pinned in
48
+ * test/bootstrapDpop.test.js.
49
+ *
50
+ * ### Deliberately NOT forwarded (regression-tested in test/initOptions.test.js)
51
+ * `scope`, `redirectUri`, `silentCheckSsoFallback`, `silentCheckSsoTimeout`.
52
+ * Consumers depend on the SDK's defaults for these. `redirectUri` in particular
53
+ * is still READ from config by `IAMService.doLogin` / `doLogout` and is used as
54
+ * a derivation source for `silentCheckSsoRedirectUri`, so a sub-path-hosted app
55
+ * still gets a correct login redirect - it just does not have `kc.redirectUri`
56
+ * overridden at init time.
57
+ *
58
+ * @param {Object} args
59
+ * @param {Object|null|undefined} args.config Resolved TideCloak config.
60
+ * @param {boolean} args.setupRequestEnclave
61
+ * @param {string|undefined} args.silentCheckSsoRedirectUri Resolved URI, or
62
+ * `undefined` when no correct URI could be derived.
63
+ * @returns {Object} Options for `TideCloak.init()`.
64
+ */
65
+ export function buildInitOptions({ config, setupRequestEnclave, silentCheckSsoRedirectUri, }: {
66
+ config: Object | null | undefined;
67
+ setupRequestEnclave: boolean;
68
+ silentCheckSsoRedirectUri: string | undefined;
69
+ }): Object;
@@ -1,3 +1,4 @@
1
1
  export { fetchJson } from "./fetch.js";
2
+ export { buildInitOptions } from "./initOptions.js";
2
3
  export { makePkce, randomPkceVerifier, base64UrlEncode } from "./pkce.js";
3
4
  export { resolveSilentCheckSsoRedirectUri, SILENT_CHECK_SSO_FILENAME } from "./silentCheckSso.js";
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Builds the options object handed to `TideCloak.init()` from a caller's
3
+ * resolved TideCloak config.
4
+ *
5
+ * ## Why this is an ALLOWLIST, deliberately
6
+ *
7
+ * `IAMService.initIAM` has always built these options as an allowlist: it reads
8
+ * a handful of keys off the config and drops everything else on the floor. The
9
+ * config object is NOT a `KeycloakInitOptions` bag - it is an adapter/config
10
+ * snapshot (`tidecloak.json` + Tide fields + app-level OIDC hints), and callers
11
+ * legitimately put keys in it that are meaningful to `IAMService` (or to their
12
+ * own code) but that must NOT be handed to `init()`.
13
+ *
14
+ * PR #91 widened this allowlist to also forward `redirectUri`,
15
+ * `silentCheckSsoFallback`, `silentCheckSsoTimeout`, `scope` and a
16
+ * caller-supplied `pkceMethod`, on the reasoning that "each is inert unless the
17
+ * caller sets it". That reasoning is wrong in one important direction: consumers
18
+ * had been shipping configs with these keys set for as long as the keys were
19
+ * being SWALLOWED, so their behaviour was pinned to the SDK's defaults, not to
20
+ * the value in their config. Forwarding them is therefore a silent behaviour
21
+ * change for every existing consumer - the exact opposite of inert.
22
+ *
23
+ * `scope` is the sharpest edge: forwarding it rewrites the authorization
24
+ * request's scope, which changes the audience/roles on the issued access token
25
+ * and can strip a consumer's admin-API access.
26
+ *
27
+ * So: forward ONLY what the SDK genuinely needs, and keep the previously
28
+ * defaulted behaviour byte-for-byte. Any option that a consumer should be able
29
+ * to set must be added here CONSCIOUSLY, with its prior default preserved.
30
+ *
31
+ * ### Forwarded
32
+ * - `setupRequestEnclave` - passed through from the `initIAM` argument.
33
+ * - `onLoad` - always `"check-sso"`. NOT taken from config: callers (e.g. the
34
+ * admin console) set `onLoad: "login-required"`, and honouring it would send
35
+ * `#processInit` down the full-redirect branch and never run silent check-sso
36
+ * at all. That is a separate product decision.
37
+ * - `silentCheckSsoRedirectUri` - the resolved URI (see ./silentCheckSso.js).
38
+ * This is the ONE option PR #91 needed to make caller-supplied, and it stays.
39
+ * `undefined` => TideCloak skips silent check-sso and falls back to an
40
+ * interactive login rather than emitting a URI that would 400.
41
+ * - `pkceMethod` - always `"S256"`, as it was before PR #91.
42
+ * - `useDPoP` / `checkLoginIframe` - the two options `initIAM` has always
43
+ * forwarded. `useDPoP` is forwarded ONLY when the caller actually set it, and
44
+ * VERBATIM. DPoP is opt-in: nothing upstream may default it on, because
45
+ * enabling it appends `dpop_jkt` to the authorization request and the realm
46
+ * then issues a `cnf.jkt`-BOUND token, which a plain-Bearer consumer cannot
47
+ * use (RFC 9449) - a bare `401` with no explanation. Pinned in
48
+ * test/bootstrapDpop.test.js.
49
+ *
50
+ * ### Deliberately NOT forwarded (regression-tested in test/initOptions.test.js)
51
+ * `scope`, `redirectUri`, `silentCheckSsoFallback`, `silentCheckSsoTimeout`.
52
+ * Consumers depend on the SDK's defaults for these. `redirectUri` in particular
53
+ * is still READ from config by `IAMService.doLogin` / `doLogout` and is used as
54
+ * a derivation source for `silentCheckSsoRedirectUri`, so a sub-path-hosted app
55
+ * still gets a correct login redirect - it just does not have `kc.redirectUri`
56
+ * overridden at init time.
57
+ *
58
+ * @param {Object} args
59
+ * @param {Object|null|undefined} args.config Resolved TideCloak config.
60
+ * @param {boolean} args.setupRequestEnclave
61
+ * @param {string|undefined} args.silentCheckSsoRedirectUri Resolved URI, or
62
+ * `undefined` when no correct URI could be derived.
63
+ * @returns {Object} Options for `TideCloak.init()`.
64
+ */
65
+ export function buildInitOptions({ config, setupRequestEnclave, silentCheckSsoRedirectUri, }: {
66
+ config: any | null | undefined;
67
+ setupRequestEnclave: boolean;
68
+ silentCheckSsoRedirectUri: string | undefined;
69
+ }): any;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tidecloak/js",
3
- "version": "0.13.35-staging",
3
+ "version": "0.13.37-staging",
4
4
  "description": "Vanilla JavaScript/browser SDK for TideCloak with an IAMService singleton and low-level client.",
5
5
  "type": "module",
6
6
  "main": "dist/cjs/src/index.js",
@@ -24,7 +24,7 @@
24
24
  "./policy.css": "./dist/esm/src/policy.css"
25
25
  },
26
26
  "scripts": {
27
- "test": "node --test test/*.test.js",
27
+ "test": "node --experimental-test-module-mocks --test test/*.test.js",
28
28
  "postinstall": "node ./scripts/postinstall.cjs",
29
29
  "build:cjs": "tsc -p tsconfig.cjs.json",
30
30
  "build:esm": "tsc -p tsconfig.esm.json",
@@ -57,11 +57,11 @@
57
57
  "authorization"
58
58
  ],
59
59
  "dependencies": {
60
- "heimdall-tide": "0.13.35-staging",
61
- "@tideorg/js": "0.13.35-staging"
60
+ "heimdall-tide": "0.13.37-staging",
61
+ "@tideorg/js": "0.13.37-staging"
62
62
  },
63
63
  "peerDependencies": {
64
- "@tidecloak/policy": "0.13.35-staging"
64
+ "@tidecloak/policy": "0.13.37-staging"
65
65
  },
66
66
  "peerDependenciesMeta": {
67
67
  "@tidecloak/policy": {