@axa-fr/oidc-client-service-worker 7.29.8 → 7.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/README.md +64 -0
  2. package/dist/OidcServiceWorker.d.ts +2 -1
  3. package/dist/OidcServiceWorker.d.ts.map +1 -0
  4. package/dist/OidcServiceWorker.js +995 -765
  5. package/dist/OidcServiceWorker.js.map +1 -1
  6. package/dist/constants.d.ts.map +1 -0
  7. package/dist/crypto.d.ts.map +1 -0
  8. package/dist/dpop.d.ts.map +1 -0
  9. package/dist/jwt.d.ts.map +1 -0
  10. package/dist/oidcConfig.d.ts.map +1 -0
  11. package/dist/protocol.d.ts +140 -2
  12. package/dist/protocol.d.ts.map +1 -0
  13. package/dist/protocol.js +50 -2
  14. package/dist/protocol.js.map +1 -1
  15. package/dist/resource.d.ts +22 -0
  16. package/dist/resource.d.ts.map +1 -0
  17. package/dist/tokenContexts.d.ts +5 -0
  18. package/dist/tokenContexts.d.ts.map +1 -0
  19. package/dist/{src/types.d.ts → types.d.ts} +26 -1
  20. package/dist/types.d.ts.map +1 -0
  21. package/dist/utils/codeVerifier.d.ts.map +1 -0
  22. package/dist/{src/utils → utils}/domains.d.ts +2 -2
  23. package/dist/utils/domains.d.ts.map +1 -0
  24. package/dist/utils/index.d.ts.map +1 -0
  25. package/dist/utils/normalizeUrl.d.ts.map +1 -0
  26. package/dist/utils/serializeHeaders.d.ts.map +1 -0
  27. package/dist/utils/shouldBypassDestination.d.ts.map +1 -0
  28. package/dist/utils/sleep.d.ts.map +1 -0
  29. package/dist/utils/strings.d.ts.map +1 -0
  30. package/dist/{src/utils → utils}/tokens.d.ts +12 -3
  31. package/dist/utils/tokens.d.ts.map +1 -0
  32. package/dist/utils/waitForValidTokens.d.ts.map +1 -0
  33. package/dist/version.d.ts +3 -0
  34. package/dist/version.d.ts.map +1 -0
  35. package/package.json +3 -2
  36. package/src/OidcServiceWorker.ts +290 -34
  37. package/src/__tests__/jwt.spec.ts +17 -0
  38. package/src/__tests__/resource.spec.ts +98 -0
  39. package/src/__tests__/workerResource.spec.ts +919 -0
  40. package/src/jwt.ts +4 -1
  41. package/src/oidcConfig.ts +2 -1
  42. package/src/protocol.ts +16 -1
  43. package/src/resource.ts +104 -0
  44. package/src/tokenContexts.ts +39 -0
  45. package/src/types.ts +27 -1
  46. package/src/utils/domains.ts +20 -2
  47. package/src/utils/tokens.ts +155 -8
  48. package/src/version.ts +1 -1
  49. package/dist/src/OidcServiceWorker.d.ts +0 -2
  50. package/dist/src/OidcServiceWorker.d.ts.map +0 -1
  51. package/dist/src/__tests__/jwt.spec.d.ts +0 -2
  52. package/dist/src/__tests__/jwt.spec.d.ts.map +0 -1
  53. package/dist/src/__tests__/oidcConfig.spec.d.ts +0 -2
  54. package/dist/src/__tests__/oidcConfig.spec.d.ts.map +0 -1
  55. package/dist/src/__tests__/protocol.spec.d.ts +0 -2
  56. package/dist/src/__tests__/protocol.spec.d.ts.map +0 -1
  57. package/dist/src/constants.d.ts.map +0 -1
  58. package/dist/src/crypto.d.ts.map +0 -1
  59. package/dist/src/dpop.d.ts.map +0 -1
  60. package/dist/src/jwt.d.ts.map +0 -1
  61. package/dist/src/oidcConfig.d.ts.map +0 -1
  62. package/dist/src/protocol.d.ts +0 -134
  63. package/dist/src/protocol.d.ts.map +0 -1
  64. package/dist/src/types.d.ts.map +0 -1
  65. package/dist/src/utils/__tests__/codeVerifier.spec.d.ts +0 -2
  66. package/dist/src/utils/__tests__/codeVerifier.spec.d.ts.map +0 -1
  67. package/dist/src/utils/__tests__/domains.spec.d.ts +0 -2
  68. package/dist/src/utils/__tests__/domains.spec.d.ts.map +0 -1
  69. package/dist/src/utils/__tests__/normalizeUrl.spec.d.ts +0 -2
  70. package/dist/src/utils/__tests__/normalizeUrl.spec.d.ts.map +0 -1
  71. package/dist/src/utils/__tests__/serializeHeaders.spec.d.ts +0 -2
  72. package/dist/src/utils/__tests__/serializeHeaders.spec.d.ts.map +0 -1
  73. package/dist/src/utils/__tests__/shouldBypassDestination.spec.d.ts +0 -2
  74. package/dist/src/utils/__tests__/shouldBypassDestination.spec.d.ts.map +0 -1
  75. package/dist/src/utils/__tests__/strings.spec.d.ts +0 -2
  76. package/dist/src/utils/__tests__/strings.spec.d.ts.map +0 -1
  77. package/dist/src/utils/__tests__/testHelper.d.ts +0 -58
  78. package/dist/src/utils/__tests__/testHelper.d.ts.map +0 -1
  79. package/dist/src/utils/__tests__/tokens.spec.d.ts +0 -2
  80. package/dist/src/utils/__tests__/tokens.spec.d.ts.map +0 -1
  81. package/dist/src/utils/__tests__/waitForValidTokens.spec.d.ts +0 -2
  82. package/dist/src/utils/__tests__/waitForValidTokens.spec.d.ts.map +0 -1
  83. package/dist/src/utils/codeVerifier.d.ts.map +0 -1
  84. package/dist/src/utils/domains.d.ts.map +0 -1
  85. package/dist/src/utils/index.d.ts.map +0 -1
  86. package/dist/src/utils/normalizeUrl.d.ts.map +0 -1
  87. package/dist/src/utils/serializeHeaders.d.ts.map +0 -1
  88. package/dist/src/utils/shouldBypassDestination.d.ts.map +0 -1
  89. package/dist/src/utils/sleep.d.ts.map +0 -1
  90. package/dist/src/utils/strings.d.ts.map +0 -1
  91. package/dist/src/utils/tokens.d.ts.map +0 -1
  92. package/dist/src/utils/waitForValidTokens.d.ts.map +0 -1
  93. package/dist/src/version.d.ts +0 -3
  94. package/dist/src/version.d.ts.map +0 -1
  95. /package/dist/{src/constants.d.ts → constants.d.ts} +0 -0
  96. /package/dist/{src/crypto.d.ts → crypto.d.ts} +0 -0
  97. /package/dist/{src/dpop.d.ts → dpop.d.ts} +0 -0
  98. /package/dist/{src/jwt.d.ts → jwt.d.ts} +0 -0
  99. /package/dist/{src/oidcConfig.d.ts → oidcConfig.d.ts} +0 -0
  100. /package/dist/{src/utils → utils}/codeVerifier.d.ts +0 -0
  101. /package/dist/{src/utils → utils}/index.d.ts +0 -0
  102. /package/dist/{src/utils → utils}/normalizeUrl.d.ts +0 -0
  103. /package/dist/{src/utils → utils}/serializeHeaders.d.ts +0 -0
  104. /package/dist/{src/utils → utils}/shouldBypassDestination.d.ts +0 -0
  105. /package/dist/{src/utils → utils}/sleep.d.ts +0 -0
  106. /package/dist/{src/utils → utils}/strings.d.ts +0 -0
  107. /package/dist/{src/utils → utils}/waitForValidTokens.d.ts +0 -0
