@tidecloak/js 0.14.41 → 0.14.43-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 (111) hide show
  1. package/README.md +32 -47
  2. package/dist/cjs/lib/tidecloak-dpop.js +151 -42
  3. package/dist/cjs/lib/tidecloak-dpop.js.map +1 -1
  4. package/dist/cjs/lib/tidecloak.js +300 -378
  5. package/dist/cjs/lib/tidecloak.js.map +1 -1
  6. package/dist/cjs/src/AdminAPI.js +12 -19
  7. package/dist/cjs/src/AdminAPI.js.map +1 -1
  8. package/dist/cjs/src/IAMService.js +159 -2095
  9. package/dist/cjs/src/IAMService.js.map +1 -1
  10. package/dist/cjs/src/index.js +1 -7
  11. package/dist/cjs/src/index.js.map +1 -1
  12. package/dist/esm/lib/tidecloak-dpop.js +151 -42
  13. package/dist/esm/lib/tidecloak-dpop.js.map +1 -1
  14. package/dist/esm/lib/tidecloak.js +300 -377
  15. package/dist/esm/lib/tidecloak.js.map +1 -1
  16. package/dist/esm/src/AdminAPI.js +3 -7
  17. package/dist/esm/src/AdminAPI.js.map +1 -1
  18. package/dist/esm/src/IAMService.js +156 -2062
  19. package/dist/esm/src/IAMService.js.map +1 -1
  20. package/dist/esm/src/index.js +1 -7
  21. package/dist/esm/src/index.js.map +1 -1
  22. package/dist/types/AdminAPI.d.ts +1 -1
  23. package/dist/types/IAMService.d.ts +57 -606
  24. package/dist/types/index.d.ts +1 -11
  25. package/dist/types/lib/tidecloak-dpop.d.ts +19 -3
  26. package/dist/types/lib/tidecloak.d.ts +46 -38
  27. package/dist/types/src/AdminAPI.d.ts +1 -1
  28. package/dist/types/src/IAMService.d.ts +57 -606
  29. package/dist/types/src/index.d.ts +1 -11
  30. package/docs/FRONT_CHANNEL.md +55 -55
  31. package/lib/README.md +10 -14
  32. package/package.json +4 -25
  33. package/dist/cjs/lib/secureFetchPolicy.js +0 -45
  34. package/dist/cjs/lib/secureFetchPolicy.js.map +0 -1
  35. package/dist/cjs/src/dpopRoute.js +0 -30
  36. package/dist/cjs/src/dpopRoute.js.map +0 -1
  37. package/dist/cjs/src/dpopServer.js +0 -77
  38. package/dist/cjs/src/dpopServer.js.map +0 -1
  39. package/dist/cjs/src/generated/dpopAuthAsset.js +0 -7
  40. package/dist/cjs/src/generated/dpopAuthAsset.js.map +0 -1
  41. package/dist/cjs/src/policy-react.js +0 -51
  42. package/dist/cjs/src/policy-react.js.map +0 -1
  43. package/dist/cjs/src/types.js +0 -6
  44. package/dist/cjs/src/types.js.map +0 -1
  45. package/dist/cjs/src/utils/dpopAuthPage.js +0 -69
  46. package/dist/cjs/src/utils/dpopAuthPage.js.map +0 -1
  47. package/dist/cjs/src/utils/fetch.js +0 -39
  48. package/dist/cjs/src/utils/fetch.js.map +0 -1
  49. package/dist/cjs/src/utils/index.js +0 -17
  50. package/dist/cjs/src/utils/index.js.map +0 -1
  51. package/dist/cjs/src/utils/initOptions.js +0 -78
  52. package/dist/cjs/src/utils/initOptions.js.map +0 -1
  53. package/dist/cjs/src/utils/pkce.js +0 -48
  54. package/dist/cjs/src/utils/pkce.js.map +0 -1
  55. package/dist/cjs/src/utils/silentCheckSso.js +0 -138
  56. package/dist/cjs/src/utils/silentCheckSso.js.map +0 -1
  57. package/dist/cjs/src/vite.js +0 -43
  58. package/dist/cjs/src/vite.js.map +0 -1
  59. package/dist/esm/lib/secureFetchPolicy.js +0 -42
  60. package/dist/esm/lib/secureFetchPolicy.js.map +0 -1
  61. package/dist/esm/src/dpopRoute.js +0 -27
  62. package/dist/esm/src/dpopRoute.js.map +0 -1
  63. package/dist/esm/src/dpopServer.js +0 -68
  64. package/dist/esm/src/dpopServer.js.map +0 -1
  65. package/dist/esm/src/generated/dpopAuthAsset.js +0 -4
  66. package/dist/esm/src/generated/dpopAuthAsset.js.map +0 -1
  67. package/dist/esm/src/policy-react.js +0 -45
  68. package/dist/esm/src/policy-react.js.map +0 -1
  69. package/dist/esm/src/types.js +0 -5
  70. package/dist/esm/src/types.js.map +0 -1
  71. package/dist/esm/src/utils/dpopAuthPage.js +0 -63
  72. package/dist/esm/src/utils/dpopAuthPage.js.map +0 -1
  73. package/dist/esm/src/utils/fetch.js +0 -36
  74. package/dist/esm/src/utils/fetch.js.map +0 -1
  75. package/dist/esm/src/utils/index.js +0 -6
  76. package/dist/esm/src/utils/index.js.map +0 -1
  77. package/dist/esm/src/utils/initOptions.js +0 -75
  78. package/dist/esm/src/utils/initOptions.js.map +0 -1
  79. package/dist/esm/src/utils/pkce.js +0 -43
  80. package/dist/esm/src/utils/pkce.js.map +0 -1
  81. package/dist/esm/src/utils/silentCheckSso.js +0 -134
  82. package/dist/esm/src/utils/silentCheckSso.js.map +0 -1
  83. package/dist/esm/src/vite.js +0 -40
  84. package/dist/esm/src/vite.js.map +0 -1
  85. package/dist/types/dpopRoute.d.ts +0 -11
  86. package/dist/types/dpopServer.d.ts +0 -35
  87. package/dist/types/generated/dpopAuthAsset.d.ts +0 -2
  88. package/dist/types/lib/secureFetchPolicy.d.ts +0 -37
  89. package/dist/types/policy-react.d.ts +0 -40
  90. package/dist/types/src/dpopRoute.d.ts +0 -11
  91. package/dist/types/src/dpopServer.d.ts +0 -35
  92. package/dist/types/src/generated/dpopAuthAsset.d.ts +0 -2
  93. package/dist/types/src/policy-react.d.ts +0 -40
  94. package/dist/types/src/types.d.ts +0 -114
  95. package/dist/types/src/utils/dpopAuthPage.d.ts +0 -22
  96. package/dist/types/src/utils/fetch.d.ts +0 -11
  97. package/dist/types/src/utils/index.d.ts +0 -5
  98. package/dist/types/src/utils/initOptions.d.ts +0 -69
  99. package/dist/types/src/utils/pkce.d.ts +0 -24
  100. package/dist/types/src/utils/silentCheckSso.d.ts +0 -82
  101. package/dist/types/src/vite.d.ts +0 -13
  102. package/dist/types/types.d.ts +0 -114
  103. package/dist/types/utils/dpopAuthPage.d.ts +0 -22
  104. package/dist/types/utils/fetch.d.ts +0 -11
  105. package/dist/types/utils/index.d.ts +0 -5
  106. package/dist/types/utils/initOptions.d.ts +0 -69
  107. package/dist/types/utils/pkce.d.ts +0 -24
  108. package/dist/types/utils/silentCheckSso.d.ts +0 -82
  109. package/dist/types/vite.d.ts +0 -13
  110. package/docs/HYBRID_MODE.md +0 -185
  111. package/docs/NATIVE_MODE.md +0 -284
