@axa-fr/oidc-client-service-worker 7.22.18 → 7.22.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/OidcServiceWorker.js +248 -222
- package/dist/OidcServiceWorker.js.map +1 -1
- package/dist/OidcTrustedDomains.js +15 -15
- package/dist/src/crypto.d.ts.map +1 -1
- package/dist/src/dpop.d.ts.map +1 -1
- package/dist/src/jwt.d.ts +2 -2
- package/dist/src/jwt.d.ts.map +1 -1
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/utils/__tests__/testHelper.d.ts.map +1 -1
- package/dist/src/utils/codeVerifier.d.ts.map +1 -1
- package/dist/src/utils/domains.d.ts.map +1 -1
- package/dist/src/utils/sleep.d.ts.map +1 -1
- package/dist/src/utils/tokens.d.ts.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/OidcServiceWorker.ts +476 -476
- package/src/OidcTrustedDomains.js +15 -15
- package/src/__tests__/oidcConfig.spec.ts +35 -34
- package/src/crypto.ts +8 -6
- package/src/dpop.ts +28 -27
- package/src/jwt.ts +152 -140
- package/src/oidcConfig.ts +9 -9
- package/src/types.ts +118 -90
- package/src/utils/__tests__/codeVerifier.spec.ts +30 -21
- package/src/utils/__tests__/domains.spec.ts +131 -131
- package/src/utils/__tests__/normalizeUrl.spec.ts +21 -21
- package/src/utils/__tests__/serializeHeaders.spec.ts +1 -3
- package/src/utils/__tests__/testHelper.ts +11 -25
- package/src/utils/__tests__/tokens.spec.ts +71 -51
- package/src/utils/codeVerifier.ts +12 -12
- package/src/utils/domains.ts +74 -74
- package/src/utils/normalizeUrl.ts +6 -7
- package/src/utils/sleep.ts +1 -1
- package/src/utils/tokens.ts +70 -63
- package/src/version.ts +1 -1
|
@@ -12,6 +12,196 @@ const TokenRenewMode = {
|
|
|
12
12
|
id_token_invalid: "id_token_invalid"
|
|
13
13
|
};
|
|
14
14
|
const openidWellknownUrlEndWith = "/.well-known/openid-configuration";
|
|
15
|
+
function strToUint8(str) {
|
|
16
|
+
return new TextEncoder().encode(str);
|
|
17
|
+
}
|
|
18
|
+
function binToUrlBase64(bin) {
|
|
19
|
+
return btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+/g, "");
|
|
20
|
+
}
|
|
21
|
+
function utf8ToBinaryString(str) {
|
|
22
|
+
const escstr = encodeURIComponent(str);
|
|
23
|
+
return escstr.replace(/%([0-9A-F]{2})/g, function(match, p1) {
|
|
24
|
+
return String.fromCharCode(parseInt(p1, 16));
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
const 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
|
+
const defaultDemonstratingProofOfPossessionConfiguration = {
|
|
38
|
+
importKeyAlgorithm: {
|
|
39
|
+
name: "ECDSA",
|
|
40
|
+
namedCurve: "P-256",
|
|
41
|
+
hash: { name: "ES256" }
|
|
42
|
+
},
|
|
43
|
+
signAlgorithm: { name: "ECDSA", hash: { name: "SHA-256" } },
|
|
44
|
+
generateKeyAlgorithm: {
|
|
45
|
+
name: "ECDSA",
|
|
46
|
+
namedCurve: "P-256"
|
|
47
|
+
},
|
|
48
|
+
digestAlgorithm: { name: "SHA-256" },
|
|
49
|
+
jwtHeaderAlgorithm: "ES256"
|
|
50
|
+
};
|
|
51
|
+
const sign = (w) => async (jwk, headers, claims, demonstratingProofOfPossessionConfiguration, jwtHeaderType = "dpop+jwt") => {
|
|
52
|
+
jwk = Object.assign({}, jwk);
|
|
53
|
+
headers.typ = jwtHeaderType;
|
|
54
|
+
headers.alg = demonstratingProofOfPossessionConfiguration.jwtHeaderAlgorithm;
|
|
55
|
+
switch (headers.alg) {
|
|
56
|
+
case "ES256":
|
|
57
|
+
headers.jwk = { kty: jwk.kty, crv: jwk.crv, x: jwk.x, y: jwk.y };
|
|
58
|
+
break;
|
|
59
|
+
case "RS256":
|
|
60
|
+
headers.jwk = { kty: jwk.kty, n: jwk.n, e: jwk.e, kid: headers.kid };
|
|
61
|
+
break;
|
|
62
|
+
default:
|
|
63
|
+
throw new Error("Unknown or not implemented JWS algorithm");
|
|
64
|
+
}
|
|
65
|
+
const jws = {
|
|
66
|
+
// @ts-ignore
|
|
67
|
+
// JWT "headers" really means JWS "protected headers"
|
|
68
|
+
protected: strToUrlBase64(JSON.stringify(headers)),
|
|
69
|
+
// @ts-ignore
|
|
70
|
+
// JWT "claims" are really a JSON-defined JWS "payload"
|
|
71
|
+
payload: strToUrlBase64(JSON.stringify(claims))
|
|
72
|
+
};
|
|
73
|
+
const keyType = demonstratingProofOfPossessionConfiguration.importKeyAlgorithm;
|
|
74
|
+
const exportable = true;
|
|
75
|
+
const privileges = ["sign"];
|
|
76
|
+
const privateKey = await w.crypto.subtle.importKey("jwk", jwk, keyType, exportable, privileges);
|
|
77
|
+
const data = strToUint8(`${jws.protected}.${jws.payload}`);
|
|
78
|
+
const signatureType = demonstratingProofOfPossessionConfiguration.signAlgorithm;
|
|
79
|
+
const signature = await w.crypto.subtle.sign(signatureType, privateKey, data);
|
|
80
|
+
jws.signature = uint8ToUrlBase64(new Uint8Array(signature));
|
|
81
|
+
return `${jws.protected}.${jws.payload}.${jws.signature}`;
|
|
82
|
+
};
|
|
83
|
+
const JWT = { sign };
|
|
84
|
+
const generate = (w) => async (generateKeyAlgorithm) => {
|
|
85
|
+
const keyType = generateKeyAlgorithm;
|
|
86
|
+
const exportable = true;
|
|
87
|
+
const privileges = ["sign", "verify"];
|
|
88
|
+
const key = await w.crypto.subtle.generateKey(keyType, exportable, privileges);
|
|
89
|
+
return await w.crypto.subtle.exportKey("jwk", key.privateKey);
|
|
90
|
+
};
|
|
91
|
+
const neuter = (jwk) => {
|
|
92
|
+
const copy = Object.assign({}, jwk);
|
|
93
|
+
delete copy.d;
|
|
94
|
+
copy.key_ops = ["verify"];
|
|
95
|
+
return copy;
|
|
96
|
+
};
|
|
97
|
+
const EC = {
|
|
98
|
+
generate,
|
|
99
|
+
neuter
|
|
100
|
+
};
|
|
101
|
+
const thumbprint = (w) => async (jwk, digestAlgorithm) => {
|
|
102
|
+
let sortedPub;
|
|
103
|
+
switch (jwk.kty) {
|
|
104
|
+
case "EC":
|
|
105
|
+
sortedPub = '{"crv":"CRV","kty":"EC","x":"X","y":"Y"}'.replace("CRV", jwk.crv).replace("X", jwk.x).replace("Y", jwk.y);
|
|
106
|
+
break;
|
|
107
|
+
case "RSA":
|
|
108
|
+
sortedPub = '{"e":"E","kty":"RSA","n":"N"}'.replace("E", jwk.e).replace("N", jwk.n);
|
|
109
|
+
break;
|
|
110
|
+
default:
|
|
111
|
+
throw new Error("Unknown or not implemented JWK type");
|
|
112
|
+
}
|
|
113
|
+
const hash = await w.crypto.subtle.digest(digestAlgorithm, strToUint8(sortedPub));
|
|
114
|
+
return uint8ToUrlBase64(new Uint8Array(hash));
|
|
115
|
+
};
|
|
116
|
+
const JWK = { thumbprint };
|
|
117
|
+
const generateJwkAsync = (w) => async (generateKeyAlgorithm) => {
|
|
118
|
+
const jwk = await EC.generate(w)(generateKeyAlgorithm);
|
|
119
|
+
return jwk;
|
|
120
|
+
};
|
|
121
|
+
const generateJwtDemonstratingProofOfPossessionAsync = (w) => (demonstratingProofOfPossessionConfiguration) => async (jwk, method = "POST", url, extrasClaims = {}) => {
|
|
122
|
+
const claims = {
|
|
123
|
+
// https://www.rfc-editor.org/rfc/rfc9449.html#name-concept
|
|
124
|
+
jti: btoa(guid()),
|
|
125
|
+
htm: method,
|
|
126
|
+
htu: url,
|
|
127
|
+
iat: Math.round(Date.now() / 1e3),
|
|
128
|
+
...extrasClaims
|
|
129
|
+
};
|
|
130
|
+
const kid = await JWK.thumbprint(w)(
|
|
131
|
+
jwk,
|
|
132
|
+
demonstratingProofOfPossessionConfiguration.digestAlgorithm
|
|
133
|
+
);
|
|
134
|
+
const jwt = await JWT.sign(w)(
|
|
135
|
+
jwk,
|
|
136
|
+
{ kid },
|
|
137
|
+
claims,
|
|
138
|
+
demonstratingProofOfPossessionConfiguration
|
|
139
|
+
);
|
|
140
|
+
return jwt;
|
|
141
|
+
};
|
|
142
|
+
const guid = () => {
|
|
143
|
+
const guidHolder = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx";
|
|
144
|
+
const hex = "0123456789abcdef";
|
|
145
|
+
let r = 0;
|
|
146
|
+
let guidResponse = "";
|
|
147
|
+
for (let i = 0; i < 36; i++) {
|
|
148
|
+
if (guidHolder[i] !== "-" && guidHolder[i] !== "4") {
|
|
149
|
+
r = Math.random() * 16 | 0;
|
|
150
|
+
}
|
|
151
|
+
if (guidHolder[i] === "x") {
|
|
152
|
+
guidResponse += hex[r];
|
|
153
|
+
} else if (guidHolder[i] === "y") {
|
|
154
|
+
r &= 3;
|
|
155
|
+
r |= 8;
|
|
156
|
+
guidResponse += hex[r];
|
|
157
|
+
} else {
|
|
158
|
+
guidResponse += guidHolder[i];
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return guidResponse;
|
|
162
|
+
};
|
|
163
|
+
function textEncodeLite(str) {
|
|
164
|
+
const buf = new ArrayBuffer(str.length);
|
|
165
|
+
const bufView = new Uint8Array(buf);
|
|
166
|
+
for (let i = 0; i < str.length; i++) {
|
|
167
|
+
bufView[i] = str.charCodeAt(i);
|
|
168
|
+
}
|
|
169
|
+
return bufView;
|
|
170
|
+
}
|
|
171
|
+
function base64urlOfHashOfASCIIEncodingAsync(code) {
|
|
172
|
+
return new Promise((resolve, reject) => {
|
|
173
|
+
crypto.subtle.digest("SHA-256", textEncodeLite(code)).then(
|
|
174
|
+
(buffer) => {
|
|
175
|
+
return resolve(uint8ToUrlBase64(new Uint8Array(buffer)));
|
|
176
|
+
},
|
|
177
|
+
(error) => reject(error)
|
|
178
|
+
);
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
const isDpop = (trustedDomain) => {
|
|
182
|
+
if (Array.isArray(trustedDomain)) {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
return trustedDomain.demonstratingProofOfPossession ?? false;
|
|
186
|
+
};
|
|
187
|
+
const getDpopConfiguration = (trustedDomain) => {
|
|
188
|
+
if (!isDpop(trustedDomain)) {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
if (Array.isArray(trustedDomain)) {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
return trustedDomain.demonstratingProofOfPossessionConfiguration ?? defaultDemonstratingProofOfPossessionConfiguration;
|
|
195
|
+
};
|
|
196
|
+
const getDpopOnlyWhenDpopHeaderPresent = (trustedDomain) => {
|
|
197
|
+
if (!isDpop(trustedDomain)) {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
if (Array.isArray(trustedDomain)) {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
return trustedDomain.demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent ?? true;
|
|
204
|
+
};
|
|
15
205
|
function normalizeUrl(url) {
|
|
16
206
|
try {
|
|
17
207
|
return new URL(url).toString();
|
|
@@ -103,23 +293,16 @@ function countLetter(str, find) {
|
|
|
103
293
|
return str.split(find).length - 1;
|
|
104
294
|
}
|
|
105
295
|
const parseJwt = (payload) => {
|
|
106
|
-
return JSON.parse(
|
|
107
|
-
b64DecodeUnicode(payload.replaceAll(/-/g, "+").replaceAll(/_/g, "/"))
|
|
108
|
-
);
|
|
296
|
+
return JSON.parse(b64DecodeUnicode(payload.replaceAll(/-/g, "+").replaceAll(/_/g, "/")));
|
|
109
297
|
};
|
|
110
298
|
function b64DecodeUnicode(str) {
|
|
111
299
|
return decodeURIComponent(
|
|
112
|
-
Array.prototype.map.call(
|
|
113
|
-
atob(str),
|
|
114
|
-
(c) => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2)
|
|
115
|
-
).join("")
|
|
300
|
+
Array.prototype.map.call(atob(str), (c) => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2)).join("")
|
|
116
301
|
);
|
|
117
302
|
}
|
|
118
303
|
function computeTimeLeft(refreshTimeBeforeTokensExpirationInSecond, expiresAt) {
|
|
119
304
|
const currentTimeUnixSecond = (/* @__PURE__ */ new Date()).getTime() / 1e3;
|
|
120
|
-
return Math.round(
|
|
121
|
-
expiresAt - refreshTimeBeforeTokensExpirationInSecond - currentTimeUnixSecond
|
|
122
|
-
);
|
|
305
|
+
return Math.round(expiresAt - refreshTimeBeforeTokensExpirationInSecond - currentTimeUnixSecond);
|
|
123
306
|
}
|
|
124
307
|
function isTokensValid(tokens) {
|
|
125
308
|
if (!tokens) {
|
|
@@ -146,18 +329,30 @@ const isTokensOidcValid = (tokens, nonce, oidcServerConfiguration) => {
|
|
|
146
329
|
if (tokens.idTokenPayload) {
|
|
147
330
|
const idTokenPayload = tokens.idTokenPayload;
|
|
148
331
|
if (idTokenPayload && oidcServerConfiguration.issuer !== idTokenPayload.iss) {
|
|
149
|
-
return {
|
|
332
|
+
return {
|
|
333
|
+
isValid: false,
|
|
334
|
+
reason: `Issuer does not match (oidcServerConfiguration issuer) ${oidcServerConfiguration.issuer} !== (idTokenPayload issuer) ${idTokenPayload.iss}`
|
|
335
|
+
};
|
|
150
336
|
}
|
|
151
337
|
const currentTimeUnixSecond = (/* @__PURE__ */ new Date()).getTime() / 1e3;
|
|
152
338
|
if (idTokenPayload && idTokenPayload.exp && idTokenPayload.exp < currentTimeUnixSecond) {
|
|
153
|
-
return {
|
|
339
|
+
return {
|
|
340
|
+
isValid: false,
|
|
341
|
+
reason: `Token expired at (idTokenPayload exp) ${idTokenPayload.exp} < (currentTimeUnixSecond) ${currentTimeUnixSecond}`
|
|
342
|
+
};
|
|
154
343
|
}
|
|
155
344
|
const timeInSevenDays = 60 * 60 * 24 * 7;
|
|
156
345
|
if (idTokenPayload && idTokenPayload.iat && idTokenPayload.iat + timeInSevenDays < currentTimeUnixSecond) {
|
|
157
|
-
return {
|
|
346
|
+
return {
|
|
347
|
+
isValid: false,
|
|
348
|
+
reason: `Token is used from too long time (idTokenPayload iat + timeInSevenDays) ${idTokenPayload.iat + timeInSevenDays} < (currentTimeUnixSecond) ${currentTimeUnixSecond}`
|
|
349
|
+
};
|
|
158
350
|
}
|
|
159
351
|
if (idTokenPayload && nonce && idTokenPayload.nonce && idTokenPayload.nonce !== nonce) {
|
|
160
|
-
return {
|
|
352
|
+
return {
|
|
353
|
+
isValid: false,
|
|
354
|
+
reason: `Nonce does not match (nonce) ${nonce} !== (idTokenPayload nonce) ${idTokenPayload.nonce}`
|
|
355
|
+
};
|
|
161
356
|
}
|
|
162
357
|
}
|
|
163
358
|
return { isValid: true, reason: "" };
|
|
@@ -260,11 +455,18 @@ function hideTokens(currentDatabaseElement, currentTabId) {
|
|
|
260
455
|
}
|
|
261
456
|
const newHeaders = new Headers(response.headers);
|
|
262
457
|
if (response.headers.has(demonstratingProofOfPossessionNonceResponseHeader)) {
|
|
263
|
-
currentDatabaseElement.demonstratingProofOfPossessionNonce = response.headers.get(
|
|
458
|
+
currentDatabaseElement.demonstratingProofOfPossessionNonce = response.headers.get(
|
|
459
|
+
demonstratingProofOfPossessionNonceResponseHeader
|
|
460
|
+
);
|
|
264
461
|
newHeaders.delete(demonstratingProofOfPossessionNonceResponseHeader);
|
|
265
462
|
}
|
|
266
463
|
return response.json().then((tokens) => {
|
|
267
|
-
const secureTokens = _hideTokens(
|
|
464
|
+
const secureTokens = _hideTokens(
|
|
465
|
+
tokens,
|
|
466
|
+
currentDatabaseElement,
|
|
467
|
+
configurationName,
|
|
468
|
+
currentTabId
|
|
469
|
+
);
|
|
268
470
|
const body = JSON.stringify(secureTokens);
|
|
269
471
|
return new Response(body, {
|
|
270
472
|
status: response.status,
|
|
@@ -274,6 +476,14 @@ function hideTokens(currentDatabaseElement, currentTabId) {
|
|
|
274
476
|
});
|
|
275
477
|
};
|
|
276
478
|
}
|
|
479
|
+
const getMatchingOidcConfigurations = (database2, url) => {
|
|
480
|
+
return Object.values(database2).filter((config) => {
|
|
481
|
+
const { oidcServerConfiguration } = config || {};
|
|
482
|
+
const { tokenEndpoint, revocationEndpoint } = oidcServerConfiguration || {};
|
|
483
|
+
const normalizedUrl = normalizeUrl(url);
|
|
484
|
+
return tokenEndpoint && normalizedUrl.startsWith(normalizeUrl(tokenEndpoint)) || revocationEndpoint && normalizedUrl.startsWith(normalizeUrl(revocationEndpoint));
|
|
485
|
+
});
|
|
486
|
+
};
|
|
277
487
|
function replaceCodeVerifier(codeVerifier, newCodeVerifier) {
|
|
278
488
|
const regex = /code_verifier=[A-Za-z0-9_-]+/i;
|
|
279
489
|
return codeVerifier.replace(regex, `code_verifier=${newCodeVerifier}`);
|
|
@@ -287,194 +497,7 @@ const extractConfigurationNameFromCodeVerifier = (chaine) => {
|
|
|
287
497
|
return null;
|
|
288
498
|
}
|
|
289
499
|
};
|
|
290
|
-
const version = "7.22.
|
|
291
|
-
function strToUint8(str) {
|
|
292
|
-
return new TextEncoder().encode(str);
|
|
293
|
-
}
|
|
294
|
-
function binToUrlBase64(bin) {
|
|
295
|
-
return btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+/g, "");
|
|
296
|
-
}
|
|
297
|
-
function utf8ToBinaryString(str) {
|
|
298
|
-
const escstr = encodeURIComponent(str);
|
|
299
|
-
return escstr.replace(/%([0-9A-F]{2})/g, function(match, p1) {
|
|
300
|
-
return String.fromCharCode(parseInt(p1, 16));
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
const uint8ToUrlBase64 = (uint8) => {
|
|
304
|
-
let bin = "";
|
|
305
|
-
uint8.forEach(function(code) {
|
|
306
|
-
bin += String.fromCharCode(code);
|
|
307
|
-
});
|
|
308
|
-
return binToUrlBase64(bin);
|
|
309
|
-
};
|
|
310
|
-
function strToUrlBase64(str) {
|
|
311
|
-
return binToUrlBase64(utf8ToBinaryString(str));
|
|
312
|
-
}
|
|
313
|
-
const defaultDemonstratingProofOfPossessionConfiguration = {
|
|
314
|
-
importKeyAlgorithm: {
|
|
315
|
-
name: "ECDSA",
|
|
316
|
-
namedCurve: "P-256",
|
|
317
|
-
hash: { name: "ES256" }
|
|
318
|
-
},
|
|
319
|
-
signAlgorithm: { name: "ECDSA", hash: { name: "SHA-256" } },
|
|
320
|
-
generateKeyAlgorithm: {
|
|
321
|
-
name: "ECDSA",
|
|
322
|
-
namedCurve: "P-256"
|
|
323
|
-
},
|
|
324
|
-
digestAlgorithm: { name: "SHA-256" },
|
|
325
|
-
jwtHeaderAlgorithm: "ES256"
|
|
326
|
-
};
|
|
327
|
-
const sign = (w) => async (jwk, headers, claims, demonstratingProofOfPossessionConfiguration, jwtHeaderType = "dpop+jwt") => {
|
|
328
|
-
jwk = Object.assign({}, jwk);
|
|
329
|
-
headers.typ = jwtHeaderType;
|
|
330
|
-
headers.alg = demonstratingProofOfPossessionConfiguration.jwtHeaderAlgorithm;
|
|
331
|
-
switch (headers.alg) {
|
|
332
|
-
case "ES256":
|
|
333
|
-
headers.jwk = { kty: jwk.kty, crv: jwk.crv, x: jwk.x, y: jwk.y };
|
|
334
|
-
break;
|
|
335
|
-
case "RS256":
|
|
336
|
-
headers.jwk = { kty: jwk.kty, n: jwk.n, e: jwk.e, kid: headers.kid };
|
|
337
|
-
break;
|
|
338
|
-
default:
|
|
339
|
-
throw new Error("Unknown or not implemented JWS algorithm");
|
|
340
|
-
}
|
|
341
|
-
const jws = {
|
|
342
|
-
// @ts-ignore
|
|
343
|
-
// JWT "headers" really means JWS "protected headers"
|
|
344
|
-
protected: strToUrlBase64(JSON.stringify(headers)),
|
|
345
|
-
// @ts-ignore
|
|
346
|
-
// JWT "claims" are really a JSON-defined JWS "payload"
|
|
347
|
-
payload: strToUrlBase64(JSON.stringify(claims))
|
|
348
|
-
};
|
|
349
|
-
const keyType = demonstratingProofOfPossessionConfiguration.importKeyAlgorithm;
|
|
350
|
-
const exportable = true;
|
|
351
|
-
const privileges = ["sign"];
|
|
352
|
-
const privateKey = await w.crypto.subtle.importKey("jwk", jwk, keyType, exportable, privileges);
|
|
353
|
-
const data = strToUint8(`${jws.protected}.${jws.payload}`);
|
|
354
|
-
const signatureType = demonstratingProofOfPossessionConfiguration.signAlgorithm;
|
|
355
|
-
const signature = await w.crypto.subtle.sign(signatureType, privateKey, data);
|
|
356
|
-
jws.signature = uint8ToUrlBase64(new Uint8Array(signature));
|
|
357
|
-
return `${jws.protected}.${jws.payload}.${jws.signature}`;
|
|
358
|
-
};
|
|
359
|
-
var JWT = { sign };
|
|
360
|
-
const generate = (w) => async (generateKeyAlgorithm) => {
|
|
361
|
-
const keyType = generateKeyAlgorithm;
|
|
362
|
-
const exportable = true;
|
|
363
|
-
const privileges = ["sign", "verify"];
|
|
364
|
-
const key = await w.crypto.subtle.generateKey(keyType, exportable, privileges);
|
|
365
|
-
return await w.crypto.subtle.exportKey("jwk", key.privateKey);
|
|
366
|
-
};
|
|
367
|
-
const neuter = (jwk) => {
|
|
368
|
-
const copy = Object.assign({}, jwk);
|
|
369
|
-
delete copy.d;
|
|
370
|
-
copy.key_ops = ["verify"];
|
|
371
|
-
return copy;
|
|
372
|
-
};
|
|
373
|
-
const EC = {
|
|
374
|
-
generate,
|
|
375
|
-
neuter
|
|
376
|
-
};
|
|
377
|
-
const thumbprint = (w) => async (jwk, digestAlgorithm) => {
|
|
378
|
-
let sortedPub;
|
|
379
|
-
switch (jwk.kty) {
|
|
380
|
-
case "EC":
|
|
381
|
-
sortedPub = '{"crv":"CRV","kty":"EC","x":"X","y":"Y"}'.replace("CRV", jwk.crv).replace("X", jwk.x).replace("Y", jwk.y);
|
|
382
|
-
break;
|
|
383
|
-
case "RSA":
|
|
384
|
-
sortedPub = '{"e":"E","kty":"RSA","n":"N"}'.replace("E", jwk.e).replace("N", jwk.n);
|
|
385
|
-
break;
|
|
386
|
-
default:
|
|
387
|
-
throw new Error("Unknown or not implemented JWK type");
|
|
388
|
-
}
|
|
389
|
-
const hash = await w.crypto.subtle.digest(digestAlgorithm, strToUint8(sortedPub));
|
|
390
|
-
return uint8ToUrlBase64(new Uint8Array(hash));
|
|
391
|
-
};
|
|
392
|
-
var JWK = { thumbprint };
|
|
393
|
-
const generateJwkAsync = (w) => async (generateKeyAlgorithm) => {
|
|
394
|
-
const jwk = await EC.generate(w)(generateKeyAlgorithm);
|
|
395
|
-
return jwk;
|
|
396
|
-
};
|
|
397
|
-
const generateJwtDemonstratingProofOfPossessionAsync = (w) => (demonstratingProofOfPossessionConfiguration) => async (jwk, method = "POST", url, extrasClaims = {}) => {
|
|
398
|
-
const claims = {
|
|
399
|
-
// https://www.rfc-editor.org/rfc/rfc9449.html#name-concept
|
|
400
|
-
jti: btoa(guid()),
|
|
401
|
-
htm: method,
|
|
402
|
-
htu: url,
|
|
403
|
-
iat: Math.round(Date.now() / 1e3),
|
|
404
|
-
...extrasClaims
|
|
405
|
-
};
|
|
406
|
-
const kid = await JWK.thumbprint(w)(jwk, demonstratingProofOfPossessionConfiguration.digestAlgorithm);
|
|
407
|
-
const jwt = await JWT.sign(w)(jwk, { kid }, claims, demonstratingProofOfPossessionConfiguration);
|
|
408
|
-
return jwt;
|
|
409
|
-
};
|
|
410
|
-
const guid = () => {
|
|
411
|
-
const guidHolder = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx";
|
|
412
|
-
const hex = "0123456789abcdef";
|
|
413
|
-
let r = 0;
|
|
414
|
-
let guidResponse = "";
|
|
415
|
-
for (let i = 0; i < 36; i++) {
|
|
416
|
-
if (guidHolder[i] !== "-" && guidHolder[i] !== "4") {
|
|
417
|
-
r = Math.random() * 16 | 0;
|
|
418
|
-
}
|
|
419
|
-
if (guidHolder[i] === "x") {
|
|
420
|
-
guidResponse += hex[r];
|
|
421
|
-
} else if (guidHolder[i] === "y") {
|
|
422
|
-
r &= 3;
|
|
423
|
-
r |= 8;
|
|
424
|
-
guidResponse += hex[r];
|
|
425
|
-
} else {
|
|
426
|
-
guidResponse += guidHolder[i];
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
return guidResponse;
|
|
430
|
-
};
|
|
431
|
-
const isDpop = (trustedDomain) => {
|
|
432
|
-
if (Array.isArray(trustedDomain)) {
|
|
433
|
-
return false;
|
|
434
|
-
}
|
|
435
|
-
return trustedDomain.demonstratingProofOfPossession ?? false;
|
|
436
|
-
};
|
|
437
|
-
const getDpopConfiguration = (trustedDomain) => {
|
|
438
|
-
if (!isDpop(trustedDomain)) {
|
|
439
|
-
return null;
|
|
440
|
-
}
|
|
441
|
-
if (Array.isArray(trustedDomain)) {
|
|
442
|
-
return null;
|
|
443
|
-
}
|
|
444
|
-
return trustedDomain.demonstratingProofOfPossessionConfiguration ?? defaultDemonstratingProofOfPossessionConfiguration;
|
|
445
|
-
};
|
|
446
|
-
const getDpopOnlyWhenDpopHeaderPresent = (trustedDomain) => {
|
|
447
|
-
if (!isDpop(trustedDomain)) {
|
|
448
|
-
return null;
|
|
449
|
-
}
|
|
450
|
-
if (Array.isArray(trustedDomain)) {
|
|
451
|
-
return null;
|
|
452
|
-
}
|
|
453
|
-
return trustedDomain.demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent ?? true;
|
|
454
|
-
};
|
|
455
|
-
function textEncodeLite(str) {
|
|
456
|
-
const buf = new ArrayBuffer(str.length);
|
|
457
|
-
const bufView = new Uint8Array(buf);
|
|
458
|
-
for (let i = 0; i < str.length; i++) {
|
|
459
|
-
bufView[i] = str.charCodeAt(i);
|
|
460
|
-
}
|
|
461
|
-
return bufView;
|
|
462
|
-
}
|
|
463
|
-
function base64urlOfHashOfASCIIEncodingAsync(code) {
|
|
464
|
-
return new Promise((resolve, reject) => {
|
|
465
|
-
crypto.subtle.digest("SHA-256", textEncodeLite(code)).then((buffer) => {
|
|
466
|
-
return resolve(uint8ToUrlBase64(new Uint8Array(buffer)));
|
|
467
|
-
}, (error) => reject(error));
|
|
468
|
-
});
|
|
469
|
-
}
|
|
470
|
-
const getMatchingOidcConfigurations = (database2, url) => {
|
|
471
|
-
return Object.values(database2).filter((config) => {
|
|
472
|
-
const { oidcServerConfiguration } = config || {};
|
|
473
|
-
const { tokenEndpoint, revocationEndpoint } = oidcServerConfiguration || {};
|
|
474
|
-
const normalizedUrl = normalizeUrl(url);
|
|
475
|
-
return tokenEndpoint && normalizedUrl.startsWith(normalizeUrl(tokenEndpoint)) || revocationEndpoint && normalizedUrl.startsWith(normalizeUrl(revocationEndpoint));
|
|
476
|
-
});
|
|
477
|
-
};
|
|
500
|
+
const version = "7.22.19";
|
|
478
501
|
if (typeof trustedTypes !== "undefined" && typeof trustedTypes.createPolicy == "function") {
|
|
479
502
|
trustedTypes.createPolicy("default", {
|
|
480
503
|
createScriptURL: function(url) {
|
|
@@ -520,7 +543,9 @@ async function generateDpopAsync(originalRequest, currentDatabase, url, extrasCl
|
|
|
520
543
|
if ((currentDatabase == null ? void 0 : currentDatabase.demonstratingProofOfPossessionConfiguration) && currentDatabase.demonstratingProofOfPossessionJwkJson && (!currentDatabase.demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent || currentDatabase.demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent && headersExtras["dpop"])) {
|
|
521
544
|
const dpopConfiguration = currentDatabase.demonstratingProofOfPossessionConfiguration;
|
|
522
545
|
const jwk = currentDatabase.demonstratingProofOfPossessionJwkJson;
|
|
523
|
-
headersExtras["dpop"] = await generateJwtDemonstratingProofOfPossessionAsync(self)(
|
|
546
|
+
headersExtras["dpop"] = await generateJwtDemonstratingProofOfPossessionAsync(self)(
|
|
547
|
+
dpopConfiguration
|
|
548
|
+
)(jwk, "POST", url, extrasClaims);
|
|
524
549
|
if (currentDatabase.demonstratingProofOfPossessionNonce != null) {
|
|
525
550
|
headersExtras["nonce"] = currentDatabase.demonstratingProofOfPossessionNonce;
|
|
526
551
|
}
|
|
@@ -598,7 +623,9 @@ const handleFetch = async (event) => {
|
|
|
598
623
|
const currentDb = currentDatabases[i];
|
|
599
624
|
const currentDbTabs = Object.keys(currentDb.state);
|
|
600
625
|
if ((currentDb == null ? void 0 : currentDb.tokens) != null) {
|
|
601
|
-
const claimsExtras = {
|
|
626
|
+
const claimsExtras = {
|
|
627
|
+
ath: await base64urlOfHashOfASCIIEncodingAsync(currentDb.tokens.access_token)
|
|
628
|
+
};
|
|
602
629
|
headers = await generateDpopAsync(originalRequest, currentDb, url, claimsExtras);
|
|
603
630
|
for (let j = 0; j < currentDbTabs.length; j++) {
|
|
604
631
|
const keyRefreshToken = TOKEN.REFRESH_TOKEN + "_" + currentDb.configurationName + "_" + currentDbTabs[j];
|
|
@@ -640,29 +667,22 @@ const handleFetch = async (event) => {
|
|
|
640
667
|
credentials: clonedRequest.credentials,
|
|
641
668
|
integrity: clonedRequest.integrity
|
|
642
669
|
});
|
|
643
|
-
if (((_a2 = currentDatabase == null ? void 0 : currentDatabase.oidcServerConfiguration) == null ? void 0 : _a2.revocationEndpoint) && url.startsWith(
|
|
644
|
-
normalizeUrl(
|
|
645
|
-
currentDatabase.oidcServerConfiguration.revocationEndpoint
|
|
646
|
-
)
|
|
647
|
-
)) {
|
|
670
|
+
if (((_a2 = currentDatabase == null ? void 0 : currentDatabase.oidcServerConfiguration) == null ? void 0 : _a2.revocationEndpoint) && url.startsWith(normalizeUrl(currentDatabase.oidcServerConfiguration.revocationEndpoint))) {
|
|
648
671
|
return fetchPromise.then(async (response2) => {
|
|
649
672
|
const text = await response2.text();
|
|
650
673
|
return new Response(text, response2);
|
|
651
674
|
});
|
|
652
675
|
}
|
|
653
|
-
return fetchPromise.then(
|
|
676
|
+
return fetchPromise.then(
|
|
677
|
+
hideTokens(currentDatabase, currentTabId)
|
|
678
|
+
);
|
|
654
679
|
} else if (actualBody.includes("code_verifier=") && extractConfigurationNameFromCodeVerifier(actualBody) != null) {
|
|
655
|
-
const [currentLoginCallbackConfigurationName, currentLoginCallbackTabId] = extractConfigurationNameFromCodeVerifier(
|
|
656
|
-
actualBody
|
|
657
|
-
) ?? [];
|
|
680
|
+
const [currentLoginCallbackConfigurationName, currentLoginCallbackTabId] = extractConfigurationNameFromCodeVerifier(actualBody) ?? [];
|
|
658
681
|
currentDatabase = database[currentLoginCallbackConfigurationName];
|
|
659
682
|
let newBody = actualBody;
|
|
660
683
|
const codeVerifier = currentDatabase.codeVerifier[currentLoginCallbackTabId];
|
|
661
684
|
if (codeVerifier != null) {
|
|
662
|
-
newBody = replaceCodeVerifier(
|
|
663
|
-
newBody,
|
|
664
|
-
codeVerifier
|
|
665
|
-
);
|
|
685
|
+
newBody = replaceCodeVerifier(newBody, codeVerifier);
|
|
666
686
|
}
|
|
667
687
|
const headersExtras = await generateDpopAsync(originalRequest, currentDatabase, url);
|
|
668
688
|
return fetch(originalRequest, {
|
|
@@ -774,13 +794,19 @@ const handleMessage = async (event) => {
|
|
|
774
794
|
currentDatabase.oidcServerConfiguration = oidcServerConfiguration;
|
|
775
795
|
currentDatabase.oidcConfiguration = data.data.oidcConfiguration;
|
|
776
796
|
if (currentDatabase.demonstratingProofOfPossessionConfiguration == null) {
|
|
777
|
-
const demonstratingProofOfPossessionConfiguration = getDpopConfiguration(
|
|
797
|
+
const demonstratingProofOfPossessionConfiguration = getDpopConfiguration(
|
|
798
|
+
trustedDomains[configurationName]
|
|
799
|
+
);
|
|
778
800
|
if (demonstratingProofOfPossessionConfiguration != null) {
|
|
779
801
|
if (currentDatabase.oidcConfiguration.demonstrating_proof_of_possession) {
|
|
780
|
-
console.warn(
|
|
802
|
+
console.warn(
|
|
803
|
+
"In service worker, demonstrating_proof_of_possession must be configured from trustedDomains file"
|
|
804
|
+
);
|
|
781
805
|
}
|
|
782
806
|
currentDatabase.demonstratingProofOfPossessionConfiguration = demonstratingProofOfPossessionConfiguration;
|
|
783
|
-
currentDatabase.demonstratingProofOfPossessionJwkJson = await generateJwkAsync(self)(
|
|
807
|
+
currentDatabase.demonstratingProofOfPossessionJwkJson = await generateJwkAsync(self)(
|
|
808
|
+
demonstratingProofOfPossessionConfiguration.generateKeyAlgorithm
|
|
809
|
+
);
|
|
784
810
|
currentDatabase.demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent = getDpopOnlyWhenDpopHeaderPresent(trustedDomains[configurationName]) ?? false;
|
|
785
811
|
}
|
|
786
812
|
}
|