@@ -1,819 +1,1049 @@
1
- //#region src/constants.ts
2
- var scriptFilename = "OidcTrustedDomains.js";
3
- var TOKEN = {
4
- REFRESH_TOKEN: "REFRESH_TOKEN_SECURED_BY_OIDC_SERVICE_WORKER",
5
- ACCESS_TOKEN: "ACCESS_TOKEN_SECURED_BY_OIDC_SERVICE_WORKER",
6
- NONCE_TOKEN: "NONCE_SECURED_BY_OIDC_SERVICE_WORKER",
7
- CODE_VERIFIER: "CODE_VERIFIER_SECURED_BY_OIDC_SERVICE_WORKER"
8
- };
9
- var TokenRenewMode = {
10
- access_token_or_id_token_invalid: "access_token_or_id_token_invalid",
11
- access_token_invalid: "access_token_invalid",
12
- id_token_invalid: "id_token_invalid"
13
- };
14
- //#endregion
15
- //#region src/jwt.ts
16
- function strToUint8(str) {
17
- return new TextEncoder().encode(str);
18
- }
19
- function binToUrlBase64(bin) {
20
- return btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+/g, "");
21
- }
22
- function utf8ToBinaryString(str) {
23
- return encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function(match, p1) {
24
- return String.fromCharCode(parseInt(p1, 16));
25
- });
26
- }
27
- var uint8ToUrlBase64 = (uint8) => {
28
- let bin = "";
29
- uint8.forEach(function(code) {
30
- bin += String.fromCharCode(code);
31
- });
32
- return binToUrlBase64(bin);
33
- };
34
- function strToUrlBase64(str) {
35
- return binToUrlBase64(utf8ToBinaryString(str));
36
- }
37
- var defaultDemonstratingProofOfPossessionConfiguration = {
38
- importKeyAlgorithm: {
39
- name: "ECDSA",
40
- namedCurve: "P-256",
41
- hash: { name: "ES256" }
42
- },
43
- signAlgorithm: {
44
- name: "ECDSA",
45
- hash: { name: "SHA-256" }
46
- },
47
- generateKeyAlgorithm: {
48
- name: "ECDSA",
49
- namedCurve: "P-256"
50
- },
51
- digestAlgorithm: { name: "SHA-256" },
52
- jwtHeaderAlgorithm: "ES256"
53
- };
54
- var sign = (w) => async (jwk, headers, claims, demonstratingProofOfPossessionConfiguration, jwtHeaderType = "dpop+jwt") => {
55
- jwk = Object.assign({}, jwk);
56
- headers.typ = jwtHeaderType;
57
- headers.alg = demonstratingProofOfPossessionConfiguration.jwtHeaderAlgorithm;
58
- switch (headers.alg) {
59
- case "ES256":
60
- headers.jwk = {
61
- kty: jwk.kty,
62
- crv: jwk.crv,
63
- x: jwk.x,
64
- y: jwk.y
65
- };
66
- break;
67
- case "RS256":
68
- headers.jwk = {
69
- kty: jwk.kty,
70
- n: jwk.n,
71
- e: jwk.e,
72
- kid: headers.kid
73
- };
74
- break;
75
- default: throw new Error("Unknown or not implemented JWS algorithm");
1
+ (function() {
2
+ //#region src/constants.ts
3
+ var scriptFilename = "OidcTrustedDomains.js";
4
+ var TOKEN = {
5
+ REFRESH_TOKEN: "REFRESH_TOKEN_SECURED_BY_OIDC_SERVICE_WORKER",
6
+ ACCESS_TOKEN: "ACCESS_TOKEN_SECURED_BY_OIDC_SERVICE_WORKER",
7
+ NONCE_TOKEN: "NONCE_SECURED_BY_OIDC_SERVICE_WORKER",
8
+ CODE_VERIFIER: "CODE_VERIFIER_SECURED_BY_OIDC_SERVICE_WORKER"
9
+ };
10
+ var TokenRenewMode = {
11
+ access_token_or_id_token_invalid: "access_token_or_id_token_invalid",
12
+ access_token_invalid: "access_token_invalid",
13
+ id_token_invalid: "id_token_invalid"
14
+ };
15
+ //#endregion
16
+ //#region src/jwt.ts
17
+ function strToUint8(str) {
18
+ return new TextEncoder().encode(str);
76
19
  }
77
- const jws = {
78
- protected: strToUrlBase64(JSON.stringify(headers)),
79
- payload: strToUrlBase64(JSON.stringify(claims))
80
- };
81
- const keyType = demonstratingProofOfPossessionConfiguration.importKeyAlgorithm;
82
- const privateKey = await w.crypto.subtle.importKey("jwk", jwk, keyType, true, ["sign"]);
83
- const data = strToUint8(`${jws.protected}.${jws.payload}`);
84
- const signatureType = demonstratingProofOfPossessionConfiguration.signAlgorithm;
85
- const signature = await w.crypto.subtle.sign(signatureType, privateKey, data);
86
- jws.signature = uint8ToUrlBase64(new Uint8Array(signature));
87
- return `${jws.protected}.${jws.payload}.${jws.signature}`;
88
- };
89
- var JWT = { sign };
90
- var generate = (w) => async (generateKeyAlgorithm) => {
91
- const keyType = generateKeyAlgorithm;
92
- const key = await w.crypto.subtle.generateKey(keyType, true, ["sign", "verify"]);
93
- return await w.crypto.subtle.exportKey("jwk", key.privateKey);
94
- };
95
- var neuter = (jwk) => {
96
- const copy = Object.assign({}, jwk);
97
- delete copy.d;
98
- copy.key_ops = ["verify"];
99
- return copy;
100
- };
101
- var EC = {
102
- generate,
103
- neuter
104
- };
105
- var thumbprint = (w) => async (jwk, digestAlgorithm) => {
106
- let sortedPub;
107
- switch (jwk.kty) {
108
- case "EC":
109
- sortedPub = "{\"crv\":\"CRV\",\"kty\":\"EC\",\"x\":\"X\",\"y\":\"Y\"}".replace("CRV", jwk.crv).replace("X", jwk.x).replace("Y", jwk.y);
110
- break;
111
- case "RSA":
112
- sortedPub = "{\"e\":\"E\",\"kty\":\"RSA\",\"n\":\"N\"}".replace("E", jwk.e).replace("N", jwk.n);
113
- break;
114
- default: throw new Error("Unknown or not implemented JWK type");
20
+ function binToUrlBase64(bin) {
21
+ return btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+/g, "");
115
22
  }
116
- const hash = await w.crypto.subtle.digest(digestAlgorithm, strToUint8(sortedPub));
117
- return uint8ToUrlBase64(new Uint8Array(hash));
118
- };
119
- var JWK = { thumbprint };
120
- var generateJwkAsync = (w) => async (generateKeyAlgorithm) => {
121
- return await EC.generate(w)(generateKeyAlgorithm);
122
- };
123
- var generateJwtDemonstratingProofOfPossessionAsync = (w) => (demonstratingProofOfPossessionConfiguration) => async (jwk, method = "POST", url, extrasClaims = {}) => {
124
- const claims = {
125
- jti: btoa(w.crypto.randomUUID()),
126
- htm: method,
127
- htu: url,
128
- iat: Math.round(Date.now() / 1e3),
129
- ...extrasClaims
130
- };
131
- const kid = await JWK.thumbprint(w)(jwk, demonstratingProofOfPossessionConfiguration.digestAlgorithm);
132
- return await JWT.sign(w)(jwk, { kid }, claims, demonstratingProofOfPossessionConfiguration);
133
- };
134
- //#endregion
135
- //#region src/crypto.ts
136
- function textEncodeLite(str) {
137
- const buf = new ArrayBuffer(str.length);
138
- const bufView = new Uint8Array(buf);
139
- for (let i = 0; i < str.length; i++) bufView[i] = str.charCodeAt(i);
140
- return bufView;
141
- }
142
- function base64urlOfHashOfASCIIEncodingAsync(code) {
143
- return new Promise((resolve, reject) => {
144
- crypto.subtle.digest("SHA-256", textEncodeLite(code)).then((buffer) => {
145
- return resolve(uint8ToUrlBase64(new Uint8Array(buffer)));
146
- }, (error) => reject(error));
147
- });
148
- }
149
- //#endregion
150
- //#region src/dpop.ts
151
- var isDpop = (trustedDomain) => {
152
- if (Array.isArray(trustedDomain)) return false;
153
- return trustedDomain.demonstratingProofOfPossession ?? false;
154
- };
155
- var getDpopConfiguration = (trustedDomain) => {
156
- if (!isDpop(trustedDomain)) return null;
157
- if (Array.isArray(trustedDomain)) return null;
158
- return trustedDomain.demonstratingProofOfPossessionConfiguration ?? defaultDemonstratingProofOfPossessionConfiguration;
159
- };
160
- var getDpopOnlyWhenDpopHeaderPresent = (trustedDomain) => {
161
- if (!isDpop(trustedDomain)) return null;
162
- if (Array.isArray(trustedDomain)) return null;
163
- return trustedDomain.demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent ?? true;
164
- };
165
- //#endregion
166
- //#region src/utils/normalizeUrl.ts
167
- function normalizeUrl(url) {
168
- try {
169
- return new URL(url).toString();
170
- } catch (error) {
171
- console.error(`Failed to normalize url: ${url}`, error);
172
- return url;
23
+ function utf8ToBinaryString(str) {
24
+ return encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function(match, p1) {
25
+ return String.fromCharCode(parseInt(p1, 16));
26
+ });
27
+ }
28
+ var uint8ToUrlBase64 = (uint8) => {
29
+ let bin = "";
30
+ uint8.forEach(function(code) {
31
+ bin += String.fromCharCode(code);
32
+ });
33
+ return binToUrlBase64(bin);
34
+ };
35
+ function strToUrlBase64(str) {
36
+ return binToUrlBase64(utf8ToBinaryString(str));
173
37
  }
174
- }
175
- //#endregion
176
- //#region src/utils/domains.ts
177
- function checkDomain(domains, endpoint) {
178
- if (!endpoint) return;
179
- if (!domains.find((domain) => {
180
- let testable;
181
- if (typeof domain === "string") testable = new RegExp(`^${domain}`);
182
- else testable = domain;
183
- return testable.test?.(endpoint);
184
- })) throw new Error("Domain " + endpoint + " is not trusted, please add domain in " + scriptFilename);
185
- }
186
- var getDomains = (trustedDomain, type) => {
187
- if (Array.isArray(trustedDomain)) return trustedDomain;
188
- return trustedDomain[`${type}Domains`] ?? trustedDomain.domains ?? [];
189
- };
190
- var getCurrentDatabaseDomain = (database, url, trustedDomains) => {
191
- if (url.endsWith("/.well-known/openid-configuration")) return null;
192
- const datatases = [];
193
- for (const [key, currentDatabase] of Object.entries(database)) {
194
- const oidcServerConfiguration = currentDatabase.oidcServerConfiguration;
195
- if (!oidcServerConfiguration) continue;
196
- if (oidcServerConfiguration.tokenEndpoint && url === normalizeUrl(oidcServerConfiguration.tokenEndpoint)) continue;
197
- if (oidcServerConfiguration.revocationEndpoint && url === normalizeUrl(oidcServerConfiguration.revocationEndpoint)) continue;
198
- const domains = getDomains(trustedDomains == null ? [] : trustedDomains[key.split("#")[0]], "accessToken");
199
- const domainsToSendTokens = oidcServerConfiguration.userInfoEndpoint ? [normalizeUrl(oidcServerConfiguration.userInfoEndpoint), ...domains] : [...domains];
200
- let hasToSendToken = false;
201
- if (domainsToSendTokens.find((f) => f === "*")) hasToSendToken = true;
202
- else for (let i = 0; i < domainsToSendTokens.length; i++) {
203
- let domain = domainsToSendTokens[i];
204
- if (typeof domain === "string") domain = new RegExp(`^${domain}`);
205
- if (domain.test?.(url)) {
206
- hasToSendToken = true;
38
+ var defaultDemonstratingProofOfPossessionConfiguration = {
39
+ importKeyAlgorithm: {
40
+ name: "ECDSA",
41
+ namedCurve: "P-256",
42
+ hash: { name: "ES256" }
43
+ },
44
+ signAlgorithm: {
45
+ name: "ECDSA",
46
+ hash: { name: "SHA-256" }
47
+ },
48
+ generateKeyAlgorithm: {
49
+ name: "ECDSA",
50
+ namedCurve: "P-256"
51
+ },
52
+ digestAlgorithm: { name: "SHA-256" },
53
+ jwtHeaderAlgorithm: "ES256"
54
+ };
55
+ var sign = (w) => async (jwk, headers, claims, demonstratingProofOfPossessionConfiguration, jwtHeaderType = "dpop+jwt") => {
56
+ jwk = Object.assign({}, jwk);
57
+ headers.typ = jwtHeaderType;
58
+ headers.alg = demonstratingProofOfPossessionConfiguration.jwtHeaderAlgorithm;
59
+ switch (headers.alg) {
60
+ case "ES256":
61
+ headers.jwk = {
62
+ kty: jwk.kty,
63
+ crv: jwk.crv,
64
+ x: jwk.x,
65
+ y: jwk.y
66
+ };
207
67
  break;
208
- }
68
+ case "RS256":
69
+ headers.jwk = {
70
+ kty: jwk.kty,
71
+ n: jwk.n,
72
+ e: jwk.e,
73
+ kid: headers.kid
74
+ };
75
+ break;
76
+ default: throw new Error("Unknown or not implemented JWS algorithm");
209
77
  }
210
- if (hasToSendToken) {
211
- if (currentDatabase.tokens) datatases.push(currentDatabase);
78
+ const jws = {
79
+ protected: strToUrlBase64(JSON.stringify(headers)),
80
+ payload: strToUrlBase64(JSON.stringify(claims))
81
+ };
82
+ const keyType = demonstratingProofOfPossessionConfiguration.importKeyAlgorithm;
83
+ const privateKey = await w.crypto.subtle.importKey("jwk", jwk, keyType, true, ["sign"]);
84
+ const data = strToUint8(`${jws.protected}.${jws.payload}`);
85
+ const signatureType = demonstratingProofOfPossessionConfiguration.signAlgorithm;
86
+ const signature = await w.crypto.subtle.sign(signatureType, privateKey, data);
87
+ jws.signature = uint8ToUrlBase64(new Uint8Array(signature));
88
+ return `${jws.protected}.${jws.payload}.${jws.signature}`;
89
+ };
90
+ var JWT = { sign };
91
+ var generate = (w) => async (generateKeyAlgorithm) => {
92
+ const keyType = generateKeyAlgorithm;
93
+ const key = await w.crypto.subtle.generateKey(keyType, true, ["sign", "verify"]);
94
+ return await w.crypto.subtle.exportKey("jwk", key.privateKey);
95
+ };
96
+ var neuter = (jwk) => {
97
+ const copy = Object.assign({}, jwk);
98
+ delete copy.d;
99
+ copy.key_ops = ["verify"];
100
+ return copy;
101
+ };
102
+ var EC = {
103
+ generate,
104
+ neuter
105
+ };
106
+ var thumbprint = (w) => async (jwk, digestAlgorithm) => {
107
+ let sortedPub;
108
+ switch (jwk.kty) {
109
+ case "EC":
110
+ sortedPub = "{\"crv\":\"CRV\",\"kty\":\"EC\",\"x\":\"X\",\"y\":\"Y\"}".replace("CRV", jwk.crv).replace("X", jwk.x).replace("Y", jwk.y);
111
+ break;
112
+ case "RSA":
113
+ sortedPub = "{\"e\":\"E\",\"kty\":\"RSA\",\"n\":\"N\"}".replace("E", jwk.e).replace("N", jwk.n);
114
+ break;
115
+ default: throw new Error("Unknown or not implemented JWK type");
212
116
  }
117
+ const hash = await w.crypto.subtle.digest(digestAlgorithm, strToUint8(sortedPub));
118
+ return uint8ToUrlBase64(new Uint8Array(hash));
119
+ };
120
+ var JWK = { thumbprint };
121
+ var generateJwkAsync = (w) => async (generateKeyAlgorithm) => {
122
+ return await EC.generate(w)(generateKeyAlgorithm);
123
+ };
124
+ var generateJwtDemonstratingProofOfPossessionAsync = (w) => (demonstratingProofOfPossessionConfiguration) => async (jwk, method = "POST", url, extrasClaims = {}) => {
125
+ const target = new URL(url, w.location?.href);
126
+ target.search = "";
127
+ target.hash = "";
128
+ const claims = {
129
+ jti: btoa(w.crypto.randomUUID()),
130
+ htm: method,
131
+ htu: target.href,
132
+ iat: Math.round(Date.now() / 1e3),
133
+ ...extrasClaims
134
+ };
135
+ const kid = await JWK.thumbprint(w)(jwk, demonstratingProofOfPossessionConfiguration.digestAlgorithm);
136
+ return await JWT.sign(w)(jwk, { kid }, claims, demonstratingProofOfPossessionConfiguration);
137
+ };
138
+ //#endregion
139
+ //#region src/crypto.ts
140
+ function textEncodeLite(str) {
141
+ const buf = new ArrayBuffer(str.length);
142
+ const bufView = new Uint8Array(buf);
143
+ for (let i = 0; i < str.length; i++) bufView[i] = str.charCodeAt(i);
144
+ return bufView;
213
145
  }
214
- return datatases;
215
- };
216
- //#endregion
217
- //#region src/utils/serializeHeaders.ts
218
- function serializeHeaders(headers) {
219
- const headersObj = {};
220
- for (const key of headers.keys()) {
221
- const value = headers.get(key);
222
- if (value !== null) headersObj[key] = value;
223
- }
224
- return headersObj;
225
- }
226
- //#endregion
227
- //#region src/utils/shouldBypassDestination.ts
228
- var bypassedDestinations = [
229
- "image",
230
- "font",
231
- "media",
232
- "document",
233
- "iframe",
234
- "script"
235
- ];
236
- /**
237
- * Determines whether a fetch event should be bypassed (not intercepted by the service worker).
238
- * Navigation requests (mode === 'navigate') are never bypassed, even when their destination
239
- * is in the bypassed list, so that access tokens can be injected into new-tab navigations.
240
- */
241
- var shouldBypassDestination = (destination, mode) => {
242
- return bypassedDestinations.includes(destination) && mode !== "navigate";
243
- };
244
- //#endregion
245
- //#region src/utils/sleep.ts
246
- var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
247
- //#endregion
248
- //#region src/utils/tokens.ts
249
- var parseJwt = (payload) => {
250
- return JSON.parse(b64DecodeUnicode(payload.replaceAll(/-/g, "+").replaceAll(/_/g, "/")));
251
- };
252
- function b64DecodeUnicode(str) {
253
- return decodeURIComponent(Array.prototype.map.call(atob(str), (c) => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2)).join(""));
254
- }
255
- function computeTimeLeft(refreshTimeBeforeTokensExpirationInSecond, expiresAt) {
256
- const currentTimeUnixSecond = (/* @__PURE__ */ new Date()).getTime() / 1e3;
257
- return Math.round(expiresAt - refreshTimeBeforeTokensExpirationInSecond - currentTimeUnixSecond);
258
- }
259
- function isTokensValid(tokens) {
260
- if (!tokens) return false;
261
- return computeTimeLeft(0, tokens.expiresAt) > 0;
262
- }
263
- var extractTokenPayload = (token) => {
264
- try {
265
- if (!token) return null;
266
- const parts = token.split(".");
267
- return parts.length === 3 ? parseJwt(parts[1]) : null;
268
- } catch (e) {
269
- console.warn(e);
146
+ function base64urlOfHashOfASCIIEncodingAsync(code) {
147
+ return new Promise((resolve, reject) => {
148
+ crypto.subtle.digest("SHA-256", textEncodeLite(code)).then((buffer) => {
149
+ return resolve(uint8ToUrlBase64(new Uint8Array(buffer)));
150
+ }, (error) => reject(error));
151
+ });
270
152
  }
271
- return null;
272
- };
273
- var isTokensOidcValid = (tokens, nonce, oidcServerConfiguration) => {
274
- if (tokens.idTokenPayload) {
275
- const idTokenPayload = tokens.idTokenPayload;
276
- if (idTokenPayload && oidcServerConfiguration.issuer !== idTokenPayload.iss) return {
277
- isValid: false,
278
- reason: `Issuer does not match (oidcServerConfiguration issuer) ${oidcServerConfiguration.issuer} !== (idTokenPayload issuer) ${idTokenPayload.iss}`
153
+ //#endregion
154
+ //#region src/dpop.ts
155
+ var isDpop = (trustedDomain) => {
156
+ if (Array.isArray(trustedDomain)) return false;
157
+ return trustedDomain.demonstratingProofOfPossession ?? false;
158
+ };
159
+ var getDpopConfiguration = (trustedDomain) => {
160
+ if (!isDpop(trustedDomain)) return null;
161
+ if (Array.isArray(trustedDomain)) return null;
162
+ return trustedDomain.demonstratingProofOfPossessionConfiguration ?? defaultDemonstratingProofOfPossessionConfiguration;
163
+ };
164
+ var getDpopOnlyWhenDpopHeaderPresent = (trustedDomain) => {
165
+ if (!isDpop(trustedDomain)) return null;
166
+ if (Array.isArray(trustedDomain)) return null;
167
+ return trustedDomain.demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent ?? true;
168
+ };
169
+ //#endregion
170
+ //#region src/resource.ts
171
+ /** Worker-only resource refresh routing hint, removed before the provider request. */
172
+ var RESOURCE_TOKEN_REQUEST_HEADER = "x-oidc-resource";
173
+ /** Builds an isolated storage/worker context without creating another OIDC client. */
174
+ var getResourceContextName = (configurationName, resource) => `${configurationName}#resource=${encodeURIComponent(resource)}`;
175
+ /** Reads the exact resource identifier from an isolated context name. */
176
+ var getContextResource = (configurationName) => {
177
+ const index = configurationName.indexOf("#resource=");
178
+ return index < 0 ? void 0 : decodeURIComponent(configurationName.slice(index + 10));
179
+ };
180
+ /** Validates RFC 8707 identifiers without treating them as API URLs or scopes. */
181
+ var validateResourceIdentifier = (resource) => {
182
+ if (typeof resource !== "string" || !/^[a-z][a-z\d+.-]*:/i.test(resource) || resource.includes("#") || /\s/.test(resource)) throw new Error("A resource must be an absolute URI without whitespace or a fragment.");
183
+ new URL(resource);
184
+ };
185
+ var getApiUrl = (value) => {
186
+ const url = new URL(value);
187
+ if (url.protocol !== "https:" && url.protocol !== "http:" || url.username || url.password || url.hash || /%2f|%5c/i.test(url.pathname)) throw new Error("API destinations must be HTTP(S) URLs without credentials, fragments, or encoded separators.");
188
+ return url;
189
+ };
190
+ var getApiPrefixUrl = (value) => {
191
+ const prefix = getApiUrl(value);
192
+ if (prefix.search) throw new Error("An API URL prefix must not contain a query.");
193
+ return prefix;
194
+ };
195
+ /** Prevents default-token fallback anywhere on an explicitly managed API origin. */
196
+ var isResourceOriginManaged = (configurations, destination) => {
197
+ const origin = getApiUrl(destination).origin;
198
+ return configurations?.some((configuration) => configuration.allowedUrlPrefixes.some((value) => getApiPrefixUrl(value).origin === origin)) ?? false;
199
+ };
200
+ /** Matches API mappings by parsed origin and path boundaries, never by host suffix. */
201
+ var isResourceUrlAllowed = (configuration, destination) => {
202
+ const url = getApiUrl(destination);
203
+ return configuration.allowedUrlPrefixes.some((value) => {
204
+ const prefix = getApiPrefixUrl(value);
205
+ const path = prefix.pathname.replace(/\/+$/, "");
206
+ return url.origin === prefix.origin && (path === "" || url.pathname === path || url.pathname.startsWith(`${path}/`));
207
+ });
208
+ };
209
+ /** Validates and retrieves an explicitly registered resource. */
210
+ var getResourceConfiguration = (configurations, resource) => {
211
+ validateResourceIdentifier(resource);
212
+ const matches = configurations?.filter((configuration) => configuration.resource === resource) ?? [];
213
+ if (matches.length !== 1 || matches[0].allowedUrlPrefixes.length === 0) throw new Error("The resource must be registered exactly once with permitted API URL prefixes.");
214
+ for (const prefix of matches[0].allowedUrlPrefixes) isResourceUrlAllowed(matches[0], prefix);
215
+ return matches[0];
216
+ };
217
+ //#endregion
218
+ //#region src/tokenContexts.ts
219
+ var getTokenContextEntries = (database) => Object.entries(database).flatMap(([key, configuration]) => [[key, configuration], ...Object.entries(configuration.resourceEntries ?? {})]);
220
+ var getTokenContexts = (database) => getTokenContextEntries(database).map(([, configuration]) => configuration);
221
+ var getOrCreateResourceContext = (configuration, resource) => {
222
+ getResourceConfiguration(configuration.oidcConfiguration?.resources, resource);
223
+ configuration.resourceEntries ??= {};
224
+ const name = getResourceContextName(configuration.configurationName, resource);
225
+ configuration.resourceEntries[name] ??= {
226
+ ...configuration,
227
+ configurationName: name,
228
+ resource,
229
+ parentConfiguration: configuration,
230
+ resourceEntries: void 0,
231
+ authorizationScope: void 0,
232
+ requestedScope: void 0,
233
+ demonstratingProofOfPossessionApiNonce: null,
234
+ refreshGrant: void 0,
235
+ tokens: null,
236
+ state: null,
237
+ codeVerifier: null,
238
+ nonce: null,
239
+ status: null,
240
+ hideAccessToken: true,
241
+ generation: 0
279
242
  };
243
+ return configuration.resourceEntries[name];
244
+ };
245
+ //#endregion
246
+ //#region src/utils/normalizeUrl.ts
247
+ function normalizeUrl(url) {
248
+ try {
249
+ return new URL(url).toString();
250
+ } catch (error) {
251
+ console.error(`Failed to normalize url: ${url}`, error);
252
+ return url;
253
+ }
254
+ }
255
+ //#endregion
256
+ //#region src/utils/domains.ts
257
+ function checkDomain(domains, endpoint) {
258
+ if (!endpoint) return;
259
+ if (!domains.find((domain) => {
260
+ let testable;
261
+ if (typeof domain === "string") testable = new RegExp(`^${domain}`);
262
+ else testable = domain;
263
+ return testable.test?.(endpoint);
264
+ })) throw new Error("Domain " + endpoint + " is not trusted, please add domain in " + scriptFilename);
265
+ }
266
+ var getDomains = (trustedDomain, type) => {
267
+ if (Array.isArray(trustedDomain)) return trustedDomain;
268
+ return trustedDomain[`${type}Domains`] ?? trustedDomain.domains ?? [];
269
+ };
270
+ var getCurrentDatabaseDomain = (database, url, trustedDomains) => {
271
+ if (url.endsWith("/.well-known/openid-configuration")) return null;
272
+ const datatases = [];
273
+ for (const [key, currentDatabase] of getTokenContextEntries(database)) {
274
+ const oidcServerConfiguration = currentDatabase.oidcServerConfiguration;
275
+ const resources = currentDatabase.oidcConfiguration?.resources;
276
+ if (currentDatabase.resource) {
277
+ if (!isResourceUrlAllowed(getResourceConfiguration(resources, currentDatabase.resource), url)) continue;
278
+ } else if (resources?.length && (!oidcServerConfiguration?.userInfoEndpoint || url !== normalizeUrl(oidcServerConfiguration.userInfoEndpoint)) && isResourceOriginManaged(resources, url)) continue;
279
+ if (!oidcServerConfiguration) continue;
280
+ if (oidcServerConfiguration.tokenEndpoint && url === normalizeUrl(oidcServerConfiguration.tokenEndpoint)) continue;
281
+ if (oidcServerConfiguration.revocationEndpoint && url === normalizeUrl(oidcServerConfiguration.revocationEndpoint)) continue;
282
+ const domains = getDomains(trustedDomains == null ? [] : trustedDomains[key.split("#")[0]], "accessToken");
283
+ const domainsToSendTokens = oidcServerConfiguration.userInfoEndpoint ? [normalizeUrl(oidcServerConfiguration.userInfoEndpoint), ...domains] : [...domains];
284
+ let hasToSendToken = false;
285
+ if (domainsToSendTokens.find((f) => f === "*")) hasToSendToken = true;
286
+ else for (let i = 0; i < domainsToSendTokens.length; i++) {
287
+ let domain = domainsToSendTokens[i];
288
+ if (typeof domain === "string") domain = new RegExp(`^${domain}`);
289
+ if (domain.test?.(url)) {
290
+ hasToSendToken = true;
291
+ break;
292
+ }
293
+ }
294
+ if (hasToSendToken) {
295
+ if (currentDatabase.tokens) datatases.push(currentDatabase);
296
+ }
297
+ }
298
+ return datatases;
299
+ };
300
+ //#endregion
301
+ //#region src/utils/serializeHeaders.ts
302
+ function serializeHeaders(headers) {
303
+ const headersObj = {};
304
+ for (const key of headers.keys()) {
305
+ const value = headers.get(key);
306
+ if (value !== null) headersObj[key] = value;
307
+ }
308
+ return headersObj;
309
+ }
310
+ //#endregion
311
+ //#region src/utils/shouldBypassDestination.ts
312
+ var bypassedDestinations = [
313
+ "image",
314
+ "font",
315
+ "media",
316
+ "document",
317
+ "iframe",
318
+ "script"
319
+ ];
320
+ /**
321
+ * Determines whether a fetch event should be bypassed (not intercepted by the service worker).
322
+ * Navigation requests (mode === 'navigate') are never bypassed, even when their destination
323
+ * is in the bypassed list, so that access tokens can be injected into new-tab navigations.
324
+ */
325
+ var shouldBypassDestination = (destination, mode) => {
326
+ return bypassedDestinations.includes(destination) && mode !== "navigate";
327
+ };
328
+ //#endregion
329
+ //#region src/utils/sleep.ts
330
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
331
+ //#endregion
332
+ //#region src/utils/tokens.ts
333
+ var parseJwt = (payload) => {
334
+ return JSON.parse(b64DecodeUnicode(payload.replaceAll(/-/g, "+").replaceAll(/_/g, "/")));
335
+ };
336
+ function b64DecodeUnicode(str) {
337
+ return decodeURIComponent(Array.prototype.map.call(atob(str), (c) => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2)).join(""));
338
+ }
339
+ function computeTimeLeft(refreshTimeBeforeTokensExpirationInSecond, expiresAt) {
280
340
  const currentTimeUnixSecond = (/* @__PURE__ */ new Date()).getTime() / 1e3;
281
- if (idTokenPayload && idTokenPayload.exp && idTokenPayload.exp < currentTimeUnixSecond) return {
282
- isValid: false,
283
- reason: `Token expired at (idTokenPayload exp) ${idTokenPayload.exp} < (currentTimeUnixSecond) ${currentTimeUnixSecond}`
284
- };
285
- const timeInSevenDays = 604800;
286
- if (idTokenPayload && idTokenPayload.iat && idTokenPayload.iat + timeInSevenDays < currentTimeUnixSecond) return {
287
- isValid: false,
288
- reason: `Token is used from too long time (idTokenPayload iat + timeInSevenDays) ${idTokenPayload.iat + timeInSevenDays} < (currentTimeUnixSecond) ${currentTimeUnixSecond}`
289
- };
290
- if (idTokenPayload && nonce && idTokenPayload.nonce && idTokenPayload.nonce !== nonce) return {
291
- isValid: false,
292
- reason: `Nonce does not match (nonce) ${nonce} !== (idTokenPayload nonce) ${idTokenPayload.nonce}`
293
- };
341
+ return Math.round(expiresAt - refreshTimeBeforeTokensExpirationInSecond - currentTimeUnixSecond);
294
342
  }
295
- return {
296
- isValid: true,
297
- reason: ""
298
- };
299
- };
300
- function extractedIssueAt(tokens, accessTokenPayload, _idTokenPayload) {
301
- if (!tokens.issued_at) {
302
- if (accessTokenPayload && accessTokenPayload.iat) return accessTokenPayload.iat;
303
- else if (_idTokenPayload && _idTokenPayload.iat) return _idTokenPayload.iat;
304
- else return (/* @__PURE__ */ new Date()).getTime() / 1e3;
305
- } else if (typeof tokens.issued_at == "string") return parseInt(tokens.issued_at, 10);
306
- return tokens.issued_at;
307
- }
308
- function _hideTokens(tokens, currentDatabaseElement, configurationName) {
309
- if (!tokens.issued_at) tokens.issued_at = (/* @__PURE__ */ new Date()).getTime() / 1e3;
310
- else if (typeof tokens.issued_at == "string") tokens.issued_at = parseInt(tokens.issued_at, 10);
311
- const accessTokenPayload = extractTokenPayload(tokens.access_token);
312
- const secureTokens = {
313
- ...tokens,
314
- accessTokenPayload
315
- };
316
- if (currentDatabaseElement.hideAccessToken) secureTokens.access_token = `${TOKEN.ACCESS_TOKEN}_${configurationName}`;
317
- tokens.accessTokenPayload = accessTokenPayload;
318
- const oldTokens = currentDatabaseElement.tokens;
319
- let id_token;
320
- if (oldTokens != null && "id_token" in oldTokens && !("id_token" in tokens)) id_token = oldTokens.id_token;
321
- else id_token = tokens.id_token;
322
- tokens.id_token = id_token;
323
- let _idTokenPayload = null;
324
- if (id_token) {
325
- _idTokenPayload = extractTokenPayload(id_token);
326
- tokens.idTokenPayload = _idTokenPayload != null ? { ..._idTokenPayload } : null;
327
- if (_idTokenPayload && _idTokenPayload.nonce && currentDatabaseElement.nonce != null) {
328
- const keyNonce = `${TOKEN.NONCE_TOKEN}_${currentDatabaseElement.configurationName}`;
329
- _idTokenPayload.nonce = keyNonce;
343
+ function isTokensValid(tokens) {
344
+ if (!tokens) return false;
345
+ return computeTimeLeft(0, tokens.expiresAt) > 0;
346
+ }
347
+ var extractTokenPayload = (token) => {
348
+ try {
349
+ if (!token) return null;
350
+ const parts = token.split(".");
351
+ return parts.length === 3 ? parseJwt(parts[1]) : null;
352
+ } catch (e) {
353
+ console.warn(e);
330
354
  }
331
- secureTokens.idTokenPayload = _idTokenPayload;
355
+ return null;
356
+ };
357
+ var isTokensOidcValid = (tokens, nonce, oidcServerConfiguration) => {
358
+ if (tokens.idTokenPayload) {
359
+ const idTokenPayload = tokens.idTokenPayload;
360
+ if (idTokenPayload && oidcServerConfiguration.issuer !== idTokenPayload.iss) return {
361
+ isValid: false,
362
+ reason: `Issuer does not match (oidcServerConfiguration issuer) ${oidcServerConfiguration.issuer} !== (idTokenPayload issuer) ${idTokenPayload.iss}`
363
+ };
364
+ const currentTimeUnixSecond = (/* @__PURE__ */ new Date()).getTime() / 1e3;
365
+ if (idTokenPayload && idTokenPayload.exp && idTokenPayload.exp < currentTimeUnixSecond) return {
366
+ isValid: false,
367
+ reason: `Token expired at (idTokenPayload exp) ${idTokenPayload.exp} < (currentTimeUnixSecond) ${currentTimeUnixSecond}`
368
+ };
369
+ const timeInSevenDays = 604800;
370
+ if (idTokenPayload && idTokenPayload.iat && idTokenPayload.iat + timeInSevenDays < currentTimeUnixSecond) return {
371
+ isValid: false,
372
+ reason: `Token is used from too long time (idTokenPayload iat + timeInSevenDays) ${idTokenPayload.iat + timeInSevenDays} < (currentTimeUnixSecond) ${currentTimeUnixSecond}`
373
+ };
374
+ if (idTokenPayload && nonce && idTokenPayload.nonce && idTokenPayload.nonce !== nonce) return {
375
+ isValid: false,
376
+ reason: `Nonce does not match (nonce) ${nonce} !== (idTokenPayload nonce) ${idTokenPayload.nonce}`
377
+ };
378
+ }
379
+ return {
380
+ isValid: true,
381
+ reason: ""
382
+ };
383
+ };
384
+ function extractedIssueAt(tokens, accessTokenPayload, _idTokenPayload) {
385
+ if (!tokens.issued_at) {
386
+ if (accessTokenPayload && accessTokenPayload.iat) return accessTokenPayload.iat;
387
+ else if (_idTokenPayload && _idTokenPayload.iat) return _idTokenPayload.iat;
388
+ else return (/* @__PURE__ */ new Date()).getTime() / 1e3;
389
+ } else if (typeof tokens.issued_at == "string") return parseInt(tokens.issued_at, 10);
390
+ return tokens.issued_at;
332
391
  }
333
- if (tokens.refresh_token) secureTokens.refresh_token = `${TOKEN.REFRESH_TOKEN}_${configurationName}`;
334
- tokens.issued_at = extractedIssueAt(tokens, accessTokenPayload, _idTokenPayload);
335
- const expireIn = typeof tokens.expires_in == "string" ? parseInt(tokens.expires_in, 10) : tokens.expires_in;
336
- const idTokenExpiresAt = _idTokenPayload && _idTokenPayload.exp ? _idTokenPayload.exp : Number.MAX_VALUE;
337
- const accessTokenExpiresAt = accessTokenPayload && accessTokenPayload.exp ? accessTokenPayload.exp : tokens.issued_at + expireIn;
338
- let expiresAt;
339
- const tokenRenewMode = currentDatabaseElement.oidcConfiguration.token_renew_mode;
340
- if (tokenRenewMode === TokenRenewMode.access_token_invalid) expiresAt = accessTokenExpiresAt;
341
- else if (tokenRenewMode === TokenRenewMode.id_token_invalid) expiresAt = idTokenExpiresAt;
342
- else expiresAt = idTokenExpiresAt < accessTokenExpiresAt ? idTokenExpiresAt : accessTokenExpiresAt;
343
- secureTokens.expiresAt = expiresAt;
344
- tokens.expiresAt = expiresAt;
345
- const { isValid, reason } = isTokensOidcValid(tokens, currentDatabaseElement.nonce ? currentDatabaseElement.nonce.nonce : null, currentDatabaseElement.oidcServerConfiguration);
346
- if (!isValid) throw Error(`Tokens are not OpenID valid, reason: ${reason}`);
347
- if (oldTokens != null && "refresh_token" in oldTokens && !("refresh_token" in tokens)) {
348
- const refreshToken = oldTokens.refresh_token;
349
- currentDatabaseElement.tokens = {
392
+ function _hideTokens(tokens, currentDatabaseElement, configurationName, isAuthorizationCode = false) {
393
+ const hasNewIdToken = typeof tokens.id_token === "string" && tokens.id_token.length > 0;
394
+ const parent = currentDatabaseElement.parentConfiguration;
395
+ if (currentDatabaseElement.resource && currentDatabaseElement.refreshGrant === currentDatabaseElement && tokens.refresh_token && tokens.refresh_token === parent?.tokens?.refresh_token) throw new Error("The provider reused a refresh token across separate authorization transactions; use a multi-resource authorization grant instead.");
396
+ if (!tokens.issued_at) tokens.issued_at = (/* @__PURE__ */ new Date()).getTime() / 1e3;
397
+ else if (typeof tokens.issued_at == "string") tokens.issued_at = parseInt(tokens.issued_at, 10);
398
+ const accessTokenPayload = extractTokenPayload(tokens.access_token);
399
+ if (currentDatabaseElement.resource || currentDatabaseElement.oidcConfiguration?.resources?.length) tokens.scope ??= currentDatabaseElement.requestedScope ?? currentDatabaseElement.tokens?.scope ?? currentDatabaseElement.oidcConfiguration?.scope;
400
+ const secureTokens = {
350
401
  ...tokens,
351
- refresh_token: refreshToken
402
+ accessTokenPayload
352
403
  };
353
- } else currentDatabaseElement.tokens = tokens;
354
- currentDatabaseElement.status = "LOGGED_IN";
355
- return secureTokens;
356
- }
357
- var demonstratingProofOfPossessionNonceResponseHeader = "DPoP-Nonce";
358
- function hideTokens(currentDatabaseElement) {
359
- const configurationName = currentDatabaseElement.configurationName;
360
- return (response) => {
361
- if (response.status !== 200) return response;
362
- const newHeaders = new Headers(response.headers);
363
- if (response.headers.has(demonstratingProofOfPossessionNonceResponseHeader)) {
364
- currentDatabaseElement.demonstratingProofOfPossessionNonce = response.headers.get(demonstratingProofOfPossessionNonceResponseHeader);
365
- newHeaders.delete(demonstratingProofOfPossessionNonceResponseHeader);
404
+ if (currentDatabaseElement.hideAccessToken) secureTokens.access_token = `${TOKEN.ACCESS_TOKEN}_${configurationName}`;
405
+ tokens.accessTokenPayload = accessTokenPayload;
406
+ const oldTokens = currentDatabaseElement.tokens;
407
+ let id_token;
408
+ if (oldTokens != null && "id_token" in oldTokens && !("id_token" in tokens)) id_token = oldTokens.id_token;
409
+ else id_token = tokens.id_token;
410
+ tokens.id_token = id_token;
411
+ let _idTokenPayload = null;
412
+ if (id_token) {
413
+ _idTokenPayload = extractTokenPayload(id_token);
414
+ tokens.idTokenPayload = _idTokenPayload != null ? { ..._idTokenPayload } : null;
415
+ if (_idTokenPayload && _idTokenPayload.nonce && currentDatabaseElement.nonce != null) {
416
+ const keyNonce = `${TOKEN.NONCE_TOKEN}_${currentDatabaseElement.configurationName}`;
417
+ _idTokenPayload.nonce = keyNonce;
418
+ }
419
+ secureTokens.idTokenPayload = currentDatabaseElement.resource && !hasNewIdToken ? null : _idTokenPayload;
420
+ }
421
+ if (currentDatabaseElement.resource && isAuthorizationCode && (!hasNewIdToken || typeof tokens.idTokenPayload?.sub !== "string" || !tokens.idTokenPayload.sub || currentDatabaseElement.nonce && tokens.idTokenPayload.nonce !== currentDatabaseElement.nonce.nonce)) throw new Error("Resource authorization requires a fresh ID token with the expected subject and nonce.");
422
+ if (tokens.refresh_token) secureTokens.refresh_token = `${TOKEN.REFRESH_TOKEN}_${(currentDatabaseElement.refreshGrant ?? currentDatabaseElement).configurationName}`;
423
+ tokens.issued_at = extractedIssueAt(tokens, accessTokenPayload, _idTokenPayload);
424
+ const expireIn = typeof tokens.expires_in == "string" ? parseInt(tokens.expires_in, 10) : tokens.expires_in;
425
+ const idTokenExpiresAt = _idTokenPayload && _idTokenPayload.exp ? _idTokenPayload.exp : Number.MAX_VALUE;
426
+ const accessTokenExpiresAt = accessTokenPayload && accessTokenPayload.exp ? accessTokenPayload.exp : tokens.issued_at + expireIn;
427
+ let expiresAt;
428
+ const tokenRenewMode = currentDatabaseElement.resource ? TokenRenewMode.access_token_invalid : currentDatabaseElement.oidcConfiguration.token_renew_mode;
429
+ if (tokenRenewMode === TokenRenewMode.access_token_invalid) expiresAt = accessTokenExpiresAt;
430
+ else if (tokenRenewMode === TokenRenewMode.id_token_invalid) expiresAt = idTokenExpiresAt;
431
+ else expiresAt = idTokenExpiresAt < accessTokenExpiresAt ? idTokenExpiresAt : accessTokenExpiresAt;
432
+ secureTokens.expiresAt = expiresAt;
433
+ tokens.expiresAt = expiresAt;
434
+ const nonce = currentDatabaseElement.nonce ? currentDatabaseElement.nonce.nonce : null;
435
+ const { isValid, reason } = isTokensOidcValid(currentDatabaseElement.resource && !hasNewIdToken ? {
436
+ ...tokens,
437
+ idTokenPayload: void 0
438
+ } : tokens, nonce, currentDatabaseElement.oidcServerConfiguration);
439
+ if (!isValid) throw Error(`Tokens are not OpenID valid, reason: ${reason}`);
440
+ if (currentDatabaseElement.resource) {
441
+ if (typeof tokens.access_token !== "string" || !tokens.access_token || !Number.isFinite(expiresAt) || !Number.isFinite(tokens.issued_at) || tokens.refresh_token != null && (typeof tokens.refresh_token !== "string" || !tokens.refresh_token)) throw new Error("The provider did not return a usable resource token response.");
442
+ const resource = getResourceConfiguration(currentDatabaseElement.oidcConfiguration?.resources, currentDatabaseElement.resource);
443
+ tokens.scope ??= currentDatabaseElement.requestedScope ?? resource.scope ?? currentDatabaseElement.oidcConfiguration?.scope;
444
+ secureTokens.scope = tokens.scope;
445
+ if (resource.scope && !resource.scope.split(/\s+/).filter(Boolean).every((scope) => (tokens.scope ?? "").split(/\s+/).includes(scope))) throw new Error("The provider did not grant the required resource scopes.");
446
+ const parentSubject = currentDatabaseElement.parentConfiguration?.tokens?.idTokenPayload?.sub;
447
+ const subject = tokens.idTokenPayload?.sub ?? oldTokens?.idTokenPayload?.sub ?? parentSubject;
448
+ if (typeof parentSubject !== "string" || subject !== parentSubject) throw new Error("Resource authorization must preserve the authenticated subject.");
449
+ if (accessTokenPayload?.iss !== void 0 && accessTokenPayload.iss !== currentDatabaseElement.oidcServerConfiguration?.issuer || accessTokenPayload && resource.audiences && !resource.audiences.some((audience) => (typeof accessTokenPayload.aud === "string" ? [accessTokenPayload.aud] : accessTokenPayload.aud ?? []).includes(audience))) throw new Error("Resource token issuer or configured audience does not match.");
450
+ }
451
+ if (oldTokens != null && "refresh_token" in oldTokens && !("refresh_token" in tokens)) {
452
+ const refreshToken = oldTokens.refresh_token;
453
+ currentDatabaseElement.tokens = {
454
+ ...tokens,
455
+ refresh_token: refreshToken
456
+ };
457
+ } else currentDatabaseElement.tokens = tokens;
458
+ currentDatabaseElement.status = "LOGGED_IN";
459
+ if (currentDatabaseElement.resource) {
460
+ const grant = currentDatabaseElement.refreshGrant ?? currentDatabaseElement;
461
+ const subject = tokens.idTokenPayload?.sub ?? grant.tokens?.idTokenPayload?.sub ?? currentDatabaseElement.parentConfiguration?.tokens?.idTokenPayload?.sub;
462
+ if (typeof subject !== "string") throw new Error("Resource tokens require an authenticated OIDC subject.");
463
+ if (grant !== currentDatabaseElement && tokens.refresh_token && grant.tokens) grant.tokens.refresh_token = tokens.refresh_token;
464
+ const resourceContext = {
465
+ resource: currentDatabaseElement.resource,
466
+ issuer: currentDatabaseElement.oidcServerConfiguration.issuer,
467
+ clientId: currentDatabaseElement.oidcConfiguration.client_id,
468
+ configurationName: currentDatabaseElement.configurationName.split("#")[0],
469
+ subject,
470
+ grantResource: grant.resource ?? null
471
+ };
472
+ currentDatabaseElement.tokens.resourceContext = resourceContext;
473
+ secureTokens.resourceContext = resourceContext;
366
474
  }
367
- return response.json().then((tokens) => {
368
- const secureTokens = _hideTokens(tokens, currentDatabaseElement, configurationName);
369
- const body = JSON.stringify(secureTokens);
370
- return new Response(body, {
371
- status: response.status,
372
- statusText: response.statusText,
373
- headers: newHeaders
475
+ return secureTokens;
476
+ }
477
+ var demonstratingProofOfPossessionNonceResponseHeader = "DPoP-Nonce";
478
+ function hideTokens(currentDatabaseElement, isCurrent = () => true, isGrantCurrent = isCurrent, isAuthorizationCode = false) {
479
+ const configurationName = currentDatabaseElement.configurationName;
480
+ return (response) => {
481
+ if (response.status !== 200) return response;
482
+ const newHeaders = new Headers(response.headers);
483
+ if (response.headers.has(demonstratingProofOfPossessionNonceResponseHeader) && isGrantCurrent()) {
484
+ currentDatabaseElement.demonstratingProofOfPossessionNonce = response.headers.get(demonstratingProofOfPossessionNonceResponseHeader);
485
+ if (currentDatabaseElement.refreshGrant && currentDatabaseElement.refreshGrant !== currentDatabaseElement) currentDatabaseElement.refreshGrant.demonstratingProofOfPossessionNonce = currentDatabaseElement.demonstratingProofOfPossessionNonce;
486
+ newHeaders.delete(demonstratingProofOfPossessionNonceResponseHeader);
487
+ }
488
+ return response.json().then((tokens) => {
489
+ const grant = currentDatabaseElement.refreshGrant;
490
+ if (currentDatabaseElement.resource && grant && isGrantCurrent() && grant.tokens && typeof tokens.refresh_token === "string" && tokens.refresh_token && (grant !== currentDatabaseElement || tokens.refresh_token !== currentDatabaseElement.parentConfiguration?.tokens?.refresh_token)) grant.tokens.refresh_token = tokens.refresh_token;
491
+ if (!isCurrent()) throw new Error("The token transaction was cleared before its response.");
492
+ const secureTokens = _hideTokens(tokens, currentDatabaseElement, configurationName, isAuthorizationCode);
493
+ const body = JSON.stringify(secureTokens);
494
+ return new Response(body, {
495
+ status: response.status,
496
+ statusText: response.statusText,
497
+ headers: newHeaders
498
+ });
374
499
  });
500
+ };
501
+ }
502
+ //#endregion
503
+ //#region src/utils/waitForValidTokens.ts
504
+ var TOKEN_RENEWAL_TIMEOUT_MS = 5e3;
505
+ /**
506
+ * Polls until the tokens in the given config are valid, or until a timeout elapses.
507
+ *
508
+ * @returns `null` when tokens become valid; a synthetic 401 `Response` on timeout or
509
+ * when the tokens are cleared while waiting (e.g. due to a parallel logout).
510
+ */
511
+ async function waitForValidTokens(config, maxWaitMs = TOKEN_RENEWAL_TIMEOUT_MS, pollIntervalMs = 200) {
512
+ const startTime = Date.now();
513
+ while (config.tokens && !isTokensValid(config.tokens)) {
514
+ if (Date.now() - startTime >= maxWaitMs) return new Response(null, {
515
+ status: 401,
516
+ statusText: "Token expired - service worker renewal timeout"
517
+ });
518
+ await sleep(pollIntervalMs);
519
+ }
520
+ if (!config.tokens?.access_token) return new Response(null, {
521
+ status: 401,
522
+ statusText: "Missing access token"
375
523
  });
524
+ return null;
525
+ }
526
+ //#endregion
527
+ //#region src/oidcConfig.ts
528
+ var getOidcServerUrls = (oidcServerConfiguration) => {
529
+ return [
530
+ oidcServerConfiguration.issuer,
531
+ oidcServerConfiguration.authorizationEndpoint,
532
+ oidcServerConfiguration.tokenEndpoint,
533
+ oidcServerConfiguration.revocationEndpoint,
534
+ oidcServerConfiguration.userInfoEndpoint
535
+ ].filter(Boolean).map(normalizeUrl);
376
536
  };
377
- }
378
- //#endregion
379
- //#region src/utils/waitForValidTokens.ts
380
- var TOKEN_RENEWAL_TIMEOUT_MS = 5e3;
381
- /**
382
- * Polls until the tokens in the given config are valid, or until a timeout elapses.
383
- *
384
- * @returns `null` when tokens become valid; a synthetic 401 `Response` on timeout or
385
- * when the tokens are cleared while waiting (e.g. due to a parallel logout).
386
- */
387
- async function waitForValidTokens(config, maxWaitMs = TOKEN_RENEWAL_TIMEOUT_MS, pollIntervalMs = 200) {
388
- const startTime = Date.now();
389
- while (config.tokens && !isTokensValid(config.tokens)) {
390
- if (Date.now() - startTime >= maxWaitMs) return new Response(null, {
391
- status: 401,
392
- statusText: "Token expired - service worker renewal timeout"
537
+ var isOidcServerRequest = (database, normalizedUrl) => {
538
+ return Object.values(database).some((config) => {
539
+ const { oidcServerConfiguration } = config || {};
540
+ if (!oidcServerConfiguration) return false;
541
+ return getOidcServerUrls(oidcServerConfiguration).some((oidcUrl) => normalizedUrl.startsWith(oidcUrl));
542
+ });
543
+ };
544
+ var isDomainMatchingUrl = (domain, normalizedUrl) => {
545
+ if (typeof domain === "string") domain = new RegExp(`^${domain}`);
546
+ return domain.test?.(normalizedUrl) ?? false;
547
+ };
548
+ var isAccessTokenDomainRequest = (database, normalizedUrl, trustedDomains) => {
549
+ return Object.entries(database).some(([key, currentDatabase]) => {
550
+ if (!currentDatabase.oidcServerConfiguration) return false;
551
+ const domains = getDomains(trustedDomains?.[key.split("#")[0]] ?? [], "accessToken");
552
+ if (domains.some((domain) => domain === "*")) return true;
553
+ return domains.some((domain) => isDomainMatchingUrl(domain, normalizedUrl));
393
554
  });
394
- await sleep(pollIntervalMs);
555
+ };
556
+ var shouldBypassNonOidcRequest = (database, normalizedUrl, trustedDomains) => {
557
+ const configurations = Object.values(database);
558
+ if (configurations.length === 0) return false;
559
+ if (!configurations.every((config) => config.bypassAllNonOidcRequests)) return false;
560
+ if (!configurations.every((config) => config.oidcServerConfiguration != null)) return false;
561
+ return !isOidcServerRequest(database, normalizedUrl) && !isAccessTokenDomainRequest(database, normalizedUrl, trustedDomains);
562
+ };
563
+ var getMatchingOidcConfigurations = (database, url) => {
564
+ const normalizedUrl = normalizeUrl(url);
565
+ return getTokenContexts(database).filter((config) => {
566
+ const { oidcServerConfiguration } = config || {};
567
+ const { tokenEndpoint, revocationEndpoint } = oidcServerConfiguration || {};
568
+ return tokenEndpoint && normalizedUrl.startsWith(normalizeUrl(tokenEndpoint)) || revocationEndpoint && normalizedUrl.startsWith(normalizeUrl(revocationEndpoint));
569
+ });
570
+ };
571
+ //#endregion
572
+ //#region src/utils/codeVerifier.ts
573
+ function replaceCodeVerifier(codeVerifier, newCodeVerifier) {
574
+ return codeVerifier.replace(/[?&]code_verifier=([^&]+)/i, `&code_verifier=${newCodeVerifier}`);
395
575
  }
396
- if (!config.tokens?.access_token) return new Response(null, {
397
- status: 401,
398
- statusText: "Missing access token"
399
- });
400
- return null;
401
- }
402
- //#endregion
403
- //#region src/oidcConfig.ts
404
- var getOidcServerUrls = (oidcServerConfiguration) => {
405
- return [
406
- oidcServerConfiguration.issuer,
407
- oidcServerConfiguration.authorizationEndpoint,
408
- oidcServerConfiguration.tokenEndpoint,
409
- oidcServerConfiguration.revocationEndpoint,
410
- oidcServerConfiguration.userInfoEndpoint
411
- ].filter(Boolean).map(normalizeUrl);
412
- };
413
- var isOidcServerRequest = (database, normalizedUrl) => {
414
- return Object.values(database).some((config) => {
415
- const { oidcServerConfiguration } = config || {};
416
- if (!oidcServerConfiguration) return false;
417
- return getOidcServerUrls(oidcServerConfiguration).some((oidcUrl) => normalizedUrl.startsWith(oidcUrl));
418
- });
419
- };
420
- var isDomainMatchingUrl = (domain, normalizedUrl) => {
421
- if (typeof domain === "string") domain = new RegExp(`^${domain}`);
422
- return domain.test?.(normalizedUrl) ?? false;
423
- };
424
- var isAccessTokenDomainRequest = (database, normalizedUrl, trustedDomains) => {
425
- return Object.entries(database).some(([key, currentDatabase]) => {
426
- if (!currentDatabase.oidcServerConfiguration) return false;
427
- const domains = getDomains(trustedDomains?.[key.split("#")[0]] ?? [], "accessToken");
428
- if (domains.some((domain) => domain === "*")) return true;
429
- return domains.some((domain) => isDomainMatchingUrl(domain, normalizedUrl));
430
- });
431
- };
432
- var shouldBypassNonOidcRequest = (database, normalizedUrl, trustedDomains) => {
433
- const configurations = Object.values(database);
434
- if (configurations.length === 0) return false;
435
- if (!configurations.every((config) => config.bypassAllNonOidcRequests)) return false;
436
- if (!configurations.every((config) => config.oidcServerConfiguration != null)) return false;
437
- return !isOidcServerRequest(database, normalizedUrl) && !isAccessTokenDomainRequest(database, normalizedUrl, trustedDomains);
438
- };
439
- var getMatchingOidcConfigurations = (database, url) => {
440
- const normalizedUrl = normalizeUrl(url);
441
- return Object.values(database).filter((config) => {
442
- const { oidcServerConfiguration } = config || {};
443
- const { tokenEndpoint, revocationEndpoint } = oidcServerConfiguration || {};
444
- return tokenEndpoint && normalizedUrl.startsWith(normalizeUrl(tokenEndpoint)) || revocationEndpoint && normalizedUrl.startsWith(normalizeUrl(revocationEndpoint));
445
- });
446
- };
447
- //#endregion
448
- //#region src/utils/codeVerifier.ts
449
- function replaceCodeVerifier(codeVerifier, newCodeVerifier) {
450
- return codeVerifier.replace(/[?&]code_verifier=([^&]+)/i, `&code_verifier=${newCodeVerifier}`);
451
- }
452
- var extractConfigurationNameFromCodeVerifier = (body) => {
453
- const match = body.match(/[?&]code_verifier=CODE_VERIFIER_SECURED_BY_OIDC_SERVICE_WORKER_([^&]+)/);
454
- return match ? decodeURIComponent(match[1]) : "";
455
- };
456
- //#endregion
457
- //#region src/version.ts
458
- var version_default = "7.29.8";
459
- //#endregion
460
- //#region src/OidcServiceWorker.ts
461
- if (typeof trustedTypes !== "undefined" && typeof trustedTypes.createPolicy === "function") trustedTypes.createPolicy("default", { createScriptURL: function(url) {
462
- if (url === "OidcTrustedDomains.js") return url;
463
- else throw new Error("Untrusted script URL blocked: " + url);
464
- } });
465
- var _self = self;
466
- _self.importScripts(scriptFilename);
467
- var id = Math.round((/* @__PURE__ */ new Date()).getTime() / 1e3).toString();
468
- console.log("init service worker with id", id);
469
- var keepAliveJsonFilename = "OidcKeepAliveServiceWorker.json";
470
- var database = {};
471
- /**
472
- * Keeps the service worker alive by responding with a cached response after a sleep.
473
- */
474
- var keepAliveAsync = async (event) => {
475
- const originalRequest = event.request;
476
- const isFromVanilla = originalRequest.headers.has("oidc-vanilla");
477
- const response = new Response("{}", {
478
- status: 200,
479
- statusText: "oidc-service-worker"
480
- });
481
- if (!isFromVanilla) {
482
- const originalRequestUrl = new URL(originalRequest.url);
483
- const minSleepSeconds = Number(originalRequestUrl.searchParams.get("minSleepSeconds")) || 240;
484
- for (let i = 0; i < minSleepSeconds; i++) {
485
- await sleep(1e3 + Math.floor(Math.random() * 1e3));
486
- await (await caches.open("oidc_dummy_cache")).put(event.request, response.clone());
576
+ var extractConfigurationNameFromCodeVerifier = (body) => {
577
+ const match = body.match(/[?&]code_verifier=CODE_VERIFIER_SECURED_BY_OIDC_SERVICE_WORKER_([^&]+)/);
578
+ return match ? decodeURIComponent(match[1]) : "";
579
+ };
580
+ //#endregion
581
+ //#region src/version.ts
582
+ var version_default = "7.30.0";
583
+ //#endregion
584
+ //#region src/OidcServiceWorker.ts
585
+ if (typeof trustedTypes !== "undefined" && typeof trustedTypes.createPolicy === "function") trustedTypes.createPolicy("default", { createScriptURL: function(url) {
586
+ if (url === "OidcTrustedDomains.js") return url;
587
+ else throw new Error("Untrusted script URL blocked: " + url);
588
+ } });
589
+ var _self = self;
590
+ _self.importScripts(scriptFilename);
591
+ var id = Math.round((/* @__PURE__ */ new Date()).getTime() / 1e3).toString();
592
+ console.log("init service worker with id", id);
593
+ var keepAliveJsonFilename = "OidcKeepAliveServiceWorker.json";
594
+ var database = {};
595
+ /**
596
+ * Keeps the service worker alive by responding with a cached response after a sleep.
597
+ */
598
+ var keepAliveAsync = async (event) => {
599
+ const originalRequest = event.request;
600
+ const isFromVanilla = originalRequest.headers.has("oidc-vanilla");
601
+ const response = new Response("{}", {
602
+ status: 200,
603
+ statusText: "oidc-service-worker"
604
+ });
605
+ if (!isFromVanilla) {
606
+ const originalRequestUrl = new URL(originalRequest.url);
607
+ const minSleepSeconds = Number(originalRequestUrl.searchParams.get("minSleepSeconds")) || 240;
608
+ for (let i = 0; i < minSleepSeconds; i++) {
609
+ await sleep(1e3 + Math.floor(Math.random() * 1e3));
610
+ await (await caches.open("oidc_dummy_cache")).put(event.request, response.clone());
611
+ }
487
612
  }
613
+ return response;
614
+ };
615
+ /**
616
+ * Generates DPoP headers when a DPoP configuration is present.
617
+ */
618
+ async function generateDpopAsync(originalRequest, currentDatabase, url, extrasClaims = {}) {
619
+ const headersExtras = serializeHeaders(originalRequest.headers);
620
+ if (currentDatabase?.demonstratingProofOfPossessionConfiguration && currentDatabase.demonstratingProofOfPossessionJwkJson && (!currentDatabase.demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent || currentDatabase.demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent && headersExtras.dpop)) {
621
+ const dpopConfiguration = currentDatabase.demonstratingProofOfPossessionConfiguration;
622
+ const jwk = currentDatabase.demonstratingProofOfPossessionJwkJson;
623
+ const method = originalRequest.method;
624
+ const resourceNonce = currentDatabase.resource ? url === normalizeUrl(currentDatabase.oidcServerConfiguration.tokenEndpoint) ? currentDatabase.demonstratingProofOfPossessionNonce : currentDatabase.demonstratingProofOfPossessionApiNonce : void 0;
625
+ headersExtras.dpop = await generateJwtDemonstratingProofOfPossessionAsync(self)(dpopConfiguration)(jwk, method, url, {
626
+ ...extrasClaims,
627
+ ...resourceNonce ? { nonce: resourceNonce } : {}
628
+ });
629
+ const nonceHeader = resourceNonce ?? (!currentDatabase.resource ? currentDatabase.demonstratingProofOfPossessionNonce : void 0);
630
+ if (nonceHeader) headersExtras.nonce = nonceHeader;
631
+ }
632
+ return headersExtras;
488
633
  }
489
- return response;
490
- };
491
- /**
492
- * Generates DPoP headers when a DPoP configuration is present.
493
- */
494
- async function generateDpopAsync(originalRequest, currentDatabase, url, extrasClaims = {}) {
495
- const headersExtras = serializeHeaders(originalRequest.headers);
496
- if (currentDatabase?.demonstratingProofOfPossessionConfiguration && currentDatabase.demonstratingProofOfPossessionJwkJson && (!currentDatabase.demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent || currentDatabase.demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent && headersExtras.dpop)) {
497
- const dpopConfiguration = currentDatabase.demonstratingProofOfPossessionConfiguration;
498
- const jwk = currentDatabase.demonstratingProofOfPossessionJwkJson;
499
- const method = originalRequest.method;
500
- headersExtras.dpop = await generateJwtDemonstratingProofOfPossessionAsync(self)(dpopConfiguration)(jwk, method, url, extrasClaims);
501
- if (currentDatabase.demonstratingProofOfPossessionNonce != null) headersExtras.nonce = currentDatabase.demonstratingProofOfPossessionNonce;
502
- }
503
- return headersExtras;
504
- }
505
- /**
506
- * Intercepts fetch requests to inject access tokens and handle token endpoints.
507
- */
508
- var handleFetch = (event) => {
509
634
  /**
510
- * Exit early for requests that do not need to have an auth token attached.
635
+ * Intercepts fetch requests to inject access tokens and handle token endpoints.
511
636
  */
512
- if (shouldBypassDestination(event.request.destination, event.request.mode)) return;
513
- const normalizedUrl = normalizeUrl(event.request.url);
514
- if (!normalizedUrl.includes(keepAliveJsonFilename) && shouldBypassNonOidcRequest(database, normalizedUrl, trustedDomains)) return;
515
- event.respondWith((async () => {
516
- try {
517
- const originalRequest = event.request;
518
- const url = normalizedUrl;
519
- if (url.includes(keepAliveJsonFilename)) return keepAliveAsync(event);
520
- const currentDatabasesForRequestAccessToken = getCurrentDatabaseDomain(database, url, trustedDomains);
521
- const authorization = originalRequest.headers.get("authorization");
522
- let authenticationMode = "Bearer";
523
- let key = "default";
524
- if (authorization) {
525
- const split = authorization.split(" ");
526
- authenticationMode = split[0];
527
- if (split[1]?.includes("ACCESS_TOKEN_SECURED_BY_OIDC_SERVICE_WORKER_")) key = split[1].split("ACCESS_TOKEN_SECURED_BY_OIDC_SERVICE_WORKER_")[1];
528
- }
529
- const currentDatabaseForRequestAccessToken = currentDatabasesForRequestAccessToken?.find((c) => c.configurationName.endsWith(key));
530
- if (currentDatabaseForRequestAccessToken?.tokens?.access_token) {
531
- const tokenError = await waitForValidTokens(currentDatabaseForRequestAccessToken);
532
- if (tokenError) return tokenError;
533
- let requestMode = originalRequest.mode;
534
- if (originalRequest.mode !== "navigate" && currentDatabaseForRequestAccessToken.convertAllRequestsToCorsExceptNavigate) requestMode = "cors";
535
- let headers;
536
- if (originalRequest.mode === "navigate" && !currentDatabaseForRequestAccessToken.setAccessTokenToNavigateRequests) headers = { ...serializeHeaders(originalRequest.headers) };
537
- else if (authenticationMode.toLowerCase() === "dpop" || !currentDatabaseForRequestAccessToken.demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent && currentDatabaseForRequestAccessToken.demonstratingProofOfPossessionConfiguration) headers = {
538
- ...await generateDpopAsync(originalRequest, currentDatabaseForRequestAccessToken, url, { ath: await base64urlOfHashOfASCIIEncodingAsync(currentDatabaseForRequestAccessToken.tokens.access_token) }),
539
- authorization: `DPoP ${currentDatabaseForRequestAccessToken.tokens.access_token}`
540
- };
541
- else headers = {
542
- ...serializeHeaders(originalRequest.headers),
543
- authorization: `${authenticationMode} ${currentDatabaseForRequestAccessToken.tokens.access_token}`
544
- };
545
- let init;
546
- if (originalRequest.mode === "navigate") init = { headers };
547
- else init = {
548
- headers,
549
- mode: requestMode
550
- };
551
- const newRequest = new Request(originalRequest, init);
552
- return fetch(newRequest);
553
- }
554
- if (event.request.method !== "POST") return fetch(originalRequest);
555
- const currentDatabases = getMatchingOidcConfigurations(database, url);
556
- const numberDatabase = currentDatabases.length;
557
- if (numberDatabase > 0) return new Promise((resolve, reject) => {
558
- const clonedRequest = originalRequest.clone();
559
- clonedRequest.text().then(async (actualBody) => {
560
- let currentDatabase = null;
561
- try {
562
- if (actualBody.includes(TOKEN.REFRESH_TOKEN) || actualBody.includes(TOKEN.ACCESS_TOKEN)) {
563
- let headers = serializeHeaders(originalRequest.headers);
564
- let newBody = actualBody;
565
- for (let i = 0; i < numberDatabase; i++) {
566
- const currentDb = currentDatabases[i];
567
- if (currentDb?.tokens) {
568
- const claimsExtras = { ath: await base64urlOfHashOfASCIIEncodingAsync(currentDb.tokens.access_token) };
569
- headers = await generateDpopAsync(originalRequest, currentDb, url, claimsExtras);
570
- const keyRefreshToken = encodeURIComponent(`${TOKEN.REFRESH_TOKEN}_${currentDb.configurationName}`);
571
- if (actualBody.includes(keyRefreshToken)) {
572
- newBody = newBody.replace(keyRefreshToken, encodeURIComponent(currentDb.tokens.refresh_token));
573
- currentDatabase = currentDb;
574
- break;
575
- }
576
- const keyAccessToken = encodeURIComponent(`${TOKEN.ACCESS_TOKEN}_${currentDb.configurationName}`);
577
- if (actualBody.includes(keyAccessToken)) {
578
- newBody = newBody.replace(keyAccessToken, encodeURIComponent(currentDb.tokens.access_token));
579
- currentDatabase = currentDb;
580
- break;
637
+ var handleFetch = (event) => {
638
+ /**
639
+ * Exit early for requests that do not need to have an auth token attached.
640
+ */
641
+ if (shouldBypassDestination(event.request.destination, event.request.mode)) return;
642
+ const normalizedUrl = normalizeUrl(event.request.url);
643
+ if (!normalizedUrl.includes(keepAliveJsonFilename) && shouldBypassNonOidcRequest(database, normalizedUrl, trustedDomains)) return;
644
+ event.respondWith((async () => {
645
+ try {
646
+ const originalRequest = event.request;
647
+ const url = normalizedUrl;
648
+ if (url.includes(keepAliveJsonFilename)) return keepAliveAsync(event);
649
+ const currentDatabasesForRequestAccessToken = getCurrentDatabaseDomain(database, url, trustedDomains);
650
+ const authorization = originalRequest.headers.get("authorization");
651
+ let authenticationMode = "Bearer";
652
+ let key = "default";
653
+ if (authorization) {
654
+ const split = authorization.split(" ");
655
+ authenticationMode = split[0];
656
+ if (split[1]?.includes("ACCESS_TOKEN_SECURED_BY_OIDC_SERVICE_WORKER_")) key = split[1].split("ACCESS_TOKEN_SECURED_BY_OIDC_SERVICE_WORKER_")[1];
657
+ }
658
+ const currentDatabaseForRequestAccessToken = currentDatabasesForRequestAccessToken?.find((c) => key.includes("#resource=") ? c.configurationName === key : !c.resource && c.configurationName.endsWith(key));
659
+ if (key.includes("#resource=") && !currentDatabaseForRequestAccessToken) {
660
+ console.error("[OidcServiceWorker] Resource token destination or context is not permitted.");
661
+ return new Response("Resource token destination or context is not permitted.", { status: 403 });
662
+ }
663
+ if (currentDatabaseForRequestAccessToken?.tokens?.access_token) {
664
+ const tokenError = await waitForValidTokens(currentDatabaseForRequestAccessToken);
665
+ if (tokenError) return tokenError;
666
+ const accessToken = currentDatabaseForRequestAccessToken.tokens.access_token;
667
+ const generation = currentDatabaseForRequestAccessToken.generation;
668
+ const parentGeneration = currentDatabaseForRequestAccessToken.parentConfiguration?.generation;
669
+ let requestMode = originalRequest.mode;
670
+ if (originalRequest.mode !== "navigate" && currentDatabaseForRequestAccessToken.convertAllRequestsToCorsExceptNavigate) requestMode = "cors";
671
+ let headers;
672
+ if (originalRequest.mode === "navigate" && !currentDatabaseForRequestAccessToken.setAccessTokenToNavigateRequests) headers = { ...serializeHeaders(originalRequest.headers) };
673
+ else if (authenticationMode.toLowerCase() === "dpop" || !currentDatabaseForRequestAccessToken.demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent && currentDatabaseForRequestAccessToken.demonstratingProofOfPossessionConfiguration) headers = {
674
+ ...await generateDpopAsync(originalRequest, currentDatabaseForRequestAccessToken, url, { ath: await base64urlOfHashOfASCIIEncodingAsync(accessToken) }),
675
+ authorization: `DPoP ${accessToken}`
676
+ };
677
+ else headers = {
678
+ ...serializeHeaders(originalRequest.headers),
679
+ authorization: `${authenticationMode} ${accessToken}`
680
+ };
681
+ let init;
682
+ if (originalRequest.mode === "navigate") init = { headers };
683
+ else init = {
684
+ headers,
685
+ mode: requestMode
686
+ };
687
+ const newRequest = new Request(originalRequest, init);
688
+ if (currentDatabaseForRequestAccessToken.resource && (currentDatabaseForRequestAccessToken.generation !== generation || currentDatabaseForRequestAccessToken.parentConfiguration?.generation !== parentGeneration || currentDatabaseForRequestAccessToken.tokens?.access_token !== accessToken)) {
689
+ console.error("[OidcServiceWorker] The resource token changed before dispatch.");
690
+ return new Response("The resource token changed before dispatch.", { status: 403 });
691
+ }
692
+ const response = await fetch(newRequest, currentDatabaseForRequestAccessToken.resource ? { redirect: "error" } : void 0);
693
+ if (currentDatabaseForRequestAccessToken.resource && currentDatabaseForRequestAccessToken.generation === generation && currentDatabaseForRequestAccessToken.parentConfiguration?.generation === parentGeneration && response.headers.has("DPoP-Nonce")) currentDatabaseForRequestAccessToken.demonstratingProofOfPossessionApiNonce = response.headers.get("DPoP-Nonce");
694
+ return response;
695
+ }
696
+ if (event.request.method !== "POST") return fetch(originalRequest);
697
+ const currentDatabases = getMatchingOidcConfigurations(database, url);
698
+ const numberDatabase = currentDatabases.length;
699
+ if (numberDatabase > 0) return await new Promise((resolve, reject) => {
700
+ const clonedRequest = originalRequest.clone();
701
+ clonedRequest.text().then(async (actualBody) => {
702
+ let currentDatabase = null;
703
+ try {
704
+ if (actualBody.includes(TOKEN.REFRESH_TOKEN) || actualBody.includes(TOKEN.ACCESS_TOKEN)) {
705
+ let headers = serializeHeaders(originalRequest.headers);
706
+ let newBody = actualBody;
707
+ for (let i = 0; i < numberDatabase; i++) {
708
+ const currentDb = currentDatabases[i];
709
+ if (currentDb?.tokens) {
710
+ const claimsExtras = { ath: await base64urlOfHashOfASCIIEncodingAsync(currentDb.tokens.access_token) };
711
+ const keyRefreshToken = encodeURIComponent(`${TOKEN.REFRESH_TOKEN}_${currentDb.configurationName}`);
712
+ if ([new URLSearchParams(actualBody).get("refresh_token"), new URLSearchParams(actualBody).get("token")].includes(decodeURIComponent(keyRefreshToken))) {
713
+ const form = new URLSearchParams(actualBody);
714
+ const selected = form.getAll("resource");
715
+ const resourceHeader = originalRequest.headers.get(RESOURCE_TOKEN_REQUEST_HEADER);
716
+ if (currentDb.oidcServerConfiguration?.revocationEndpoint && url === normalizeUrl(currentDb.oidcServerConfiguration.revocationEndpoint) && form.has("token")) currentDatabase = currentDb;
717
+ else if (resourceHeader !== null) {
718
+ const resource = decodeURIComponent(resourceHeader);
719
+ if (selected.length !== 1 || selected[0] !== resource) throw new Error("The resource routing hint must match the request.");
720
+ if (!currentDb.oidcConfiguration) throw new Error("The resource grant configuration is unavailable.");
721
+ const resourceConfiguration = getResourceConfiguration(currentDb.oidcConfiguration.resources, resource);
722
+ const grantedScope = currentDb.tokens.scope ?? currentDb.authorizationScope ?? currentDb.oidcConfiguration.scope;
723
+ if ([form.get("scope"), resourceConfiguration.scope].filter((scope) => typeof scope === "string").some((scope) => !scope.split(/\s+/).filter(Boolean).every((value) => grantedScope?.split(/\s+/).includes(value)))) throw new Error("The resource scope is outside this refresh grant.");
724
+ if (currentDb.resource !== void 0 ? currentDb.resource !== resource : !(typeof currentDb.oidcConfiguration.resource === "string" ? [currentDb.oidcConfiguration.resource] : currentDb.oidcConfiguration.resource ?? []).includes(resource)) throw new Error("The resource is outside this refresh grant.");
725
+ currentDatabase = currentDb.resource ? currentDb : getOrCreateResourceContext(currentDb, resource);
726
+ currentDatabase.refreshGrant = currentDb;
727
+ currentDatabase.requestedScope = form.get("scope") ?? grantedScope;
728
+ currentDatabase.nonce = currentDb.nonce;
729
+ currentDatabase.demonstratingProofOfPossessionJwkJson = currentDb.demonstratingProofOfPossessionJwkJson;
730
+ currentDatabase.demonstratingProofOfPossessionConfiguration = currentDb.demonstratingProofOfPossessionConfiguration;
731
+ currentDatabase.demonstratingProofOfPossessionNonce = currentDb.demonstratingProofOfPossessionNonce;
732
+ } else if (currentDb.resource) throw new Error("A resource refresh must select exactly its own resource.");
733
+ else {
734
+ currentDatabase = currentDb;
735
+ currentDatabase.requestedScope = form.get("scope") ?? currentDb.tokens.scope;
736
+ }
737
+ headers = await generateDpopAsync(originalRequest, currentDatabase, url, claimsExtras);
738
+ newBody = newBody.replace(keyRefreshToken, encodeURIComponent(currentDb.tokens.refresh_token));
739
+ break;
740
+ }
741
+ const keyAccessToken = encodeURIComponent(`${TOKEN.ACCESS_TOKEN}_${currentDb.configurationName}`);
742
+ if (new URLSearchParams(actualBody).get("token") === decodeURIComponent(keyAccessToken)) {
743
+ headers = await generateDpopAsync(originalRequest, currentDb, url, claimsExtras);
744
+ newBody = newBody.replace(keyAccessToken, encodeURIComponent(currentDb.tokens.access_token));
745
+ currentDatabase = currentDb;
746
+ break;
747
+ }
581
748
  }
582
749
  }
750
+ if (!currentDatabase) throw new Error("The token placeholder does not identify an available grant.");
751
+ delete headers[RESOURCE_TOKEN_REQUEST_HEADER];
752
+ const generation = currentDatabase.generation;
753
+ const grant = currentDatabase.refreshGrant ?? currentDatabase;
754
+ const grantGeneration = grant.generation;
755
+ if (currentDatabase.resource && url !== normalizeUrl(currentDatabase.oidcServerConfiguration.tokenEndpoint) && url !== normalizeUrl(currentDatabase.oidcServerConfiguration.revocationEndpoint)) throw new Error("Resource tokens may only be sent to their exact token endpoint.");
756
+ const fetchPromise = fetch(originalRequest, {
757
+ body: newBody,
758
+ method: clonedRequest.method,
759
+ headers,
760
+ mode: clonedRequest.mode,
761
+ cache: clonedRequest.cache,
762
+ redirect: clonedRequest.redirect,
763
+ referrer: clonedRequest.referrer,
764
+ credentials: clonedRequest.credentials,
765
+ integrity: clonedRequest.integrity
766
+ });
767
+ if (currentDatabase?.oidcServerConfiguration?.revocationEndpoint && url.startsWith(normalizeUrl(currentDatabase.oidcServerConfiguration.revocationEndpoint))) {
768
+ const resp = await fetchPromise;
769
+ const txt = await resp.text();
770
+ resolve(new Response(txt, resp));
771
+ return;
772
+ }
773
+ resolve(await fetchPromise.then(hideTokens(currentDatabase, () => currentDatabase.generation === generation && grant.generation === grantGeneration, () => grant.generation === grantGeneration)));
774
+ return;
583
775
  }
584
- const fetchPromise = fetch(originalRequest, {
585
- body: newBody,
586
- method: clonedRequest.method,
587
- headers,
588
- mode: clonedRequest.mode,
589
- cache: clonedRequest.cache,
590
- redirect: clonedRequest.redirect,
591
- referrer: clonedRequest.referrer,
592
- credentials: clonedRequest.credentials,
593
- integrity: clonedRequest.integrity
594
- });
595
- if (currentDatabase?.oidcServerConfiguration?.revocationEndpoint && url.startsWith(normalizeUrl(currentDatabase.oidcServerConfiguration.revocationEndpoint))) {
596
- const resp = await fetchPromise;
597
- const txt = await resp.text();
598
- resolve(new Response(txt, resp));
776
+ if (actualBody.includes("code_verifier=")) {
777
+ const currentLoginCallbackConfigurationName = extractConfigurationNameFromCodeVerifier(actualBody);
778
+ if (!currentLoginCallbackConfigurationName || currentLoginCallbackConfigurationName === "") throw new Error("No configuration name found in code_verifier");
779
+ currentDatabase = getTokenContexts(database).find((context) => context.configurationName === currentLoginCallbackConfigurationName) ?? null;
780
+ if (!currentDatabase) throw new Error("No available authorization transaction matches the code verifier.");
781
+ if (currentDatabase.resource) {
782
+ const selected = new URLSearchParams(actualBody).getAll("resource");
783
+ if (selected.length !== 1 || selected[0] !== currentDatabase.resource) throw new Error("Authorization resource mismatch.");
784
+ if (url !== normalizeUrl(currentDatabase.oidcServerConfiguration.tokenEndpoint)) throw new Error("Invalid resource token endpoint.");
785
+ currentDatabase.refreshGrant = currentDatabase;
786
+ }
787
+ currentDatabase.requestedScope = currentDatabase.authorizationScope ?? (currentDatabase.resource ? getResourceConfiguration(currentDatabase.oidcConfiguration?.resources, currentDatabase.resource).scope : currentDatabase.oidcConfiguration?.scope);
788
+ const generation = currentDatabase.generation;
789
+ let newBody = actualBody;
790
+ const codeVerifier = currentDatabase.codeVerifier;
791
+ if (codeVerifier != null) newBody = replaceCodeVerifier(newBody, codeVerifier);
792
+ const headersExtras = await generateDpopAsync(originalRequest, currentDatabase, url);
793
+ const resp = await fetch(originalRequest, {
794
+ body: newBody,
795
+ method: clonedRequest.method,
796
+ headers: headersExtras,
797
+ mode: clonedRequest.mode,
798
+ cache: clonedRequest.cache,
799
+ redirect: clonedRequest.redirect,
800
+ referrer: clonedRequest.referrer,
801
+ credentials: clonedRequest.credentials,
802
+ integrity: clonedRequest.integrity
803
+ });
804
+ resolve(await hideTokens(currentDatabase, () => currentDatabase.generation === generation, void 0, true)(resp));
599
805
  return;
600
806
  }
601
- resolve(await fetchPromise.then(hideTokens(currentDatabase)));
602
- return;
603
- }
604
- if (actualBody.includes("code_verifier=")) {
605
- const currentLoginCallbackConfigurationName = extractConfigurationNameFromCodeVerifier(actualBody);
606
- if (!currentLoginCallbackConfigurationName || currentLoginCallbackConfigurationName === "") throw new Error("No configuration name found in code_verifier");
607
- currentDatabase = database[currentLoginCallbackConfigurationName];
608
- let newBody = actualBody;
609
- const codeVerifier = currentDatabase.codeVerifier;
610
- if (codeVerifier != null) newBody = replaceCodeVerifier(newBody, codeVerifier);
611
- const headersExtras = await generateDpopAsync(originalRequest, currentDatabase, url);
612
- const resp = await fetch(originalRequest, {
613
- body: newBody,
807
+ resolve(await fetch(originalRequest, {
808
+ body: actualBody,
614
809
  method: clonedRequest.method,
615
- headers: headersExtras,
810
+ headers: serializeHeaders(originalRequest.headers),
616
811
  mode: clonedRequest.mode,
617
812
  cache: clonedRequest.cache,
618
813
  redirect: clonedRequest.redirect,
619
814
  referrer: clonedRequest.referrer,
620
815
  credentials: clonedRequest.credentials,
621
816
  integrity: clonedRequest.integrity
622
- });
623
- resolve(await hideTokens(currentDatabase)(resp));
624
- return;
817
+ }));
818
+ } catch (err) {
819
+ reject(err);
625
820
  }
626
- resolve(await fetch(originalRequest, {
627
- body: actualBody,
628
- method: clonedRequest.method,
629
- headers: serializeHeaders(originalRequest.headers),
630
- mode: clonedRequest.mode,
631
- cache: clonedRequest.cache,
632
- redirect: clonedRequest.redirect,
633
- referrer: clonedRequest.referrer,
634
- credentials: clonedRequest.credentials,
635
- integrity: clonedRequest.integrity
636
- }));
637
- } catch (err) {
638
- reject(err);
639
- }
640
- }).catch(reject);
641
- });
642
- return fetch(originalRequest);
643
- } catch (err) {
644
- console.error("[OidcServiceWorker] handleFetch error:", err);
645
- return new Response("Service Worker Error", { status: 500 });
646
- }
647
- })());
648
- };
649
- var handleMessage = async (event) => {
650
- const port = event.ports[0];
651
- const data = event.data;
652
- if (event.data?.type === "SKIP_WAITING") {
653
- await _self.skipWaiting();
654
- port?.postMessage?.({});
655
- return;
656
- } else if (event.data.type === "claim") {
657
- _self.clients.claim().then(() => port.postMessage({}));
658
- return;
659
- }
660
- const configurationName = data.configurationName.split("#")[0];
661
- if (trustedDomains == null) trustedDomains = {};
662
- const trustedDomain = trustedDomains[configurationName];
663
- const allowMultiTabLogin = Array.isArray(trustedDomain) ? false : trustedDomain.allowMultiTabLogin;
664
- const tabId = allowMultiTabLogin ? data.tabId : "default";
665
- const configurationNameWithTabId = `${configurationName}#tabId=${tabId}`;
666
- let currentDatabase = database[configurationNameWithTabId];
667
- if (!currentDatabase) {
668
- const showAccessToken = Array.isArray(trustedDomain) ? false : trustedDomain.showAccessToken;
669
- const doNotSetAccessTokenToNavigateRequests = Array.isArray(trustedDomain) ? true : trustedDomain.setAccessTokenToNavigateRequests;
670
- const convertAllRequestsToCorsExceptNavigate = Array.isArray(trustedDomain) ? false : trustedDomain.convertAllRequestsToCorsExceptNavigate;
671
- const bypassAllNonOidcRequests = Array.isArray(trustedDomain) ? false : trustedDomain.bypassAllNonOidcRequests;
672
- database[configurationNameWithTabId] = {
673
- tokens: null,
674
- state: null,
675
- codeVerifier: null,
676
- oidcServerConfiguration: null,
677
- oidcConfiguration: void 0,
678
- nonce: null,
679
- status: null,
680
- configurationName: configurationNameWithTabId,
681
- hideAccessToken: !showAccessToken,
682
- setAccessTokenToNavigateRequests: doNotSetAccessTokenToNavigateRequests ?? true,
683
- convertAllRequestsToCorsExceptNavigate: convertAllRequestsToCorsExceptNavigate ?? false,
684
- demonstratingProofOfPossessionNonce: null,
685
- demonstratingProofOfPossessionJwkJson: null,
686
- demonstratingProofOfPossessionConfiguration: null,
687
- demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent: false,
688
- allowMultiTabLogin: allowMultiTabLogin ?? false,
689
- bypassAllNonOidcRequests: bypassAllNonOidcRequests ?? false
690
- };
691
- currentDatabase = database[configurationNameWithTabId];
692
- if (!trustedDomains[configurationName]) trustedDomains[configurationName] = [];
693
- }
694
- switch (data.type) {
695
- case "clear":
696
- currentDatabase.tokens = null;
697
- currentDatabase.state = null;
698
- currentDatabase.codeVerifier = null;
699
- currentDatabase.nonce = null;
700
- currentDatabase.demonstratingProofOfPossessionNonce = null;
701
- currentDatabase.demonstratingProofOfPossessionJwkJson = null;
702
- currentDatabase.demonstratingProofOfPossessionConfiguration = null;
703
- currentDatabase.demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent = false;
704
- currentDatabase.status = data.data.status;
705
- port.postMessage({ configurationName });
706
- return;
707
- case "init": {
708
- const oidcServerConfiguration = data.data.oidcServerConfiguration;
709
- const domains = getDomains(trustedDomain, "oidc");
710
- if (!domains.some((domain) => domain === "*")) [
711
- oidcServerConfiguration.tokenEndpoint,
712
- oidcServerConfiguration.revocationEndpoint,
713
- oidcServerConfiguration.userInfoEndpoint,
714
- oidcServerConfiguration.issuer
715
- ].forEach((u) => {
716
- checkDomain(domains, u);
717
- });
718
- currentDatabase.oidcServerConfiguration = oidcServerConfiguration;
719
- currentDatabase.oidcConfiguration = data.data.oidcConfiguration;
720
- if (currentDatabase.demonstratingProofOfPossessionConfiguration == null) {
721
- const demonstratingProofOfPossessionConfiguration = getDpopConfiguration(trustedDomain);
722
- if (demonstratingProofOfPossessionConfiguration != null) {
723
- if (currentDatabase.oidcConfiguration.demonstrating_proof_of_possession) console.warn("In service worker, demonstrating_proof_of_possession must be configured from trustedDomains file");
724
- currentDatabase.demonstratingProofOfPossessionConfiguration = demonstratingProofOfPossessionConfiguration;
725
- currentDatabase.demonstratingProofOfPossessionJwkJson = await generateJwkAsync(self)(demonstratingProofOfPossessionConfiguration.generateKeyAlgorithm);
726
- currentDatabase.demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent = getDpopOnlyWhenDpopHeaderPresent(trustedDomain) ?? false;
727
- }
728
- }
729
- if (!currentDatabase.tokens) port.postMessage({
730
- tokens: null,
731
- status: currentDatabase.status,
732
- configurationName,
733
- version: version_default
734
- });
735
- else {
736
- const tokens = { ...currentDatabase.tokens };
737
- if (currentDatabase.hideAccessToken) tokens.access_token = `${TOKEN.ACCESS_TOKEN}_${configurationName}#tabId=${tabId}`;
738
- if (tokens.refresh_token) tokens.refresh_token = `${TOKEN.REFRESH_TOKEN}_${configurationName}#tabId=${tabId}`;
739
- if (tokens?.idTokenPayload?.nonce && currentDatabase.nonce != null) tokens.idTokenPayload.nonce = `${TOKEN.NONCE_TOKEN}_${configurationName}#tabId=${tabId}`;
740
- port.postMessage({
741
- tokens,
742
- status: currentDatabase.status,
743
- configurationName,
744
- version: version_default
821
+ }).catch(reject);
745
822
  });
823
+ return fetch(originalRequest);
824
+ } catch (err) {
825
+ console.error("[OidcServiceWorker] handleFetch error:", err);
826
+ return new Response("Service Worker Error", { status: 500 });
746
827
  }
828
+ })());
829
+ };
830
+ var handleMessageAsync = async (event) => {
831
+ const port = event.ports[0];
832
+ const data = event.data;
833
+ if (event.data?.type === "SKIP_WAITING") {
834
+ await _self.skipWaiting();
835
+ port?.postMessage?.({});
747
836
  return;
748
- }
749
- case "setDemonstratingProofOfPossessionNonce":
750
- currentDatabase.demonstratingProofOfPossessionNonce = data.data.demonstratingProofOfPossessionNonce;
751
- port.postMessage({ configurationName });
752
- return;
753
- case "getDemonstratingProofOfPossessionNonce": {
754
- const demonstratingProofOfPossessionNonce = currentDatabase.demonstratingProofOfPossessionNonce;
755
- port.postMessage({
756
- configurationName,
757
- demonstratingProofOfPossessionNonce
758
- });
759
- return;
760
- }
761
- case "setState":
762
- currentDatabase.state = data.data.state;
763
- port.postMessage({ configurationName });
764
- return;
765
- case "getState": {
766
- const state = currentDatabase.state;
767
- port.postMessage({
768
- configurationName,
769
- state
770
- });
837
+ } else if (event.data.type === "claim") {
838
+ _self.clients.claim().then(() => port.postMessage({}));
771
839
  return;
772
840
  }
773
- case "setCodeVerifier":
774
- currentDatabase.codeVerifier = data.data.codeVerifier;
775
- port.postMessage({ configurationName });
776
- return;
777
- case "getCodeVerifier": {
778
- const codeVerifier = currentDatabase.codeVerifier != null ? `${TOKEN.CODE_VERIFIER}_${configurationName}#tabId=${tabId}` : null;
779
- port.postMessage({
780
- configurationName,
781
- codeVerifier
782
- });
783
- return;
841
+ const configurationName = data.configurationName.split("#")[0];
842
+ const resource = getContextResource(data.configurationName);
843
+ if (trustedDomains == null) trustedDomains = {};
844
+ const trustedDomain = trustedDomains[configurationName];
845
+ const allowMultiTabLogin = Array.isArray(trustedDomain) ? false : trustedDomain.allowMultiTabLogin;
846
+ const configurationNameWithTabId = `${configurationName}#tabId=${allowMultiTabLogin ? data.tabId : "default"}`;
847
+ let currentDatabase = database[configurationNameWithTabId];
848
+ if (!currentDatabase) {
849
+ const showAccessToken = Array.isArray(trustedDomain) ? false : trustedDomain.showAccessToken;
850
+ const doNotSetAccessTokenToNavigateRequests = Array.isArray(trustedDomain) ? true : trustedDomain.setAccessTokenToNavigateRequests;
851
+ const convertAllRequestsToCorsExceptNavigate = Array.isArray(trustedDomain) ? false : trustedDomain.convertAllRequestsToCorsExceptNavigate;
852
+ const bypassAllNonOidcRequests = Array.isArray(trustedDomain) ? false : trustedDomain.bypassAllNonOidcRequests;
853
+ database[configurationNameWithTabId] = {
854
+ tokens: null,
855
+ state: null,
856
+ codeVerifier: null,
857
+ oidcServerConfiguration: null,
858
+ oidcConfiguration: void 0,
859
+ nonce: null,
860
+ status: null,
861
+ configurationName: configurationNameWithTabId,
862
+ hideAccessToken: !showAccessToken,
863
+ setAccessTokenToNavigateRequests: doNotSetAccessTokenToNavigateRequests ?? true,
864
+ convertAllRequestsToCorsExceptNavigate: convertAllRequestsToCorsExceptNavigate ?? false,
865
+ demonstratingProofOfPossessionNonce: null,
866
+ demonstratingProofOfPossessionJwkJson: null,
867
+ demonstratingProofOfPossessionConfiguration: null,
868
+ demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent: false,
869
+ allowMultiTabLogin: allowMultiTabLogin ?? false,
870
+ bypassAllNonOidcRequests: bypassAllNonOidcRequests ?? false
871
+ };
872
+ currentDatabase = database[configurationNameWithTabId];
873
+ if (!trustedDomains[configurationName]) trustedDomains[configurationName] = [];
784
874
  }
785
- case "setSessionState":
786
- currentDatabase.sessionState = data.data.sessionState;
787
- port.postMessage({ configurationName });
788
- return;
789
- case "getSessionState": {
790
- const sessionState = currentDatabase.sessionState;
875
+ if (resource !== void 0) try {
876
+ currentDatabase = getOrCreateResourceContext(currentDatabase, resource);
877
+ } catch (error) {
878
+ console.error("[OidcServiceWorker] Invalid resource context:", error);
791
879
  port.postMessage({
792
880
  configurationName,
793
- sessionState
881
+ error: "The resource is not registered."
794
882
  });
795
883
  return;
796
884
  }
797
- case "setNonce": {
798
- const nonce = data.data.nonce;
799
- if (nonce) currentDatabase.nonce = nonce;
800
- port.postMessage({ configurationName });
801
- return;
885
+ switch (data.type) {
886
+ case "clear":
887
+ currentDatabase.generation = (currentDatabase.generation ?? 0) + 1;
888
+ for (const entry of Object.values(currentDatabase.resourceEntries ?? {})) entry.generation = (entry.generation ?? 0) + 1;
889
+ currentDatabase.resourceEntries = void 0;
890
+ currentDatabase.tokens = null;
891
+ currentDatabase.state = null;
892
+ currentDatabase.codeVerifier = null;
893
+ currentDatabase.nonce = null;
894
+ currentDatabase.demonstratingProofOfPossessionNonce = null;
895
+ currentDatabase.demonstratingProofOfPossessionJwkJson = null;
896
+ currentDatabase.demonstratingProofOfPossessionConfiguration = null;
897
+ currentDatabase.demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent = false;
898
+ currentDatabase.authorizationScope = void 0;
899
+ currentDatabase.requestedScope = void 0;
900
+ currentDatabase.demonstratingProofOfPossessionApiNonce = null;
901
+ currentDatabase.status = data.data.status;
902
+ port.postMessage({ configurationName });
903
+ return;
904
+ case "init": {
905
+ const oidcServerConfiguration = data.data.oidcServerConfiguration;
906
+ const domains = getDomains(trustedDomain, "oidc");
907
+ if (!domains.some((domain) => domain === "*")) [
908
+ oidcServerConfiguration.tokenEndpoint,
909
+ oidcServerConfiguration.revocationEndpoint,
910
+ oidcServerConfiguration.userInfoEndpoint,
911
+ oidcServerConfiguration.issuer
912
+ ].forEach((u) => {
913
+ checkDomain(domains, u);
914
+ });
915
+ currentDatabase.oidcServerConfiguration = oidcServerConfiguration;
916
+ currentDatabase.oidcConfiguration = data.data.oidcConfiguration;
917
+ if (data.data.where === "loginAsync") currentDatabase.authorizationScope = currentDatabase.oidcConfiguration.scope;
918
+ if (resource !== void 0) getResourceConfiguration(currentDatabase.oidcConfiguration.resources, resource);
919
+ if (currentDatabase.demonstratingProofOfPossessionConfiguration == null) {
920
+ const parent = currentDatabase.parentConfiguration;
921
+ if (parent?.demonstratingProofOfPossessionJwkJson) {
922
+ currentDatabase.demonstratingProofOfPossessionJwkJson = parent.demonstratingProofOfPossessionJwkJson;
923
+ currentDatabase.demonstratingProofOfPossessionConfiguration = parent.demonstratingProofOfPossessionConfiguration;
924
+ }
925
+ const demonstratingProofOfPossessionConfiguration = getDpopConfiguration(trustedDomain);
926
+ if (demonstratingProofOfPossessionConfiguration != null && currentDatabase.demonstratingProofOfPossessionConfiguration == null) {
927
+ if (currentDatabase.oidcConfiguration.demonstrating_proof_of_possession) console.warn("In service worker, demonstrating_proof_of_possession must be configured from trustedDomains file");
928
+ currentDatabase.demonstratingProofOfPossessionConfiguration = demonstratingProofOfPossessionConfiguration;
929
+ currentDatabase.demonstratingProofOfPossessionJwkJson = await generateJwkAsync(self)(demonstratingProofOfPossessionConfiguration.generateKeyAlgorithm);
930
+ currentDatabase.demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent = getDpopOnlyWhenDpopHeaderPresent(trustedDomain) ?? false;
931
+ }
932
+ }
933
+ if (!currentDatabase.tokens) port.postMessage({
934
+ tokens: null,
935
+ status: currentDatabase.status,
936
+ configurationName,
937
+ version: version_default,
938
+ resourceTokenManagement: true,
939
+ resourceContexts: Object.keys(currentDatabase.resourceEntries ?? {})
940
+ });
941
+ else {
942
+ const tokens = {
943
+ ...currentDatabase.tokens,
944
+ idTokenPayload: currentDatabase.tokens.idTokenPayload ? { ...currentDatabase.tokens.idTokenPayload } : void 0
945
+ };
946
+ if (currentDatabase.hideAccessToken) tokens.access_token = `${TOKEN.ACCESS_TOKEN}_${currentDatabase.configurationName}`;
947
+ if (tokens.refresh_token) tokens.refresh_token = `${TOKEN.REFRESH_TOKEN}_${(currentDatabase.refreshGrant ?? currentDatabase).configurationName}`;
948
+ if (tokens?.idTokenPayload?.nonce && currentDatabase.nonce != null) tokens.idTokenPayload.nonce = `${TOKEN.NONCE_TOKEN}_${currentDatabase.configurationName}`;
949
+ port.postMessage({
950
+ tokens,
951
+ status: currentDatabase.status,
952
+ configurationName,
953
+ version: version_default,
954
+ resourceTokenManagement: true,
955
+ resourceContexts: Object.keys(currentDatabase.resourceEntries ?? {})
956
+ });
957
+ }
958
+ return;
959
+ }
960
+ case "setDemonstratingProofOfPossessionNonce":
961
+ currentDatabase.demonstratingProofOfPossessionNonce = data.data.demonstratingProofOfPossessionNonce;
962
+ port.postMessage({ configurationName });
963
+ return;
964
+ case "getDemonstratingProofOfPossessionNonce": {
965
+ const demonstratingProofOfPossessionNonce = currentDatabase.demonstratingProofOfPossessionNonce;
966
+ port.postMessage({
967
+ configurationName,
968
+ demonstratingProofOfPossessionNonce
969
+ });
970
+ return;
971
+ }
972
+ case "setState":
973
+ currentDatabase.state = data.data.state;
974
+ port.postMessage({ configurationName });
975
+ return;
976
+ case "getState": {
977
+ const state = currentDatabase.state;
978
+ port.postMessage({
979
+ configurationName,
980
+ state
981
+ });
982
+ return;
983
+ }
984
+ case "setCodeVerifier":
985
+ currentDatabase.codeVerifier = data.data.codeVerifier;
986
+ port.postMessage({ configurationName });
987
+ return;
988
+ case "getCodeVerifier": {
989
+ const codeVerifier = currentDatabase.codeVerifier != null ? `${TOKEN.CODE_VERIFIER}_${currentDatabase.configurationName}` : null;
990
+ port.postMessage({
991
+ configurationName,
992
+ codeVerifier
993
+ });
994
+ return;
995
+ }
996
+ case "setSessionState":
997
+ currentDatabase.sessionState = data.data.sessionState;
998
+ port.postMessage({ configurationName });
999
+ return;
1000
+ case "getSessionState": {
1001
+ const sessionState = currentDatabase.sessionState;
1002
+ port.postMessage({
1003
+ configurationName,
1004
+ sessionState
1005
+ });
1006
+ return;
1007
+ }
1008
+ case "setNonce": {
1009
+ const nonce = data.data.nonce;
1010
+ if (nonce) {
1011
+ if (!currentDatabase.resource && currentDatabase.nonce && nonce.nonce !== currentDatabase.nonce.nonce) {
1012
+ currentDatabase.generation = (currentDatabase.generation ?? 0) + 1;
1013
+ for (const entry of Object.values(currentDatabase.resourceEntries ?? {})) entry.generation = (entry.generation ?? 0) + 1;
1014
+ currentDatabase.resourceEntries = void 0;
1015
+ }
1016
+ currentDatabase.nonce = nonce;
1017
+ }
1018
+ port.postMessage({ configurationName });
1019
+ return;
1020
+ }
1021
+ case "getNonce": {
1022
+ const keyNonce = `${TOKEN.NONCE_TOKEN}_${currentDatabase.configurationName}`;
1023
+ const nonce = currentDatabase.nonce ? keyNonce : null;
1024
+ port.postMessage({
1025
+ configurationName,
1026
+ nonce
1027
+ });
1028
+ return;
1029
+ }
1030
+ default: return;
802
1031
  }
803
- case "getNonce": {
804
- const keyNonce = `${TOKEN.NONCE_TOKEN}_${configurationName}#tabId=${tabId}`;
805
- const nonce = currentDatabase.nonce ? keyNonce : null;
806
- port.postMessage({
807
- configurationName,
808
- nonce
1032
+ };
1033
+ var handleMessage = async (event) => {
1034
+ try {
1035
+ await handleMessageAsync(event);
1036
+ } catch (error) {
1037
+ console.error("[OidcServiceWorker] Message failed:", error);
1038
+ event.ports?.[0]?.postMessage({
1039
+ configurationName: event.data?.configurationName,
1040
+ error: error instanceof Error ? error.message : "The worker could not process this message."
809
1041
  });
810
- return;
811
1042
  }
812
- default: return;
813
- }
814
- };
815
- _self.addEventListener("fetch", handleFetch);
816
- _self.addEventListener("message", handleMessage);
817
- //#endregion
1043
+ };
1044
+ _self.addEventListener("fetch", handleFetch);
1045
+ _self.addEventListener("message", handleMessage);
1046
+ //#endregion
1047
+ })();
818
1048
 
819
1049
  //# sourceMappingURL=OidcServiceWorker.js.map