@@ -1,69 +0,0 @@
1
- "use strict";
2
- // With DPoP on, the enclave loads tide_dpop_auth.html from this app's origin
3
- // during login. If it is missing, login stalls ~4s and falls back to a popup
4
- // with nothing logged. This check says which URL is missing. It never throws.
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.utf8ToHex = utf8ToHex;
7
- exports.buildDpopAuthUrl = buildDpopAuthUrl;
8
- exports.probeDpopAuthPage = probeDpopAuthPage;
9
- exports.preflightDpopAuthPage = preflightDpopAuthPage;
10
- function utf8ToHex(value) {
11
- let out = '';
12
- for (const b of new TextEncoder().encode(value))
13
- out += b.toString(16).padStart(2, '0');
14
- return out;
15
- }
16
- function buildDpopAuthUrl({ origin, authServerUrl, realm, clientId }) {
17
- if (!origin || !authServerUrl || !realm || !clientId)
18
- return null;
19
- const issuer = `${String(authServerUrl).replace(/\/+$/, '')}/realms/${realm}`;
20
- return `${String(origin).replace(/\/+$/, '')}/tide_dpop/iss/${utf8ToHex(issuer)}/aud/${utf8ToHex(clientId)}/tide_dpop_auth.html`;
21
- }
22
- // Returns served | missing-headers | mismatch | not-served | inconclusive.
23
- async function probeDpopAuthPage({ url, fetchImpl = globalThis.fetch, timeoutMs = 2000 }) {
24
- const controller = new AbortController();
25
- const timer = setTimeout(() => controller.abort(), timeoutMs);
26
- let res;
27
- try {
28
- res = await fetchImpl(url, { method: 'HEAD', cache: 'no-store', credentials: 'omit', signal: controller.signal });
29
- }
30
- catch {
31
- return { outcome: 'inconclusive' };
32
- }
33
- finally {
34
- clearTimeout(timer);
35
- }
36
- // 405/501: a GET-only route is still a working route.
37
- if (res.status === 403)
38
- return { outcome: 'mismatch' };
39
- if (res.status === 404 || res.status === 410)
40
- return { outcome: 'not-served' };
41
- if (res.status < 200 || res.status >= 300)
42
- return { outcome: 'inconclusive' };
43
- const missing = ['Content-Security-Policy', 'Allow-CSP-From'].filter((h) => !res.headers.get(h));
44
- return missing.length ? { outcome: 'missing-headers', missing } : { outcome: 'served' };
45
- }
46
- async function preflightDpopAuthPage({ logger = console, ...params }) {
47
- try {
48
- const url = buildDpopAuthUrl(params);
49
- if (!url)
50
- return 'inconclusive';
51
- const { outcome, missing } = await probeDpopAuthPage({ url, ...params });
52
- if (outcome === 'not-served') {
53
- logger.error(`[IAMService] DPoP is on but nothing is served at ${url}. Serve tide_dpop_auth.html there ` +
54
- 'with Content-Security-Policy and Allow-CSP-From: * headers, or logins will stall and fall back to a popup.');
55
- }
56
- else if (outcome === 'missing-headers') {
57
- logger.warn(`[IAMService] ${url} is served without ${missing.join(' and ')}. The enclave cannot embed it.`);
58
- }
59
- else if (outcome === 'mismatch') {
60
- // Behind a proxy, TideCloak may see a different base URL than the one in our config.
61
- logger.warn(`[IAMService] ${url} answered 403. Check the realm and client id match your TideCloak config.`);
62
- }
63
- return outcome;
64
- }
65
- catch {
66
- return 'inconclusive';
67
- }
68
- }
69
- //# sourceMappingURL=dpopAuthPage.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"dpopAuthPage.js","sourceRoot":"","sources":["../../../../src/utils/dpopAuthPage.js"],"names":[],"mappings":"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"}
@@ -1,39 +0,0 @@
1
- "use strict";
2
- /**
3
- * Fetch utilities for HTTP requests.
4
- */
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.fetchJson = fetchJson;
7
- /**
8
- * Fetch JSON with credentials included.
9
- * @param {string} url - URL to fetch
10
- * @param {RequestInit} [options={}] - Fetch options
11
- * @returns {Promise<any>} Parsed JSON response
12
- * @throws {Error} If request fails (with status and body properties)
13
- */
14
- async function fetchJson(url, options = {}) {
15
- const res = await fetch(url, {
16
- credentials: "include",
17
- ...options,
18
- headers: {
19
- "Content-Type": "application/json",
20
- ...(options.headers || {}),
21
- },
22
- });
23
- const text = await res.text();
24
- let json = null;
25
- try {
26
- json = text ? JSON.parse(text) : null;
27
- }
28
- catch {
29
- // ignore parse errors
30
- }
31
- if (!res.ok) {
32
- const err = new Error((json === null || json === void 0 ? void 0 : json.message) || `Request failed (${res.status})`);
33
- err.status = res.status;
34
- err.body = json !== null && json !== void 0 ? json : text;
35
- throw err;
36
- }
37
- return json;
38
- }
39
- //# sourceMappingURL=fetch.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"fetch.js","sourceRoot":"","sources":["../../../../src/utils/fetch.js"],"names":[],"mappings":";AAAA;;GAEG;;;AAEH;;;;;;GAMG;AACI,KAAK,oBAAoB,GAAG,EAAE,OAAO,GAAG,EAAE;IAC/C,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAC3B,WAAW,EAAE,SAAS;QACtB,GAAG,OAAO;QACV,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC;SAC3B;KACF,CAAC,CAAC;IAEH,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAC9B,IAAI,IAAI,GAAG,IAAI,CAAC;IAChB,IAAI,CAAC;QACH,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACxC,CAAC;IAAC,MAAM,CAAC;QACP,sBAAsB;IACxB,CAAC;IAED,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,CAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,OAAO,KAAI,mBAAmB,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC;QACzE,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;QACxB,GAAG,CAAC,IAAI,GAAG,IAAI,aAAJ,IAAI,cAAJ,IAAI,GAAI,IAAI,CAAC;QACxB,MAAM,GAAG,CAAC;IACZ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -1,17 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.preflightDpopAuthPage = exports.buildInitOptions = exports.SILENT_CHECK_SSO_FILENAME = exports.resolveSilentCheckSsoRedirectUri = exports.fetchJson = exports.base64UrlEncode = exports.randomPkceVerifier = exports.makePkce = void 0;
4
- var pkce_js_1 = require("./pkce.js");
5
- Object.defineProperty(exports, "makePkce", { enumerable: true, get: function () { return pkce_js_1.makePkce; } });
6
- Object.defineProperty(exports, "randomPkceVerifier", { enumerable: true, get: function () { return pkce_js_1.randomPkceVerifier; } });
7
- Object.defineProperty(exports, "base64UrlEncode", { enumerable: true, get: function () { return pkce_js_1.base64UrlEncode; } });
8
- var fetch_js_1 = require("./fetch.js");
9
- Object.defineProperty(exports, "fetchJson", { enumerable: true, get: function () { return fetch_js_1.fetchJson; } });
10
- var silentCheckSso_js_1 = require("./silentCheckSso.js");
11
- Object.defineProperty(exports, "resolveSilentCheckSsoRedirectUri", { enumerable: true, get: function () { return silentCheckSso_js_1.resolveSilentCheckSsoRedirectUri; } });
12
- Object.defineProperty(exports, "SILENT_CHECK_SSO_FILENAME", { enumerable: true, get: function () { return silentCheckSso_js_1.SILENT_CHECK_SSO_FILENAME; } });
13
- var initOptions_js_1 = require("./initOptions.js");
14
- Object.defineProperty(exports, "buildInitOptions", { enumerable: true, get: function () { return initOptions_js_1.buildInitOptions; } });
15
- var dpopAuthPage_js_1 = require("./dpopAuthPage.js");
16
- Object.defineProperty(exports, "preflightDpopAuthPage", { enumerable: true, get: function () { return dpopAuthPage_js_1.preflightDpopAuthPage; } });
17
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/utils/index.js"],"names":[],"mappings":";;;AAAA,qCAA0E;AAAjE,mGAAA,QAAQ,OAAA;AAAE,6GAAA,kBAAkB,OAAA;AAAE,0GAAA,eAAe,OAAA;AACtD,uCAAuC;AAA9B,qGAAA,SAAS,OAAA;AAClB,yDAAkG;AAAzF,qIAAA,gCAAgC,OAAA;AAAE,8HAAA,yBAAyB,OAAA;AACpE,mDAAoD;AAA3C,kHAAA,gBAAgB,OAAA;AACzB,qDAA0D;AAAjD,wHAAA,qBAAqB,OAAA"}
@@ -1,78 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.buildInitOptions = buildInitOptions;
4
- /**
5
- * Builds the options object handed to `TideCloak.init()` from a caller's
6
- * resolved TideCloak config.
7
- *
8
- * ## Why this is an ALLOWLIST, deliberately
9
- *
10
- * `IAMService.initIAM` has always built these options as an allowlist: it reads
11
- * a handful of keys off the config and drops everything else on the floor. The
12
- * config object is NOT a `KeycloakInitOptions` bag - it is an adapter/config
13
- * snapshot (`tidecloak.json` + Tide fields + app-level OIDC hints), and callers
14
- * legitimately put keys in it that are meaningful to `IAMService` (or to their
15
- * own code) but that must NOT be handed to `init()`.
16
- *
17
- * PR #91 widened this allowlist to also forward `redirectUri`,
18
- * `silentCheckSsoFallback`, `silentCheckSsoTimeout`, `scope` and a
19
- * caller-supplied `pkceMethod`, on the reasoning that "each is inert unless the
20
- * caller sets it". That reasoning is wrong in one important direction: consumers
21
- * had been shipping configs with these keys set for as long as the keys were
22
- * being SWALLOWED, so their behaviour was pinned to the SDK's defaults, not to
23
- * the value in their config. Forwarding them is therefore a silent behaviour
24
- * change for every existing consumer - the exact opposite of inert.
25
- *
26
- * `scope` is the sharpest edge: forwarding it rewrites the authorization
27
- * request's scope, which changes the audience/roles on the issued access token
28
- * and can strip a consumer's admin-API access.
29
- *
30
- * So: forward ONLY what the SDK genuinely needs, and keep the previously
31
- * defaulted behaviour byte-for-byte. Any option that a consumer should be able
32
- * to set must be added here CONSCIOUSLY, with its prior default preserved.
33
- *
34
- * ### Forwarded
35
- * - `setupRequestEnclave` - passed through from the `initIAM` argument.
36
- * - `onLoad` - always `"check-sso"`. NOT taken from config: callers (e.g. the
37
- * admin console) set `onLoad: "login-required"`, and honouring it would send
38
- * `#processInit` down the full-redirect branch and never run silent check-sso
39
- * at all. That is a separate product decision.
40
- * - `silentCheckSsoRedirectUri` - the resolved URI (see ./silentCheckSso.js).
41
- * This is the ONE option PR #91 needed to make caller-supplied, and it stays.
42
- * `undefined` => TideCloak skips silent check-sso and falls back to an
43
- * interactive login rather than emitting a URI that would 400.
44
- * - `pkceMethod` - always `"S256"`, as it was before PR #91.
45
- * - `useDPoP` / `checkLoginIframe` - the two options `initIAM` has always
46
- * forwarded. `useDPoP` is forwarded ONLY when the caller actually set it, and
47
- * VERBATIM. DPoP is opt-in: nothing upstream may default it on, because
48
- * enabling it appends `dpop_jkt` to the authorization request and the realm
49
- * then issues a `cnf.jkt`-BOUND token, which a plain-Bearer consumer cannot
50
- * use (RFC 9449) - a bare `401` with no explanation. Pinned in
51
- * test/bootstrapDpop.test.js.
52
- *
53
- * ### Deliberately NOT forwarded (regression-tested in test/initOptions.test.js)
54
- * `scope`, `redirectUri`, `silentCheckSsoFallback`, `silentCheckSsoTimeout`.
55
- * Consumers depend on the SDK's defaults for these. `redirectUri` in particular
56
- * is still READ from config by `IAMService.doLogin` / `doLogout` and is used as
57
- * a derivation source for `silentCheckSsoRedirectUri`, so a sub-path-hosted app
58
- * still gets a correct login redirect - it just does not have `kc.redirectUri`
59
- * overridden at init time.
60
- *
61
- * @param {Object} args
62
- * @param {Object|null|undefined} args.config Resolved TideCloak config.
63
- * @param {boolean} args.setupRequestEnclave
64
- * @param {string|undefined} args.silentCheckSsoRedirectUri Resolved URI, or
65
- * `undefined` when no correct URI could be derived.
66
- * @returns {Object} Options for `TideCloak.init()`.
67
- */
68
- function buildInitOptions({ config, setupRequestEnclave, silentCheckSsoRedirectUri, }) {
69
- return {
70
- setupRequestEnclave,
71
- onLoad: "check-sso",
72
- silentCheckSsoRedirectUri,
73
- pkceMethod: "S256",
74
- ...((config === null || config === void 0 ? void 0 : config.useDPoP) && { useDPoP: config.useDPoP }),
75
- ...((config === null || config === void 0 ? void 0 : config.checkLoginIframe) === false && { checkLoginIframe: false }),
76
- };
77
- }
78
- //# sourceMappingURL=initOptions.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"initOptions.js","sourceRoot":"","sources":["../../../../src/utils/initOptions.js"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+DG;AACH,0BAAiC,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"}
@@ -1,48 +0,0 @@
1
- "use strict";
2
- /**
3
- * PKCE (Proof Key for Code Exchange) utilities for OAuth 2.0 authorization code flow.
4
- */
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.randomPkceVerifier = randomPkceVerifier;
7
- exports.base64UrlEncode = base64UrlEncode;
8
- exports.makePkce = makePkce;
9
- /**
10
- * Generate random PKCE verifier string.
11
- * @param {number} len - Length of the verifier (default 96)
12
- * @returns {string} Random verifier string
13
- */
14
- function randomPkceVerifier(len = 96) {
15
- const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
16
- const bytes = new Uint8Array(len);
17
- crypto.getRandomValues(bytes);
18
- let out = "";
19
- for (let i = 0; i < len; i++) {
20
- out += charset[bytes[i] % charset.length];
21
- }
22
- return out;
23
- }
24
- /**
25
- * Base64 URL encode an ArrayBuffer.
26
- * @param {ArrayBuffer} arrayBuffer - Buffer to encode
27
- * @returns {string} Base64 URL encoded string
28
- */
29
- function base64UrlEncode(arrayBuffer) {
30
- const bytes = new Uint8Array(arrayBuffer);
31
- let binary = "";
32
- for (let i = 0; i < bytes.byteLength; i++) {
33
- binary += String.fromCharCode(bytes[i]);
34
- }
35
- return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
36
- }
37
- /**
38
- * Generate PKCE verifier and challenge.
39
- * @returns {Promise<{verifier: string, challenge: string, method: string}>}
40
- */
41
- async function makePkce() {
42
- const verifier = randomPkceVerifier(96);
43
- const encoder = new TextEncoder();
44
- const digest = await crypto.subtle.digest("SHA-256", encoder.encode(verifier));
45
- const challenge = base64UrlEncode(digest);
46
- return { verifier, challenge, method: "S256" };
47
- }
48
- //# sourceMappingURL=pkce.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"pkce.js","sourceRoot":"","sources":["../../../../src/utils/pkce.js"],"names":[],"mappings":";AAAA;;GAEG;;;;;AAEH;;;;GAIG;AACH,4BAAmC,GAAG,GAAG,EAAE;IACzC,MAAM,OAAO,GAAG,oEAAoE,CAAC;IACrF,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC;IAClC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;IAC9B,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7B,GAAG,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5C,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;GAIG;AACH,yBAAgC,WAAW;IACzC,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,WAAW,CAAC,CAAC;IAC1C,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,UAAU,EAAE,CAAC,EAAE,EAAE,CAAC;QAC1C,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAClF,CAAC;AAED;;;GAGG;AACI,KAAK;IACV,MAAM,QAAQ,GAAG,kBAAkB,CAAC,EAAE,CAAC,CAAC;IACxC,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;IAClC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC/E,MAAM,SAAS,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IAC1C,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AACjD,CAAC"}
@@ -1,138 +0,0 @@
1
- "use strict";
2
- /**
3
- * Resolution of the silent-check-SSO redirect URI.
4
- *
5
- * ## Why this exists
6
- *
7
- * `IAMService.initIAM` used to hardcode:
8
- *
9
- * ```js
10
- * silentCheckSsoRedirectUri: `${window.location.origin}/silent-check-sso.html`
11
- * ```
12
- *
13
- * That is only correct for an app served from the ORIGIN ROOT. For any app
14
- * hosted under a sub-path (e.g. a console served at
15
- * `https://host/realms/<realm>/tide-console/`) the origin-root URI is:
16
- *
17
- * - NOT served (there is no file at `/silent-check-sso.html`), and
18
- * - NOT a registered `redirectUri` on the OIDC client,
19
- *
20
- * so the authorization request fails with `400 Invalid parameter: redirect_uri`.
21
- * The silent iframe then never posts back and the app hangs on its bootstrap
22
- * spinner. A silently-wrong URI is the worst possible outcome, so this module
23
- * either derives a CORRECT base-aware URI or reports that it cannot, letting
24
- * the caller fail loud and fall back to a redirect-based login.
25
- *
26
- * ## Resolution order
27
- *
28
- * 1. **Caller-supplied `silentCheckSsoRedirectUri`** - always wins, verbatim.
29
- * 2. **`<base href>`** - the standard way a sub-path-hosted SPA declares where
30
- * it lives. Resolved via the browser's own `document.baseURI`.
31
- * 3. **The app's configured OIDC `redirectUri`** - the SDK already requires this
32
- * to be base-aware and registered, so it is a sound source of truth for the
33
- * app's base. The SDK's own convention is `<base>auth/redirect`, which is
34
- * stripped to recover `<base>` exactly; any other redirect path is resolved
35
- * relative to its own directory.
36
- * 4. **Origin root** - only when the app declares NEITHER a base NOR a
37
- * redirectUri. Such an app is relying on the SDK's origin-root default
38
- * redirectUri (`${origin}/auth/redirect`) and is therefore root-hosted by
39
- * construction. This preserves the historical behaviour for every existing
40
- * root-hosted consumer.
41
- *
42
- * Anything that cannot be resolved (cross-origin or malformed redirectUri)
43
- * returns `uri: undefined` plus a `reason`, so the caller can log an error and
44
- * SKIP the silent check rather than emit a URI that will 400.
45
- */
46
- Object.defineProperty(exports, "__esModule", { value: true });
47
- exports.SILENT_CHECK_SSO_FILENAME = void 0;
48
- exports.resolveSilentCheckSsoRedirectUri = resolveSilentCheckSsoRedirectUri;
49
- /** Filename the SDK ships and that hosting providers are expected to serve. */
50
- exports.SILENT_CHECK_SSO_FILENAME = 'silent-check-sso.html';
51
- /** The redirect path convention IAMService itself defaults to (`<base>auth/redirect`). */
52
- const AUTH_REDIRECT_SUFFIX = 'auth/redirect';
53
- /**
54
- * @typedef {object} SilentCheckSsoResolution
55
- * @property {string|undefined} uri The resolved URI, or undefined if none could be derived.
56
- * @property {'config'|'base-href'|'redirect-uri-convention'|'redirect-uri'|'origin-root'|'none'} source
57
- * @property {string} [reason] Why resolution failed (only when `uri` is undefined).
58
- */
59
- /**
60
- * Resolve the silent-check-SSO redirect URI.
61
- *
62
- * Pure: every environment input is passed in explicitly, so this is unit-testable
63
- * with no DOM. See the module docblock for the resolution order.
64
- *
65
- * @param {object} params
66
- * @param {string} params.origin The app's origin (`window.location.origin`).
67
- * @param {string} [params.configured] Caller-supplied `silentCheckSsoRedirectUri`.
68
- * @param {string} [params.baseHref] `document.baseURI`, but ONLY when a `<base href>` element exists.
69
- * @param {string} [params.redirectUri] Caller-supplied OIDC `redirectUri`.
70
- * @returns {SilentCheckSsoResolution}
71
- */
72
- function resolveSilentCheckSsoRedirectUri({ origin, configured, baseHref, redirectUri } = {}) {
73
- // 1. An explicit value ALWAYS wins, on every init path. Dropping it is the bug
74
- // that took the sub-path-hosted console down.
75
- if (isNonEmptyString(configured)) {
76
- return { uri: configured.trim(), source: 'config' };
77
- }
78
- if (!isNonEmptyString(origin)) {
79
- return { uri: undefined, source: 'none', reason: 'no origin available (non-browser context)' };
80
- }
81
- // 2. A declared <base href> is the app telling us exactly where it is hosted.
82
- if (isNonEmptyString(baseHref)) {
83
- try {
84
- const base = new URL(baseHref, origin);
85
- return { uri: new URL(exports.SILENT_CHECK_SSO_FILENAME, base).href, source: 'base-href' };
86
- }
87
- catch {
88
- // Malformed <base href> - fall through to the redirectUri, which is more
89
- // load-bearing anyway (login is already broken if it is wrong).
90
- }
91
- }
92
- // 3. Derive the base from the app's own configured OIDC redirectUri.
93
- if (isNonEmptyString(redirectUri)) {
94
- let parsed;
95
- try {
96
- parsed = new URL(redirectUri, origin);
97
- }
98
- catch {
99
- return {
100
- uri: undefined,
101
- source: 'none',
102
- reason: `configured redirectUri (${redirectUri}) is not a valid URL, so the app base cannot be derived`
103
- };
104
- }
105
- if (parsed.origin !== origin) {
106
- return {
107
- uri: undefined,
108
- source: 'none',
109
- reason: `configured redirectUri (${redirectUri}) is on a different origin than the app (${origin}), so the app base cannot be derived`
110
- };
111
- }
112
- // The SDK's own convention: `<base>auth/redirect`. Strip the suffix to
113
- // recover `<base>` (keeping its trailing slash) rather than resolving
114
- // relative to `<base>auth/`, which would be one directory too deep.
115
- if (parsed.pathname.endsWith(`/${AUTH_REDIRECT_SUFFIX}`)) {
116
- const basePath = parsed.pathname.slice(0, parsed.pathname.length - AUTH_REDIRECT_SUFFIX.length);
117
- return {
118
- uri: new URL(exports.SILENT_CHECK_SSO_FILENAME, new URL(basePath, origin)).href,
119
- source: 'redirect-uri-convention'
120
- };
121
- }
122
- // Any other redirect path: resolve the filename against the redirect URI's
123
- // own directory (`/app/callback` -> `/app/silent-check-sso.html`).
124
- return { uri: new URL(exports.SILENT_CHECK_SSO_FILENAME, parsed).href, source: 'redirect-uri' };
125
- }
126
- // 4. Neither a base nor a redirectUri was declared. The app is therefore
127
- // relying on the SDK's origin-root default redirectUri, i.e. it is
128
- // root-hosted, and origin-root is the right answer.
129
- return { uri: `${origin}/${exports.SILENT_CHECK_SSO_FILENAME}`, source: 'origin-root' };
130
- }
131
- /**
132
- * @param {unknown} value
133
- * @returns {value is string}
134
- */
135
- function isNonEmptyString(value) {
136
- return typeof value === 'string' && value.trim() !== '';
137
- }
138
- //# sourceMappingURL=silentCheckSso.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"silentCheckSso.js","sourceRoot":"","sources":["../../../../src/utils/silentCheckSso.js"],"names":[],"mappings":"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"}
@@ -1,43 +0,0 @@
1
- "use strict";
2
- // Serves tide_dpop_auth.html from `vite dev` and `vite preview`:
3
- //
4
- // import { tideDpopPlugin } from '@tidecloak/js/vite';
5
- // export default { plugins: [tideDpopPlugin({ config: tidecloak })] };
6
- //
7
- // A static production build still needs the two headers set on the host.
8
- Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.tideDpopPlugin = tideDpopPlugin;
10
- const dpopServer_js_1 = require("./dpopServer.js");
11
- /**
12
- * @param {Object} [options]
13
- * @param {Object} [options.config] TideCloak adapter config (tidecloak.json). Used to
14
- * reject requests for another realm or client.
15
- */
16
- function tideDpopPlugin({ config } = {}) {
17
- const { issuer, client } = (0, dpopServer_js_1.expectationsFromConfig)(config);
18
- const middleware = (req, res, next) => {
19
- var _a, _b;
20
- const result = (0, dpopServer_js_1.resolveDpopAuthRequest)({
21
- pathname: (_a = req.url) !== null && _a !== void 0 ? _a : '',
22
- expectedIssuer: issuer,
23
- expectedClient: client,
24
- method: req.method,
25
- });
26
- if (!result)
27
- return next();
28
- res.statusCode = result.status;
29
- for (const [name, value] of Object.entries(result.headers))
30
- res.setHeader(name, value);
31
- res.end((_b = result.body) !== null && _b !== void 0 ? _b : '');
32
- };
33
- const install = (server) => {
34
- if (!issuer || !client) {
35
- console.warn('[tidecloak] Serving the DPoP page without an issuer/client check. Pass `config` to enable it.');
36
- }
37
- // Registered here, before Vite's own middleware, so the SPA fallback cannot answer first.
38
- server.middlewares.use(middleware);
39
- };
40
- return { name: 'tidecloak-dpop-auth-page', configureServer: install, configurePreviewServer: install };
41
- }
42
- exports.default = tideDpopPlugin;
43
- //# sourceMappingURL=vite.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"vite.js","sourceRoot":"","sources":["../../../src/vite.js"],"names":[],"mappings":";AAAA,iEAAiE;AACjE,EAAE;AACF,yDAAyD;AACzD,yEAAyE;AACzE,EAAE;AACF,yEAAyE;;;AAEzE,mDAAiF;AAEjF;;;;GAIG;AACH,wBAA+B,EAAE,MAAM,EAAE,GAAG,EAAE;IAC5C,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,IAAA,sCAAsB,EAAC,MAAM,CAAC,CAAC;IAE1D,MAAM,UAAU,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;;QACpC,MAAM,MAAM,GAAG,IAAA,sCAAsB,EAAC;YACpC,QAAQ,QAAE,GAAG,CAAC,GAAG,mCAAI,EAAE;YACvB,cAAc,EAAE,MAAM;YACtB,cAAc,EAAE,MAAM;YACtB,MAAM,EAAE,GAAG,CAAC,MAAM;SACnB,CAAC,CAAC;QACH,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,EAAE,CAAC;QAE3B,GAAG,CAAC,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC;QAC/B,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC;YAAE,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACvF,GAAG,CAAC,GAAG,OAAC,MAAM,CAAC,IAAI,mCAAI,EAAE,CAAC,CAAC;IAC7B,CAAC,CAAC;IAEF,MAAM,OAAO,GAAG,CAAC,MAAM,EAAE,EAAE;QACzB,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;YACvB,OAAO,CAAC,IAAI,CAAC,+FAA+F,CAAC,CAAC;QAChH,CAAC;QACD,0FAA0F;QAC1F,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACrC,CAAC,CAAC;IAEF,OAAO,EAAE,IAAI,EAAE,0BAA0B,EAAE,eAAe,EAAE,OAAO,EAAE,sBAAsB,EAAE,OAAO,EAAE,CAAC;AACzG,CAAC;kBAEc,cAAc"}
@@ -1,42 +0,0 @@
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, ourToken) {
38
- if (existingAuth === null || existingAuth === undefined)
39
- return true;
40
- return existingAuth === `Bearer ${ourToken}`;
41
- }
42
- //# sourceMappingURL=secureFetchPolicy.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"secureFetchPolicy.js","sourceRoot":"","sources":["../../../lib/secureFetchPolicy.js"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,UAAU,qBAAqB,CAAE,YAAY,EAAE,QAAQ;IAC3D,IAAI,YAAY,KAAK,IAAI,IAAI,YAAY,KAAK,SAAS;QAAE,OAAO,IAAI,CAAA;IACpE,OAAO,YAAY,KAAK,UAAU,QAAQ,EAAE,CAAA;AAC9C,CAAC"}
@@ -1,27 +0,0 @@
1
- // Serves tide_dpop_auth.html at /tide_dpop/iss/<hex>/aud/<hex>/tide_dpop_auth.html.
2
- // Uses web Request/Response, so it drops into a Next.js route handler:
3
- //
4
- // // app/tide_dpop/[...path]/route.js
5
- // export const { GET, HEAD } = createDpopRoute({ config });
6
- import { resolveDpopAuthRequest, expectationsFromConfig } from './dpopServer.js';
7
- /**
8
- * @param {Object} [options]
9
- * @param {Object} [options.config] TideCloak adapter config (tidecloak.json). Used to
10
- * reject requests for another realm or client. Omitted or empty, it serves any.
11
- */
12
- export function createDpopRoute({ config = {} } = {}) {
13
- const { issuer, client } = expectationsFromConfig(config);
14
- const handle = (request) => {
15
- const result = resolveDpopAuthRequest({
16
- pathname: new URL(request.url).pathname,
17
- expectedIssuer: issuer,
18
- expectedClient: client,
19
- method: request.method,
20
- });
21
- if (!result)
22
- return new Response('Not Found', { status: 404, headers: { 'Content-Type': 'text/plain' } });
23
- return new Response(result.body, { status: result.status, headers: result.headers });
24
- };
25
- return { GET: handle, HEAD: handle };
26
- }
27
- //# sourceMappingURL=dpopRoute.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"dpopRoute.js","sourceRoot":"","sources":["../../../src/dpopRoute.js"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,uEAAuE;AACvE,EAAE;AACF,wCAAwC;AACxC,8DAA8D;AAE9D,OAAO,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAEjF;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,EAAE,MAAM,GAAG,EAAE,EAAE,GAAG,EAAE;IAClD,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,sBAAsB,CAAC,MAAM,CAAC,CAAC;IAE1D,MAAM,MAAM,GAAG,CAAC,OAAO,EAAE,EAAE;QACzB,MAAM,MAAM,GAAG,sBAAsB,CAAC;YACpC,QAAQ,EAAE,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,QAAQ;YACvC,cAAc,EAAE,MAAM;YACtB,cAAc,EAAE,MAAM;YACtB,MAAM,EAAE,OAAO,CAAC,MAAM;SACvB,CAAC,CAAC;QACH,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,QAAQ,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,YAAY,EAAE,EAAE,CAAC,CAAC;QAC1G,OAAO,IAAI,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;IACvF,CAAC,CAAC;IAEF,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AACvC,CAAC"}
@@ -1,68 +0,0 @@
1
- // Framework-neutral core for serving tide_dpop_auth.html. No fs, crypto or
2
- // Buffer, so it also runs on Edge. Adapters: ./dpopRoute.js and ./vite.js.
3
- import { DPOP_AUTH_HTML, DPOP_AUTH_CSP } from './generated/dpopAuthAsset.js';
4
- export { DPOP_AUTH_HTML, DPOP_AUTH_CSP };
5
- const PATH_RE = /^\/tide_dpop\/iss\/([0-9a-fA-F]+)\/aud\/([0-9a-fA-F]+)\/tide_dpop_auth\.html$/;
6
- // X-Frame-Options or frame-ancestors would block the enclave's embed.
7
- export function dpopAuthHeaders() {
8
- return {
9
- 'Content-Type': 'text/html; charset=utf-8',
10
- 'Content-Security-Policy': DPOP_AUTH_CSP,
11
- 'Allow-CSP-From': '*',
12
- 'Cache-Control': 'no-store',
13
- };
14
- }
15
- export function hexToUtf8(hex) {
16
- if (!hex || hex.length % 2 !== 0 || !/^[0-9a-fA-F]+$/.test(hex))
17
- return null;
18
- const bytes = new Uint8Array(hex.length / 2);
19
- for (let i = 0; i < bytes.length; i++)
20
- bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
21
- try {
22
- return new TextDecoder('utf-8', { fatal: true }).decode(bytes);
23
- }
24
- catch {
25
- return null;
26
- }
27
- }
28
- export function dpopAuthPathFor(issuer, clientId) {
29
- const hex = (s) => [...new TextEncoder().encode(s)].map((b) => b.toString(16).padStart(2, '0')).join('');
30
- return `/tide_dpop/iss/${hex(issuer)}/aud/${hex(clientId)}/tide_dpop_auth.html`;
31
- }
32
- /**
33
- * Issuer and client id to accept, from a TideCloak adapter config.
34
- * Either is undefined when the config does not carry it (e.g. the `{}` placeholder).
35
- */
36
- export function expectationsFromConfig(config = {}) {
37
- const url = config['auth-server-url'];
38
- return {
39
- issuer: url && config.realm ? `${String(url).replace(/\/+$/, '')}/realms/${config.realm}` : undefined,
40
- client: config.resource || undefined,
41
- };
42
- }
43
- /**
44
- * The response for a request path, as `{ status, headers, body }`, or null when
45
- * the path is not the DPoP page. Omitted expectations are not checked.
46
- */
47
- export function resolveDpopAuthRequest({ pathname, expectedIssuer, expectedClient, method = 'GET' }) {
48
- const match = String(pathname !== null && pathname !== void 0 ? pathname : '').split('?')[0].match(PATH_RE);
49
- if (!match)
50
- return null;
51
- const text = (status, body, extra = {}) => ({
52
- status,
53
- headers: { 'Content-Type': 'text/plain; charset=utf-8', 'Cache-Control': 'no-store', ...extra },
54
- body,
55
- });
56
- const verb = String(method).toUpperCase();
57
- if (verb !== 'GET' && verb !== 'HEAD')
58
- return text(405, 'Method Not Allowed', { Allow: 'GET, HEAD' });
59
- const issuer = hexToUtf8(match[1]);
60
- const client = hexToUtf8(match[2]);
61
- if (issuer === null || client === null)
62
- return text(400, 'Invalid hex encoding in URL');
63
- if ((expectedIssuer && issuer !== expectedIssuer) || (expectedClient && client !== expectedClient)) {
64
- return text(403, 'Issuer or client mismatch');
65
- }
66
- return { status: 200, headers: dpopAuthHeaders(), body: verb === 'HEAD' ? null : DPOP_AUTH_HTML };
67
- }
68
- //# sourceMappingURL=dpopServer.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"dpopServer.js","sourceRoot":"","sources":["../../../src/dpopServer.js"],"names":[],"mappings":"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"}