@axa-fr/oidc-client-service-worker 7.27.0 → 7.27.2
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 +25 -3
- package/dist/OidcServiceWorker.js.map +1 -1
- package/dist/src/utils/__tests__/waitForValidTokens.spec.d.ts +2 -0
- package/dist/src/utils/__tests__/waitForValidTokens.spec.d.ts.map +1 -0
- package/dist/src/utils/index.d.ts +1 -0
- package/dist/src/utils/index.d.ts.map +1 -1
- package/dist/src/utils/waitForValidTokens.d.ts +11 -0
- package/dist/src/utils/waitForValidTokens.d.ts.map +1 -0
- package/dist/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/OidcServiceWorker.ts +27 -36
- package/src/utils/__tests__/waitForValidTokens.spec.ts +88 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/waitForValidTokens.ts +38 -0
- package/src/version.ts +1 -1
|
@@ -467,6 +467,27 @@ function hideTokens(currentDatabaseElement) {
|
|
|
467
467
|
});
|
|
468
468
|
};
|
|
469
469
|
}
|
|
470
|
+
const TOKEN_RENEWAL_TIMEOUT_MS = 5e3;
|
|
471
|
+
const TOKEN_RENEWAL_POLL_INTERVAL_MS = 200;
|
|
472
|
+
async function waitForValidTokens(config, maxWaitMs = TOKEN_RENEWAL_TIMEOUT_MS, pollIntervalMs = TOKEN_RENEWAL_POLL_INTERVAL_MS) {
|
|
473
|
+
const startTime = Date.now();
|
|
474
|
+
while (config.tokens && !isTokensValid(config.tokens)) {
|
|
475
|
+
if (Date.now() - startTime >= maxWaitMs) {
|
|
476
|
+
return new Response(null, {
|
|
477
|
+
status: 401,
|
|
478
|
+
statusText: "Token expired - service worker renewal timeout"
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
await sleep(pollIntervalMs);
|
|
482
|
+
}
|
|
483
|
+
if (!config.tokens?.access_token) {
|
|
484
|
+
return new Response(null, {
|
|
485
|
+
status: 401,
|
|
486
|
+
statusText: "Missing access token"
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
return null;
|
|
490
|
+
}
|
|
470
491
|
const getMatchingOidcConfigurations = (database2, url) => {
|
|
471
492
|
return Object.values(database2).filter((config) => {
|
|
472
493
|
const { oidcServerConfiguration } = config || {};
|
|
@@ -488,7 +509,7 @@ const extractConfigurationNameFromCodeVerifier = (chaine) => {
|
|
|
488
509
|
return "";
|
|
489
510
|
}
|
|
490
511
|
};
|
|
491
|
-
const version = "7.27.
|
|
512
|
+
const version = "7.27.2";
|
|
492
513
|
if (typeof trustedTypes !== "undefined" && typeof trustedTypes.createPolicy === "function") {
|
|
493
514
|
trustedTypes.createPolicy("default", {
|
|
494
515
|
createScriptURL: function(url) {
|
|
@@ -573,8 +594,9 @@ const handleFetch = (event) => {
|
|
|
573
594
|
(c) => c.configurationName.endsWith(key)
|
|
574
595
|
);
|
|
575
596
|
if (currentDatabaseForRequestAccessToken?.tokens?.access_token) {
|
|
576
|
-
|
|
577
|
-
|
|
597
|
+
const tokenError = await waitForValidTokens(currentDatabaseForRequestAccessToken);
|
|
598
|
+
if (tokenError) {
|
|
599
|
+
return tokenError;
|
|
578
600
|
}
|
|
579
601
|
let requestMode = originalRequest.mode;
|
|
580
602
|
if (originalRequest.mode !== "navigate" && currentDatabaseForRequestAccessToken.convertAllRequestsToCorsExceptNavigate) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OidcServiceWorker.js","sources":["../src/constants.ts","../src/jwt.ts","../src/crypto.ts","../src/dpop.ts","../src/utils/normalizeUrl.ts","../src/utils/domains.ts","../src/utils/serializeHeaders.ts","../src/utils/sleep.ts","../src/utils/strings.ts","../src/utils/tokens.ts","../src/oidcConfig.ts","../src/utils/codeVerifier.ts","../src/version.ts","../src/OidcServiceWorker.ts"],"sourcesContent":["const scriptFilename = 'OidcTrustedDomains.js';\nconst acceptAnyDomainToken = '*';\n\ntype TokenType = {\n readonly REFRESH_TOKEN: string;\n readonly ACCESS_TOKEN: string;\n readonly NONCE_TOKEN: string;\n readonly CODE_VERIFIER: string;\n};\n\nconst TOKEN: TokenType = {\n REFRESH_TOKEN: 'REFRESH_TOKEN_SECURED_BY_OIDC_SERVICE_WORKER',\n ACCESS_TOKEN: 'ACCESS_TOKEN_SECURED_BY_OIDC_SERVICE_WORKER',\n NONCE_TOKEN: 'NONCE_SECURED_BY_OIDC_SERVICE_WORKER',\n CODE_VERIFIER: 'CODE_VERIFIER_SECURED_BY_OIDC_SERVICE_WORKER',\n};\n\ntype TokenRenewModeType = {\n readonly access_token_or_id_token_invalid: string;\n readonly access_token_invalid: string;\n readonly id_token_invalid: string;\n};\n\nconst TokenRenewMode: TokenRenewModeType = {\n access_token_or_id_token_invalid: 'access_token_or_id_token_invalid',\n access_token_invalid: 'access_token_invalid',\n id_token_invalid: 'id_token_invalid',\n};\n\nconst openidWellknownUrlEndWith = '/.well-known/openid-configuration';\n\nexport { acceptAnyDomainToken, openidWellknownUrlEndWith, scriptFilename, TOKEN, TokenRenewMode };\n","// code base on https://coolaj86.com/articles/sign-jwt-webcrypto-vanilla-js/\n\n// String (UCS-2) to Uint8Array\n//\n// because... JavaScript, Strings, and Buffers\n// @ts-ignore\nimport { DemonstratingProofOfPossessionConfiguration } from './types';\n\nfunction strToUint8(str: string) {\n return new TextEncoder().encode(str);\n}\n\n// Binary String to URL-Safe Base64\n//\n// btoa (Binary-to-Ascii) means \"binary string\" to base64\n// @ts-ignore\nfunction binToUrlBase64(bin) {\n return btoa(bin).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+/g, '');\n}\n\n// UTF-8 to Binary String\n//\n// Because JavaScript has a strange relationship with strings\n// https://coolaj86.com/articles/base64-unicode-utf-8-javascript-and-you/\n// @ts-ignore\nfunction utf8ToBinaryString(str) {\n const escstr = encodeURIComponent(str);\n // replaces any uri escape sequence, such as %0A,\n // with binary escape, such as 0x0A\n // @ts-ignore\n return escstr.replace(/%([0-9A-F]{2})/g, function (match: string, p1) {\n return String.fromCharCode(parseInt(p1, 16));\n });\n}\n\n// Uint8Array to URL Safe Base64\n//\n// the shortest distant between two encodings... binary string\n// @ts-ignore\nexport const uint8ToUrlBase64 = (uint8: Uint8Array) => {\n let bin = '';\n // @ts-ignore\n uint8.forEach(function (code) {\n bin += String.fromCharCode(code);\n });\n return binToUrlBase64(bin);\n};\n\n// UCS-2 String to URL-Safe Base64\n//\n// btoa doesn't work on UTF-8 strings\n// @ts-ignore\nfunction strToUrlBase64(str) {\n return binToUrlBase64(utf8ToBinaryString(str));\n}\n\nexport const defaultDemonstratingProofOfPossessionConfiguration: DemonstratingProofOfPossessionConfiguration =\n {\n importKeyAlgorithm: {\n name: 'ECDSA',\n namedCurve: 'P-256',\n hash: { name: 'ES256' },\n },\n signAlgorithm: { name: 'ECDSA', hash: { name: 'SHA-256' } },\n generateKeyAlgorithm: {\n name: 'ECDSA',\n namedCurve: 'P-256',\n },\n digestAlgorithm: { name: 'SHA-256' },\n jwtHeaderAlgorithm: 'ES256',\n };\n\n// @ts-ignore\nconst sign =\n (w: any) =>\n async (\n jwk: any,\n headers: any,\n claims: any,\n demonstratingProofOfPossessionConfiguration: DemonstratingProofOfPossessionConfiguration,\n jwtHeaderType = 'dpop+jwt',\n ) => {\n // Make a shallow copy of the key\n // (to set ext if it wasn't already set)\n jwk = Object.assign({}, jwk);\n\n // The headers should probably be empty\n headers.typ = jwtHeaderType;\n headers.alg = demonstratingProofOfPossessionConfiguration.jwtHeaderAlgorithm;\n switch (headers.alg) {\n case 'ES256': //if (!headers.kid) {\n // alternate: see thumbprint function below\n headers.jwk = { kty: jwk.kty, crv: jwk.crv, x: jwk.x, y: jwk.y };\n //}\n break;\n case 'RS256':\n headers.jwk = { kty: jwk.kty, n: jwk.n, e: jwk.e, kid: headers.kid };\n break;\n default:\n throw new Error('Unknown or not implemented JWS algorithm');\n }\n\n const jws = {\n // @ts-ignore\n // JWT \"headers\" really means JWS \"protected headers\"\n protected: strToUrlBase64(JSON.stringify(headers)),\n // @ts-ignore\n // JWT \"claims\" are really a JSON-defined JWS \"payload\"\n payload: strToUrlBase64(JSON.stringify(claims)),\n };\n\n // To import as EC (ECDSA, P-256, SHA-256, ES256)\n const keyType = demonstratingProofOfPossessionConfiguration.importKeyAlgorithm;\n\n // To make re-exportable as JSON (or DER/PEM)\n const exportable = true;\n\n // Import as a private key that isn't black-listed from signing\n const privileges = ['sign'];\n\n // Actually do the import, which comes out as an abstract key type\n // @ts-ignore\n const privateKey = await w.crypto.subtle.importKey('jwk', jwk, keyType, exportable, privileges);\n // Convert UTF-8 to Uint8Array ArrayBuffer\n // @ts-ignore\n const data = strToUint8(`${jws.protected}.${jws.payload}`);\n\n // The signature and hash should match the bit-entropy of the key\n // https://tools.ietf.org/html/rfc7518#section-3\n const signatureType = demonstratingProofOfPossessionConfiguration.signAlgorithm;\n\n const signature = await w.crypto.subtle.sign(signatureType, privateKey, data);\n // returns an ArrayBuffer containing a JOSE (not X509) signature,\n // which must be converted to Uint8 to be useful\n // @ts-ignore\n jws.signature = uint8ToUrlBase64(new Uint8Array(signature));\n // JWT is just a \"compressed\", \"protected\" JWS\n // @ts-ignore\n return `${jws.protected}.${jws.payload}.${jws.signature}`;\n };\n\nexport const JWT = { sign };\n\n// @ts-ignore\nconst generate =\n (w: any) => async (generateKeyAlgorithm: RsaHashedKeyGenParams | EcKeyGenParams) => {\n const keyType = generateKeyAlgorithm;\n const exportable = true;\n const privileges = ['sign', 'verify'];\n // @ts-ignore\n const key = await w.crypto.subtle.generateKey(keyType, exportable, privileges);\n // returns an abstract and opaque WebCrypto object,\n // which in most cases you'll want to export as JSON to be able to save\n return await w.crypto.subtle.exportKey('jwk', key.privateKey);\n };\n\n// Create a Public Key from a Private Key\n//\n// chops off the private parts\n// @ts-ignore\nconst neuter = jwk => {\n const copy = Object.assign({}, jwk);\n delete copy.d;\n copy.key_ops = ['verify'];\n return copy;\n};\n\nconst EC = {\n generate,\n neuter,\n};\n// @ts-ignore\nconst thumbprint = (w: any) => async (jwk, digestAlgorithm: AlgorithmIdentifier) => {\n let sortedPub;\n // lexigraphically sorted, no spaces\n switch (jwk.kty) {\n case 'EC':\n sortedPub = '{\"crv\":\"CRV\",\"kty\":\"EC\",\"x\":\"X\",\"y\":\"Y\"}'\n .replace('CRV', jwk.crv)\n .replace('X', jwk.x)\n .replace('Y', jwk.y);\n break;\n case 'RSA':\n sortedPub = '{\"e\":\"E\",\"kty\":\"RSA\",\"n\":\"N\"}'.replace('E', jwk.e).replace('N', jwk.n);\n break;\n default:\n throw new Error('Unknown or not implemented JWK type');\n }\n // The hash should match the size of the key,\n // but we're only dealing with P-256\n const hash = await w.crypto.subtle.digest(digestAlgorithm, strToUint8(sortedPub));\n return uint8ToUrlBase64(new Uint8Array(hash));\n};\n\nexport const JWK = { thumbprint };\n\nexport const generateJwkAsync =\n (w: any) => async (generateKeyAlgorithm: RsaHashedKeyGenParams | EcKeyGenParams) => {\n // @ts-ignore\n const jwk = await EC.generate(w)(generateKeyAlgorithm);\n // console.info('Private Key:', JSON.stringify(jwk));\n // @ts-ignore\n // console.info('Public Key:', JSON.stringify(EC.neuter(jwk)));\n return jwk;\n };\n\nexport const generateJwtDemonstratingProofOfPossessionAsync =\n (w: any) =>\n (demonstratingProofOfPossessionConfiguration: DemonstratingProofOfPossessionConfiguration) =>\n async (jwk: any, method = 'POST', url: string, extrasClaims = {}) => {\n const claims = {\n // https://www.rfc-editor.org/rfc/rfc9449.html#name-concept\n jti: btoa(guid()),\n htm: method,\n htu: url,\n iat: Math.round(Date.now() / 1000),\n ...extrasClaims,\n };\n // @ts-ignore\n const kid = await JWK.thumbprint(w)(\n jwk,\n demonstratingProofOfPossessionConfiguration.digestAlgorithm,\n );\n // @ts-ignore\n const jwt = await JWT.sign(w)(\n jwk,\n { kid: kid },\n claims,\n demonstratingProofOfPossessionConfiguration,\n );\n // console.info('JWT:', jwt);\n return jwt;\n };\n\nconst guid = () => {\n // RFC4122: The version 4 UUID is meant for generating UUIDs from truly-random or\n // pseudo-random numbers.\n // The algorithm is as follows:\n // Set the two most significant bits (bits 6 and 7) of the\n // clock_seq_hi_and_reserved to zero and one, respectively.\n // Set the four most significant bits (bits 12 through 15) of the\n // time_hi_and_version field to the 4-bit version number from\n // Section 4.1.3. Version4\n // Set all the other bits to randomly (or pseudo-randomly) chosen\n // values.\n // UUID = time-low \"-\" time-mid \"-\"time-high-and-version \"-\"clock-seq-reserved and low(2hexOctet)\"-\" node\n // time-low = 4hexOctet\n // time-mid = 2hexOctet\n // time-high-and-version = 2hexOctet\n // clock-seq-and-reserved = hexOctet:\n // clock-seq-low = hexOctet\n // node = 6hexOctet\n // Format: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\n // y could be 1000, 1001, 1010, 1011 since most significant two bits needs to be 10\n // y values are 8, 9, A, B\n const guidHolder = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';\n const hex = '0123456789abcdef';\n let r = 0;\n let guidResponse = '';\n for (let i = 0; i < 36; i++) {\n if (guidHolder[i] !== '-' && guidHolder[i] !== '4') {\n // each x and y needs to be random\n r = (Math.random() * 16) | 0;\n }\n\n if (guidHolder[i] === 'x') {\n guidResponse += hex[r];\n } else if (guidHolder[i] === 'y') {\n // clock-seq-and-reserved first hex is filtered and remaining hex values are random\n r &= 0x3; // bit and with 0011 to set pos 2 to zero ?0??\n r |= 0x8; // set pos 3 to 1 as 1???\n guidResponse += hex[r];\n } else {\n guidResponse += guidHolder[i];\n }\n }\n\n return guidResponse;\n};\n","import { uint8ToUrlBase64 } from './jwt';\n\nexport function textEncodeLite(str: string) {\n const buf = new ArrayBuffer(str.length);\n const bufView = new Uint8Array(buf);\n\n for (let i = 0; i < str.length; i++) {\n bufView[i] = str.charCodeAt(i);\n }\n return bufView;\n}\n\nexport function base64urlOfHashOfASCIIEncodingAsync(code: string): Promise<string> {\n return new Promise((resolve, reject) => {\n crypto.subtle.digest('SHA-256', textEncodeLite(code)).then(\n buffer => {\n return resolve(uint8ToUrlBase64(new Uint8Array(buffer)));\n },\n error => reject(error),\n );\n });\n}\n","import { defaultDemonstratingProofOfPossessionConfiguration } from './jwt';\nimport { Domain, DomainDetails } from './types.js';\n\nconst isDpop = (trustedDomain: Domain[] | DomainDetails): boolean => {\n if (Array.isArray(trustedDomain)) {\n return false;\n }\n return trustedDomain.demonstratingProofOfPossession ?? false;\n};\n\nexport const getDpopConfiguration = (trustedDomain: Domain[] | DomainDetails) => {\n if (!isDpop(trustedDomain)) {\n return null;\n }\n\n if (Array.isArray(trustedDomain)) {\n return null;\n }\n\n return (\n trustedDomain.demonstratingProofOfPossessionConfiguration ??\n defaultDemonstratingProofOfPossessionConfiguration\n );\n};\n\nexport const getDpopOnlyWhenDpopHeaderPresent = (trustedDomain: Domain[] | DomainDetails) => {\n if (!isDpop(trustedDomain)) {\n return null;\n }\n\n if (Array.isArray(trustedDomain)) {\n return null;\n }\n\n return trustedDomain.demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent ?? true;\n};\n","export function normalizeUrl(url: string) {\n try {\n return new URL(url).toString();\n } catch (error) {\n console.error(`Failed to normalize url: ${url}`, error);\n return url;\n }\n}\n","import { acceptAnyDomainToken, openidWellknownUrlEndWith, scriptFilename } from '../constants';\nimport { Database, Domain, DomainDetails, OidcConfig, TrustedDomains } from '../types';\nimport { normalizeUrl } from './normalizeUrl';\n\nexport function checkDomain(domains: Domain[], endpoint: string) {\n if (!endpoint) {\n return;\n }\n\n const domain = domains.find(domain => {\n let testable: RegExp;\n\n if (typeof domain === 'string') {\n testable = new RegExp(`^${domain}`);\n } else {\n testable = domain;\n }\n\n return testable.test?.(endpoint);\n });\n if (!domain) {\n throw new Error(\n 'Domain ' + endpoint + ' is not trusted, please add domain in ' + scriptFilename,\n );\n }\n}\n\nexport const getDomains = (\n trustedDomain: Domain[] | DomainDetails,\n type: 'oidc' | 'accessToken',\n) => {\n if (Array.isArray(trustedDomain)) {\n return trustedDomain;\n }\n\n return trustedDomain[`${type}Domains`] ?? trustedDomain.domains ?? [];\n};\n\nexport const getCurrentDatabaseDomain = (\n database: Database,\n url: string,\n trustedDomains: TrustedDomains,\n) => {\n if (url.endsWith(openidWellknownUrlEndWith)) {\n return null;\n }\n const datatases = [];\n for (const [key, currentDatabase] of Object.entries<OidcConfig>(database)) {\n const oidcServerConfiguration = currentDatabase.oidcServerConfiguration;\n\n if (!oidcServerConfiguration) {\n continue;\n }\n\n if (\n oidcServerConfiguration.tokenEndpoint &&\n url === normalizeUrl(oidcServerConfiguration.tokenEndpoint)\n ) {\n continue;\n }\n if (\n oidcServerConfiguration.revocationEndpoint &&\n url === normalizeUrl(oidcServerConfiguration.revocationEndpoint)\n ) {\n continue;\n }\n const trustedDomain = trustedDomains == null ? [] : trustedDomains[key.split('#')[0]];\n\n const domains = getDomains(trustedDomain, 'accessToken');\n const domainsToSendTokens = oidcServerConfiguration.userInfoEndpoint\n ? [normalizeUrl(oidcServerConfiguration.userInfoEndpoint), ...domains]\n : [...domains];\n\n let hasToSendToken = false;\n if (domainsToSendTokens.find(f => f === acceptAnyDomainToken)) {\n hasToSendToken = true;\n } else {\n for (let i = 0; i < domainsToSendTokens.length; i++) {\n let domain = domainsToSendTokens[i];\n\n if (typeof domain === 'string') {\n domain = new RegExp(`^${domain}`);\n }\n\n if (domain.test?.(url)) {\n hasToSendToken = true;\n break;\n }\n }\n }\n\n if (hasToSendToken) {\n if (currentDatabase.tokens) {\n datatases.push(currentDatabase);\n }\n }\n }\n return datatases;\n};\n","import { FetchHeaders } from '../types';\n\nfunction serializeHeaders(headers: Headers) {\n const headersObj: Record<string, string> = {};\n for (const key of (headers as FetchHeaders).keys()) {\n if (headers.has(key)) {\n headersObj[key] = headers.get(key) as string;\n }\n }\n return headersObj;\n}\nexport { serializeHeaders };\n","const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));\nexport { sleep };\n","/**\n * Count occurances of letter in string\n * @param str\n * @param find\n * @returns\n */\nexport function countLetter(str: string, find: string) {\n return str.split(find).length - 1;\n}\n","/* eslint-disable simple-import-sort/exports */\nimport { TOKEN, TokenRenewMode } from '../constants';\nimport {\n AccessTokenPayload,\n IdTokenPayload,\n OidcConfig,\n OidcConfiguration,\n OidcServerConfiguration,\n Tokens,\n} from '../types';\nimport { countLetter } from './strings';\n\nexport const parseJwt = (payload: string) => {\n return JSON.parse(b64DecodeUnicode(payload.replaceAll(/-/g, '+').replaceAll(/_/g, '/')));\n};\nfunction b64DecodeUnicode(str: string) {\n return decodeURIComponent(\n Array.prototype.map\n .call(atob(str), c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))\n .join(''),\n );\n}\n\nfunction computeTimeLeft(refreshTimeBeforeTokensExpirationInSecond: number, expiresAt: number) {\n const currentTimeUnixSecond = new Date().getTime() / 1000;\n return Math.round(expiresAt - refreshTimeBeforeTokensExpirationInSecond - currentTimeUnixSecond);\n}\n\nfunction isTokensValid(tokens: Tokens | null) {\n if (!tokens) {\n return false;\n }\n return computeTimeLeft(0, tokens.expiresAt) > 0;\n}\n\nconst extractTokenPayload = (token?: string) => {\n try {\n if (!token) {\n return null;\n }\n if (countLetter(token, '.') === 2) {\n return parseJwt(token.split('.')[1]);\n } else {\n return null;\n }\n } catch (e) {\n console.warn(e);\n }\n return null;\n};\n\n// https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation (excluding rules #1, #4, #5, #7, #8, #12, and #13 which did not apply).\n// https://github.com/openid/AppAuth-JS/issues/65\nconst isTokensOidcValid = (\n tokens: Tokens,\n nonce: string | null,\n oidcServerConfiguration: OidcServerConfiguration,\n): { isValid: boolean; reason: string } => {\n if (tokens.idTokenPayload) {\n const idTokenPayload = tokens.idTokenPayload;\n // 2: The Issuer Identifier for the OpenID Provider (which is typically obtained during Discovery) MUST exactly match the value of the iss (issuer) Claim.\n if (idTokenPayload && oidcServerConfiguration.issuer !== idTokenPayload.iss) {\n return {\n isValid: false,\n reason: `Issuer does not match (oidcServerConfiguration issuer) ${oidcServerConfiguration.issuer} !== (idTokenPayload issuer) ${idTokenPayload.iss}`,\n };\n }\n // 3: The Client MUST validate that the aud (audience) Claim contains its client_id value registered at the Issuer identified by the iss (issuer) Claim as an audience. The aud (audience) Claim MAY contain an array with more than one element. The ID Token MUST be rejected if the ID Token does not list the Client as a valid audience, or if it contains additional audiences not trusted by the Client.\n\n // 6: If the ID Token is received via direct communication between the Client and the Token Endpoint (which it is in this flow), the TLS server validation MAY be used to validate the issuer in place of checking the token signature. The Client MUST validate the signature of all other ID Tokens according to JWS [JWS] using the algorithm specified in the JWT alg Header Parameter. The Client MUST use the keys provided by the Issuer.\n\n // 9: The current time MUST be before the time represented by the exp Claim.\n const currentTimeUnixSecond = new Date().getTime() / 1000;\n if (idTokenPayload && idTokenPayload.exp && idTokenPayload.exp < currentTimeUnixSecond) {\n return {\n isValid: false,\n reason: `Token expired at (idTokenPayload exp) ${idTokenPayload.exp} < (currentTimeUnixSecond) ${currentTimeUnixSecond}`,\n };\n }\n // 10: The iat Claim can be used to reject tokens that were issued too far away from the current time, limiting the amount of time that nonces need to be stored to prevent attacks. The acceptable range is Client specific.\n const timeInSevenDays = 60 * 60 * 24 * 7;\n if (\n idTokenPayload &&\n idTokenPayload.iat &&\n idTokenPayload.iat + timeInSevenDays < currentTimeUnixSecond\n ) {\n return {\n isValid: false,\n reason: `Token is used from too long time (idTokenPayload iat + timeInSevenDays) ${idTokenPayload.iat + timeInSevenDays} < (currentTimeUnixSecond) ${currentTimeUnixSecond}`,\n };\n }\n // 11: If a nonce value was sent in the Authentication Request, a nonce Claim MUST be present and its value checked to verify that it is the same value as the one that was sent in the Authentication Request. The Client SHOULD check the nonce value for replay attacks. The precise method for detecting replay attacks is Client specific.\n if (idTokenPayload && nonce && idTokenPayload.nonce && idTokenPayload.nonce !== nonce) {\n return {\n isValid: false,\n reason: `Nonce does not match (nonce) ${nonce} !== (idTokenPayload nonce) ${idTokenPayload.nonce}`,\n };\n }\n }\n return { isValid: true, reason: '' };\n};\n\nfunction extractedIssueAt(\n tokens: Tokens,\n accessTokenPayload: AccessTokenPayload | null,\n _idTokenPayload: IdTokenPayload,\n) {\n if (!tokens.issued_at) {\n if (accessTokenPayload && accessTokenPayload.iat) {\n return accessTokenPayload.iat;\n } else if (_idTokenPayload && _idTokenPayload.iat) {\n return _idTokenPayload.iat;\n } else {\n const currentTimeUnixSecond = new Date().getTime() / 1000;\n return currentTimeUnixSecond;\n }\n } else if (typeof tokens.issued_at == 'string') {\n return parseInt(tokens.issued_at, 10);\n }\n return tokens.issued_at;\n}\n\nfunction _hideTokens(\n tokens: Tokens,\n currentDatabaseElement: OidcConfig,\n configurationName: string,\n) {\n if (!tokens.issued_at) {\n const currentTimeUnixSecond = new Date().getTime() / 1000;\n tokens.issued_at = currentTimeUnixSecond;\n } else if (typeof tokens.issued_at == 'string') {\n tokens.issued_at = parseInt(tokens.issued_at, 10);\n }\n\n const accessTokenPayload = extractTokenPayload(tokens.access_token);\n const secureTokens = {\n ...tokens,\n accessTokenPayload,\n };\n if (currentDatabaseElement.hideAccessToken) {\n secureTokens.access_token = `${TOKEN.ACCESS_TOKEN}_${configurationName}`;\n }\n tokens.accessTokenPayload = accessTokenPayload;\n\n // When id_token is not rotated we reuse old id_token\n const oldTokens = currentDatabaseElement.tokens;\n let id_token: string | null;\n if (oldTokens != null && 'id_token' in oldTokens && !('id_token' in tokens)) {\n id_token = oldTokens.id_token;\n } else {\n id_token = tokens.id_token;\n }\n tokens.id_token = id_token;\n\n let _idTokenPayload = null;\n if (id_token) {\n _idTokenPayload = extractTokenPayload(id_token);\n tokens.idTokenPayload = _idTokenPayload != null ? { ..._idTokenPayload } : null;\n if (_idTokenPayload && _idTokenPayload.nonce && currentDatabaseElement.nonce != null) {\n const keyNonce = `${TOKEN.NONCE_TOKEN}_${currentDatabaseElement.configurationName}`;\n _idTokenPayload.nonce = keyNonce;\n }\n secureTokens.idTokenPayload = _idTokenPayload;\n }\n if (tokens.refresh_token) {\n secureTokens.refresh_token = `${TOKEN.REFRESH_TOKEN}_${configurationName}`;\n }\n\n tokens.issued_at = extractedIssueAt(tokens, accessTokenPayload, _idTokenPayload);\n\n const expireIn =\n typeof tokens.expires_in == 'string' ? parseInt(tokens.expires_in, 10) : tokens.expires_in;\n\n const idTokenExpiresAt =\n _idTokenPayload && _idTokenPayload.exp ? _idTokenPayload.exp : Number.MAX_VALUE;\n const accessTokenExpiresAt =\n accessTokenPayload && accessTokenPayload.exp\n ? accessTokenPayload.exp\n : tokens.issued_at + expireIn;\n\n let expiresAt: number;\n const tokenRenewMode = (currentDatabaseElement.oidcConfiguration as OidcConfiguration)\n .token_renew_mode;\n if (tokenRenewMode === TokenRenewMode.access_token_invalid) {\n expiresAt = accessTokenExpiresAt;\n } else if (tokenRenewMode === TokenRenewMode.id_token_invalid) {\n expiresAt = idTokenExpiresAt;\n } else {\n expiresAt = idTokenExpiresAt < accessTokenExpiresAt ? idTokenExpiresAt : accessTokenExpiresAt;\n }\n secureTokens.expiresAt = expiresAt;\n\n tokens.expiresAt = expiresAt;\n const nonce = currentDatabaseElement.nonce ? currentDatabaseElement.nonce.nonce : null;\n const { isValid, reason } = isTokensOidcValid(\n tokens,\n nonce as string,\n currentDatabaseElement.oidcServerConfiguration as OidcServerConfiguration,\n ); // TODO: Type assertion, could be null.\n if (!isValid) {\n throw Error(`Tokens are not OpenID valid, reason: ${reason}`);\n }\n\n // When refresh_token is not rotated we reuse old refresh_token\n if (oldTokens != null && 'refresh_token' in oldTokens && !('refresh_token' in tokens)) {\n const refreshToken = oldTokens.refresh_token;\n\n currentDatabaseElement.tokens = {\n ...tokens,\n refresh_token: refreshToken,\n };\n } else {\n currentDatabaseElement.tokens = tokens;\n }\n\n currentDatabaseElement.status = 'LOGGED_IN';\n return secureTokens;\n}\n\nconst demonstratingProofOfPossessionNonceResponseHeader = 'DPoP-Nonce';\nfunction hideTokens(currentDatabaseElement: OidcConfig) {\n const configurationName = currentDatabaseElement.configurationName;\n return (response: Response) => {\n if (response.status !== 200) {\n return response;\n }\n const newHeaders = new Headers(response.headers);\n if (response.headers.has(demonstratingProofOfPossessionNonceResponseHeader)) {\n currentDatabaseElement.demonstratingProofOfPossessionNonce = response.headers.get(\n demonstratingProofOfPossessionNonceResponseHeader,\n );\n newHeaders.delete(demonstratingProofOfPossessionNonceResponseHeader);\n }\n\n return response.json().then<Response>((tokens: Tokens) => {\n const secureTokens = _hideTokens(tokens, currentDatabaseElement, configurationName);\n const body = JSON.stringify(secureTokens);\n return new Response(body, {\n status: response.status,\n statusText: response.statusText,\n headers: newHeaders,\n });\n });\n };\n}\n\nexport {\n b64DecodeUnicode,\n computeTimeLeft,\n isTokensValid,\n extractTokenPayload,\n isTokensOidcValid,\n hideTokens,\n _hideTokens,\n};\n","import { Database, OidcConfig } from './types';\nimport { normalizeUrl } from './utils';\n\nconst getMatchingOidcConfigurations = (database: Database, url: string): OidcConfig[] => {\n return Object.values(database).filter(config => {\n const { oidcServerConfiguration } = config || {};\n const { tokenEndpoint, revocationEndpoint } = oidcServerConfiguration || {};\n\n const normalizedUrl = normalizeUrl(url);\n return (\n (tokenEndpoint && normalizedUrl.startsWith(normalizeUrl(tokenEndpoint))) ||\n (revocationEndpoint && normalizedUrl.startsWith(normalizeUrl(revocationEndpoint)))\n );\n });\n};\n\nexport { getMatchingOidcConfigurations as getCurrentDatabasesTokenEndpoint };\n","export function replaceCodeVerifier(codeVerifier: string, newCodeVerifier: string): string {\n const regex = /[?&]code_verifier=([^&]+)/i;\n return codeVerifier.replace(regex, `&code_verifier=${newCodeVerifier}`);\n}\n\nexport const extractConfigurationNameFromCodeVerifier = (chaine: string): string => {\n const regex = /[?&]code_verifier=CODE_VERIFIER_SECURED_BY_OIDC_SERVICE_WORKER_([^&]+)/;\n const match = chaine.match(regex);\n\n if (match && match.length > 0) {\n return decodeURIComponent(match[1]);\n } else {\n return '';\n }\n};\n","export default '7.27.0';\n","import { acceptAnyDomainToken, scriptFilename, TOKEN } from './constants';\nimport { base64urlOfHashOfASCIIEncodingAsync } from './crypto';\nimport { getDpopConfiguration, getDpopOnlyWhenDpopHeaderPresent } from './dpop';\nimport { generateJwkAsync, generateJwtDemonstratingProofOfPossessionAsync } from './jwt';\nimport { getCurrentDatabasesTokenEndpoint } from './oidcConfig';\nimport { Database, MessageEventData, OidcConfig, TrustedDomains } from './types';\nimport {\n checkDomain,\n getCurrentDatabaseDomain,\n getDomains,\n hideTokens,\n isTokensValid,\n normalizeUrl,\n serializeHeaders,\n sleep,\n} from './utils';\nimport {\n extractConfigurationNameFromCodeVerifier,\n replaceCodeVerifier,\n} from './utils/codeVerifier';\nimport version from './version';\n\n// @ts-ignore\nif (typeof trustedTypes !== 'undefined' && typeof trustedTypes.createPolicy === 'function') {\n // @ts-ignore\n trustedTypes.createPolicy('default', {\n createScriptURL: function (url: string) {\n if (url === scriptFilename) {\n return url;\n } else {\n throw new Error('Untrusted script URL blocked: ' + url);\n }\n },\n });\n}\n\nconst _self = self as ServiceWorkerGlobalScope & typeof globalThis;\n\n// Déclare `trustedDomains` qui vient de l'extérieur :\ndeclare let trustedDomains: TrustedDomains;\n\n_self.importScripts(scriptFilename);\n\nconst id = Math.round(new Date().getTime() / 1000).toString();\nconsole.log('init service worker with id', id);\nconst keepAliveJsonFilename = 'OidcKeepAliveServiceWorker.json';\nconst database: Database = {};\n\n/**\n * Routine keepAlive : renvoie une réponse après un \"sleep\" éventuel.\n */\nconst keepAliveAsync = async (event: FetchEvent) => {\n const originalRequest = event.request;\n const isFromVanilla = originalRequest.headers.has('oidc-vanilla');\n const init = { status: 200, statusText: 'oidc-service-worker' };\n const response = new Response('{}', init);\n\n if (!isFromVanilla) {\n const originalRequestUrl = new URL(originalRequest.url);\n const minSleepSeconds = Number(originalRequestUrl.searchParams.get('minSleepSeconds')) || 240;\n for (let i = 0; i < minSleepSeconds; i++) {\n await sleep(1000 + Math.floor(Math.random() * 1000));\n const cache = await caches.open('oidc_dummy_cache');\n await cache.put(event.request, response.clone());\n }\n }\n return response;\n};\n\n/**\n * Génération d'en-têtes DPoP s'il y a configuration dpop.\n */\nasync function generateDpopAsync(\n originalRequest: Request,\n currentDatabase: OidcConfig | null,\n url: string,\n extrasClaims = {},\n) {\n const headersExtras = serializeHeaders(originalRequest.headers);\n if (\n currentDatabase?.demonstratingProofOfPossessionConfiguration &&\n currentDatabase.demonstratingProofOfPossessionJwkJson &&\n (!currentDatabase.demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent ||\n (currentDatabase.demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent &&\n headersExtras.dpop))\n ) {\n const dpopConfiguration = currentDatabase.demonstratingProofOfPossessionConfiguration;\n const jwk = currentDatabase.demonstratingProofOfPossessionJwkJson;\n const method = originalRequest.method;\n const dpop = await generateJwtDemonstratingProofOfPossessionAsync(self)(dpopConfiguration)(\n jwk,\n method,\n url,\n extrasClaims,\n );\n\n headersExtras.dpop = dpop;\n if (currentDatabase.demonstratingProofOfPossessionNonce != null) {\n headersExtras.nonce = currentDatabase.demonstratingProofOfPossessionNonce;\n }\n }\n return headersExtras;\n}\n\n/**\n * Nouveau handleFetch : on n’est plus async \"directement\".\n * On encapsule toute la logique dans un `respondWith((async () => { ... })())`.\n */\nconst handleFetch = (event: FetchEvent): void => {\n /**\n * Exit early for requests that do not need to have an auth token attached.\n */\n const bypassedDestinations = ['image', 'font', 'media', 'document', 'iframe', 'script'];\n if (bypassedDestinations.includes(event.request.destination)) {\n return; // Don't call event.respondWith() - let browser handle naturally\n }\n event.respondWith(\n (async (): Promise<Response> => {\n try {\n const originalRequest = event.request;\n const url = normalizeUrl(originalRequest.url);\n\n // 1) Si on est sur la ressource KeepAlive\n if (url.includes(keepAliveJsonFilename)) {\n return keepAliveAsync(event);\n }\n\n // 2) Cas normal : on regarde si on a un token\n const currentDatabasesForRequestAccessToken = getCurrentDatabaseDomain(\n database,\n url,\n trustedDomains,\n );\n\n const authorization = originalRequest.headers.get('authorization');\n let authenticationMode = 'Bearer';\n let key = 'default';\n\n if (authorization) {\n const split = authorization.split(' ');\n authenticationMode = split[0];\n if (split[1]?.includes('ACCESS_TOKEN_SECURED_BY_OIDC_SERVICE_WORKER_')) {\n key = split[1].split('ACCESS_TOKEN_SECURED_BY_OIDC_SERVICE_WORKER_')[1];\n }\n }\n\n const currentDatabaseForRequestAccessToken = currentDatabasesForRequestAccessToken?.find(\n c => c.configurationName.endsWith(key),\n );\n\n // 2a) Si on a déjà des tokens valides\n if (currentDatabaseForRequestAccessToken?.tokens?.access_token) {\n // On attend que le token soit valide (refresh possible en parallèle)\n while (\n currentDatabaseForRequestAccessToken.tokens &&\n !isTokensValid(currentDatabaseForRequestAccessToken.tokens)\n ) {\n await sleep(200);\n }\n\n // Ajustement du mode\n let requestMode = originalRequest.mode;\n if (\n originalRequest.mode !== 'navigate' &&\n currentDatabaseForRequestAccessToken.convertAllRequestsToCorsExceptNavigate\n ) {\n requestMode = 'cors';\n }\n\n // Construction des en-têtes\n let headers: { [p: string]: string };\n\n // Pas de token sur la requête \"navigate\" si setAccessTokenToNavigateRequests = false\n if (\n originalRequest.mode === 'navigate' &&\n !currentDatabaseForRequestAccessToken.setAccessTokenToNavigateRequests\n ) {\n headers = {\n ...serializeHeaders(originalRequest.headers),\n };\n } else {\n // On injecte le token\n if (\n authenticationMode.toLowerCase() === 'dpop' ||\n (!currentDatabaseForRequestAccessToken.demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent &&\n currentDatabaseForRequestAccessToken.demonstratingProofOfPossessionConfiguration)\n ) {\n // Mode DPoP\n const claimsExtras = {\n ath: await base64urlOfHashOfASCIIEncodingAsync(\n currentDatabaseForRequestAccessToken.tokens.access_token,\n ),\n };\n const dpopHeaders = await generateDpopAsync(\n originalRequest,\n currentDatabaseForRequestAccessToken,\n url,\n claimsExtras,\n );\n headers = {\n ...dpopHeaders,\n authorization: `DPoP ${currentDatabaseForRequestAccessToken.tokens.access_token}`,\n };\n } else {\n // Mode Bearer\n headers = {\n ...serializeHeaders(originalRequest.headers),\n authorization: `${authenticationMode} ${currentDatabaseForRequestAccessToken.tokens.access_token}`,\n };\n }\n }\n\n let init: RequestInit;\n if (originalRequest.mode === 'navigate') {\n init = {\n headers: headers,\n };\n } else {\n init = {\n headers: headers,\n mode: requestMode,\n };\n }\n\n const newRequest = new Request(originalRequest, init);\n return fetch(newRequest);\n }\n\n // 3) S’il ne s’agit pas d’un POST => on laisse passer\n if (event.request.method !== 'POST') {\n return fetch(originalRequest);\n }\n\n // 4) Cas POST vers un endpoint connu (token, revocation)\n const currentDatabases = getCurrentDatabasesTokenEndpoint(database, url);\n const numberDatabase = currentDatabases.length;\n\n if (numberDatabase > 0) {\n // On gère tout dans une promesse\n const responsePromise = new Promise<Response>((resolve, reject) => {\n const clonedRequest = originalRequest.clone();\n clonedRequest\n .text()\n .then(async actualBody => {\n let currentDatabase: OidcConfig | null = null;\n try {\n // 4a) S’il y a un refresh_token masqué\n if (\n actualBody.includes(TOKEN.REFRESH_TOKEN) ||\n actualBody.includes(TOKEN.ACCESS_TOKEN)\n ) {\n let headers = serializeHeaders(originalRequest.headers);\n let newBody = actualBody;\n\n for (let i = 0; i < numberDatabase; i++) {\n const currentDb = currentDatabases[i];\n if (currentDb?.tokens) {\n const claimsExtras = {\n ath: await base64urlOfHashOfASCIIEncodingAsync(\n currentDb.tokens.access_token,\n ),\n };\n headers = await generateDpopAsync(\n originalRequest,\n currentDb,\n url,\n claimsExtras,\n );\n\n const keyRefreshToken = encodeURIComponent(\n `${TOKEN.REFRESH_TOKEN}_${currentDb.configurationName}`,\n );\n if (actualBody.includes(keyRefreshToken)) {\n newBody = newBody.replace(\n keyRefreshToken,\n encodeURIComponent(currentDb.tokens.refresh_token as string),\n );\n currentDatabase = currentDb;\n break;\n }\n\n const keyAccessToken = encodeURIComponent(\n `${TOKEN.ACCESS_TOKEN}_${currentDb.configurationName}`,\n );\n if (actualBody.includes(keyAccessToken)) {\n newBody = newBody.replace(\n keyAccessToken,\n encodeURIComponent(currentDb.tokens.access_token),\n );\n currentDatabase = currentDb;\n break;\n }\n }\n }\n\n const fetchPromise = fetch(originalRequest, {\n body: newBody,\n method: clonedRequest.method,\n headers,\n mode: clonedRequest.mode,\n cache: clonedRequest.cache,\n redirect: clonedRequest.redirect,\n referrer: clonedRequest.referrer,\n credentials: clonedRequest.credentials,\n integrity: clonedRequest.integrity,\n });\n\n // Cas “revocationEndpoint” ?\n if (\n currentDatabase?.oidcServerConfiguration?.revocationEndpoint &&\n url.startsWith(\n normalizeUrl(currentDatabase.oidcServerConfiguration.revocationEndpoint),\n )\n ) {\n // On ne modifie pas le corps\n const resp = await fetchPromise;\n const txt = await resp.text();\n resolve(new Response(txt, resp));\n return;\n }\n\n // Sinon on “cache” les tokens dans la réponse\n const hidden = await fetchPromise.then(\n hideTokens(currentDatabase as OidcConfig),\n );\n resolve(hidden);\n return;\n }\n\n // 4b) Sinon si c’est le code_verifier\n const isCodeVerifier = actualBody.includes('code_verifier=');\n if (isCodeVerifier) {\n const currentLoginCallbackConfigurationName =\n extractConfigurationNameFromCodeVerifier(actualBody);\n if (\n !currentLoginCallbackConfigurationName ||\n currentLoginCallbackConfigurationName === ''\n ) {\n throw new Error('No configuration name found in code_verifier');\n }\n currentDatabase = database[currentLoginCallbackConfigurationName];\n let newBody = actualBody;\n const codeVerifier = currentDatabase.codeVerifier;\n if (codeVerifier != null) {\n newBody = replaceCodeVerifier(newBody, codeVerifier);\n }\n\n const headersExtras = await generateDpopAsync(\n originalRequest,\n currentDatabase,\n url,\n );\n const resp = await fetch(originalRequest, {\n body: newBody,\n method: clonedRequest.method,\n headers: headersExtras,\n mode: clonedRequest.mode,\n cache: clonedRequest.cache,\n redirect: clonedRequest.redirect,\n referrer: clonedRequest.referrer,\n credentials: clonedRequest.credentials,\n integrity: clonedRequest.integrity,\n });\n const hidden = await hideTokens(currentDatabase)(resp);\n resolve(hidden);\n return;\n }\n\n // 4c) Sinon on laisse passer tel quel\n const normalResp = await fetch(originalRequest, {\n body: actualBody,\n method: clonedRequest.method,\n headers: serializeHeaders(originalRequest.headers),\n mode: clonedRequest.mode,\n cache: clonedRequest.cache,\n redirect: clonedRequest.redirect,\n referrer: clonedRequest.referrer,\n credentials: clonedRequest.credentials,\n integrity: clonedRequest.integrity,\n });\n resolve(normalResp);\n } catch (err) {\n reject(err);\n }\n })\n .catch(reject);\n });\n\n // On renvoie simplement la promesse\n return responsePromise;\n }\n\n // 5) Par défaut, on laisse passer la requête\n return fetch(originalRequest);\n } catch (err) {\n // En cas d’erreur imprévue, on log et on retourne une 500\n console.error('[OidcServiceWorker] handleFetch error:', err);\n return new Response('Service Worker Error', { status: 500 });\n }\n })(),\n );\n};\n\n// ---- Gestion des messages depuis la page\nconst handleMessage = async (event: ExtendableMessageEvent) => {\n const port = event.ports[0];\n const data = event.data as MessageEventData;\n\n if (event.data?.type === 'SKIP_WAITING') {\n await _self.skipWaiting();\n port?.postMessage?.({});\n return;\n } else if (event.data.type === 'claim') {\n _self.clients.claim().then(() => port.postMessage({}));\n return;\n }\n\n const configurationName = data.configurationName.split('#')[0];\n\n if (trustedDomains == null) {\n trustedDomains = {};\n }\n\n const trustedDomain = trustedDomains[configurationName];\n const allowMultiTabLogin = Array.isArray(trustedDomain)\n ? false\n : trustedDomain.allowMultiTabLogin;\n\n const tabId = allowMultiTabLogin ? data.tabId : 'default';\n const configurationNameWithTabId = `${configurationName}#tabId=${tabId}`;\n\n let currentDatabase = database[configurationNameWithTabId];\n if (!currentDatabase) {\n const showAccessToken = Array.isArray(trustedDomain) ? false : trustedDomain.showAccessToken;\n const doNotSetAccessTokenToNavigateRequests = Array.isArray(trustedDomain)\n ? true\n : trustedDomain.setAccessTokenToNavigateRequests;\n const convertAllRequestsToCorsExceptNavigate = Array.isArray(trustedDomain)\n ? false\n : trustedDomain.convertAllRequestsToCorsExceptNavigate;\n\n database[configurationNameWithTabId] = {\n tokens: null,\n state: null,\n codeVerifier: null,\n oidcServerConfiguration: null,\n oidcConfiguration: undefined,\n nonce: null,\n status: null,\n configurationName: configurationNameWithTabId,\n hideAccessToken: !showAccessToken,\n setAccessTokenToNavigateRequests: doNotSetAccessTokenToNavigateRequests ?? true,\n convertAllRequestsToCorsExceptNavigate: convertAllRequestsToCorsExceptNavigate ?? false,\n demonstratingProofOfPossessionNonce: null,\n demonstratingProofOfPossessionJwkJson: null,\n demonstratingProofOfPossessionConfiguration: null,\n demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent: false,\n allowMultiTabLogin: allowMultiTabLogin ?? false,\n };\n currentDatabase = database[configurationNameWithTabId];\n\n if (!trustedDomains[configurationName]) {\n trustedDomains[configurationName] = [];\n }\n }\n\n switch (data.type) {\n case 'clear':\n currentDatabase.tokens = null;\n currentDatabase.state = null;\n currentDatabase.codeVerifier = null;\n currentDatabase.nonce = null;\n currentDatabase.demonstratingProofOfPossessionNonce = null;\n currentDatabase.demonstratingProofOfPossessionJwkJson = null;\n currentDatabase.demonstratingProofOfPossessionConfiguration = null;\n currentDatabase.demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent = false;\n currentDatabase.status = data.data.status;\n port.postMessage({ configurationName });\n return;\n\n case 'init': {\n const oidcServerConfiguration = data.data.oidcServerConfiguration;\n const domains = getDomains(trustedDomain, 'oidc');\n\n if (!domains.some(domain => domain === acceptAnyDomainToken)) {\n [\n oidcServerConfiguration.tokenEndpoint,\n oidcServerConfiguration.revocationEndpoint,\n oidcServerConfiguration.userInfoEndpoint,\n oidcServerConfiguration.issuer,\n ].forEach(u => {\n checkDomain(domains, u);\n });\n }\n\n currentDatabase.oidcServerConfiguration = oidcServerConfiguration;\n currentDatabase.oidcConfiguration = data.data.oidcConfiguration;\n\n // Cas DPoP\n if (currentDatabase.demonstratingProofOfPossessionConfiguration == null) {\n const demonstratingProofOfPossessionConfiguration = getDpopConfiguration(trustedDomain);\n if (demonstratingProofOfPossessionConfiguration != null) {\n if (currentDatabase.oidcConfiguration.demonstrating_proof_of_possession) {\n console.warn(\n 'In service worker, demonstrating_proof_of_possession must be configured from trustedDomains file',\n );\n }\n currentDatabase.demonstratingProofOfPossessionConfiguration =\n demonstratingProofOfPossessionConfiguration;\n currentDatabase.demonstratingProofOfPossessionJwkJson = await generateJwkAsync(self)(\n demonstratingProofOfPossessionConfiguration.generateKeyAlgorithm,\n );\n currentDatabase.demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent =\n getDpopOnlyWhenDpopHeaderPresent(trustedDomain) ?? false;\n }\n }\n\n if (!currentDatabase.tokens) {\n port.postMessage({\n tokens: null,\n status: currentDatabase.status,\n configurationName,\n version,\n });\n } else {\n const tokens = { ...currentDatabase.tokens };\n if (currentDatabase.hideAccessToken) {\n tokens.access_token = `${TOKEN.ACCESS_TOKEN}_${configurationName}#tabId=${tabId}`;\n }\n if (tokens.refresh_token) {\n tokens.refresh_token = `${TOKEN.REFRESH_TOKEN}_${configurationName}#tabId=${tabId}`;\n }\n if (tokens?.idTokenPayload?.nonce && currentDatabase.nonce != null) {\n tokens.idTokenPayload.nonce = `${TOKEN.NONCE_TOKEN}_${configurationName}#tabId=${tabId}`;\n }\n port.postMessage({\n tokens,\n status: currentDatabase.status,\n configurationName,\n version,\n });\n }\n return;\n }\n\n case 'setDemonstratingProofOfPossessionNonce': {\n currentDatabase.demonstratingProofOfPossessionNonce =\n data.data.demonstratingProofOfPossessionNonce;\n port.postMessage({ configurationName });\n return;\n }\n\n case 'getDemonstratingProofOfPossessionNonce': {\n const demonstratingProofOfPossessionNonce =\n currentDatabase.demonstratingProofOfPossessionNonce;\n port.postMessage({\n configurationName,\n demonstratingProofOfPossessionNonce,\n });\n return;\n }\n\n case 'setState': {\n currentDatabase.state = data.data.state;\n port.postMessage({ configurationName });\n return;\n }\n\n case 'getState': {\n const state = currentDatabase.state;\n port.postMessage({ configurationName, state });\n return;\n }\n\n case 'setCodeVerifier': {\n currentDatabase.codeVerifier = data.data.codeVerifier;\n port.postMessage({ configurationName });\n return;\n }\n\n case 'getCodeVerifier': {\n const codeVerifier =\n currentDatabase.codeVerifier != null\n ? `${TOKEN.CODE_VERIFIER}_${configurationName}#tabId=${tabId}`\n : null;\n port.postMessage({\n configurationName,\n codeVerifier,\n });\n return;\n }\n\n case 'setSessionState': {\n currentDatabase.sessionState = data.data.sessionState;\n port.postMessage({ configurationName });\n return;\n }\n\n case 'getSessionState': {\n const sessionState = currentDatabase.sessionState;\n port.postMessage({ configurationName, sessionState });\n return;\n }\n\n case 'setNonce': {\n const nonce = data.data.nonce;\n if (nonce) {\n currentDatabase.nonce = nonce;\n }\n port.postMessage({ configurationName });\n return;\n }\n\n case 'getNonce': {\n const keyNonce = `${TOKEN.NONCE_TOKEN}_${configurationName}#tabId=${tabId}`;\n const nonce = currentDatabase.nonce ? keyNonce : null;\n port.postMessage({ configurationName, nonce });\n return;\n }\n\n default:\n return;\n }\n};\n\n// Écouteurs\n_self.addEventListener('fetch', handleFetch);\n_self.addEventListener('message', handleMessage);\n"],"names":["domain","database","trustedDomains","getCurrentDatabasesTokenEndpoint"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"OidcServiceWorker.js","sources":["../src/constants.ts","../src/jwt.ts","../src/crypto.ts","../src/dpop.ts","../src/utils/normalizeUrl.ts","../src/utils/domains.ts","../src/utils/serializeHeaders.ts","../src/utils/sleep.ts","../src/utils/strings.ts","../src/utils/tokens.ts","../src/utils/waitForValidTokens.ts","../src/oidcConfig.ts","../src/utils/codeVerifier.ts","../src/version.ts","../src/OidcServiceWorker.ts"],"sourcesContent":["const scriptFilename = 'OidcTrustedDomains.js';\nconst acceptAnyDomainToken = '*';\n\ntype TokenType = {\n readonly REFRESH_TOKEN: string;\n readonly ACCESS_TOKEN: string;\n readonly NONCE_TOKEN: string;\n readonly CODE_VERIFIER: string;\n};\n\nconst TOKEN: TokenType = {\n REFRESH_TOKEN: 'REFRESH_TOKEN_SECURED_BY_OIDC_SERVICE_WORKER',\n ACCESS_TOKEN: 'ACCESS_TOKEN_SECURED_BY_OIDC_SERVICE_WORKER',\n NONCE_TOKEN: 'NONCE_SECURED_BY_OIDC_SERVICE_WORKER',\n CODE_VERIFIER: 'CODE_VERIFIER_SECURED_BY_OIDC_SERVICE_WORKER',\n};\n\ntype TokenRenewModeType = {\n readonly access_token_or_id_token_invalid: string;\n readonly access_token_invalid: string;\n readonly id_token_invalid: string;\n};\n\nconst TokenRenewMode: TokenRenewModeType = {\n access_token_or_id_token_invalid: 'access_token_or_id_token_invalid',\n access_token_invalid: 'access_token_invalid',\n id_token_invalid: 'id_token_invalid',\n};\n\nconst openidWellknownUrlEndWith = '/.well-known/openid-configuration';\n\nexport { acceptAnyDomainToken, openidWellknownUrlEndWith, scriptFilename, TOKEN, TokenRenewMode };\n","// code base on https://coolaj86.com/articles/sign-jwt-webcrypto-vanilla-js/\n\n// String (UCS-2) to Uint8Array\n//\n// because... JavaScript, Strings, and Buffers\n// @ts-ignore\nimport { DemonstratingProofOfPossessionConfiguration } from './types';\n\nfunction strToUint8(str: string) {\n return new TextEncoder().encode(str);\n}\n\n// Binary String to URL-Safe Base64\n//\n// btoa (Binary-to-Ascii) means \"binary string\" to base64\n// @ts-ignore\nfunction binToUrlBase64(bin) {\n return btoa(bin).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+/g, '');\n}\n\n// UTF-8 to Binary String\n//\n// Because JavaScript has a strange relationship with strings\n// https://coolaj86.com/articles/base64-unicode-utf-8-javascript-and-you/\n// @ts-ignore\nfunction utf8ToBinaryString(str) {\n const escstr = encodeURIComponent(str);\n // replaces any uri escape sequence, such as %0A,\n // with binary escape, such as 0x0A\n // @ts-ignore\n return escstr.replace(/%([0-9A-F]{2})/g, function (match: string, p1) {\n return String.fromCharCode(parseInt(p1, 16));\n });\n}\n\n// Uint8Array to URL Safe Base64\n//\n// the shortest distant between two encodings... binary string\n// @ts-ignore\nexport const uint8ToUrlBase64 = (uint8: Uint8Array) => {\n let bin = '';\n // @ts-ignore\n uint8.forEach(function (code) {\n bin += String.fromCharCode(code);\n });\n return binToUrlBase64(bin);\n};\n\n// UCS-2 String to URL-Safe Base64\n//\n// btoa doesn't work on UTF-8 strings\n// @ts-ignore\nfunction strToUrlBase64(str) {\n return binToUrlBase64(utf8ToBinaryString(str));\n}\n\nexport const defaultDemonstratingProofOfPossessionConfiguration: DemonstratingProofOfPossessionConfiguration =\n {\n importKeyAlgorithm: {\n name: 'ECDSA',\n namedCurve: 'P-256',\n hash: { name: 'ES256' },\n },\n signAlgorithm: { name: 'ECDSA', hash: { name: 'SHA-256' } },\n generateKeyAlgorithm: {\n name: 'ECDSA',\n namedCurve: 'P-256',\n },\n digestAlgorithm: { name: 'SHA-256' },\n jwtHeaderAlgorithm: 'ES256',\n };\n\n// @ts-ignore\nconst sign =\n (w: any) =>\n async (\n jwk: any,\n headers: any,\n claims: any,\n demonstratingProofOfPossessionConfiguration: DemonstratingProofOfPossessionConfiguration,\n jwtHeaderType = 'dpop+jwt',\n ) => {\n // Make a shallow copy of the key\n // (to set ext if it wasn't already set)\n jwk = Object.assign({}, jwk);\n\n // The headers should probably be empty\n headers.typ = jwtHeaderType;\n headers.alg = demonstratingProofOfPossessionConfiguration.jwtHeaderAlgorithm;\n switch (headers.alg) {\n case 'ES256': //if (!headers.kid) {\n // alternate: see thumbprint function below\n headers.jwk = { kty: jwk.kty, crv: jwk.crv, x: jwk.x, y: jwk.y };\n //}\n break;\n case 'RS256':\n headers.jwk = { kty: jwk.kty, n: jwk.n, e: jwk.e, kid: headers.kid };\n break;\n default:\n throw new Error('Unknown or not implemented JWS algorithm');\n }\n\n const jws = {\n // @ts-ignore\n // JWT \"headers\" really means JWS \"protected headers\"\n protected: strToUrlBase64(JSON.stringify(headers)),\n // @ts-ignore\n // JWT \"claims\" are really a JSON-defined JWS \"payload\"\n payload: strToUrlBase64(JSON.stringify(claims)),\n };\n\n // To import as EC (ECDSA, P-256, SHA-256, ES256)\n const keyType = demonstratingProofOfPossessionConfiguration.importKeyAlgorithm;\n\n // To make re-exportable as JSON (or DER/PEM)\n const exportable = true;\n\n // Import as a private key that isn't black-listed from signing\n const privileges = ['sign'];\n\n // Actually do the import, which comes out as an abstract key type\n // @ts-ignore\n const privateKey = await w.crypto.subtle.importKey('jwk', jwk, keyType, exportable, privileges);\n // Convert UTF-8 to Uint8Array ArrayBuffer\n // @ts-ignore\n const data = strToUint8(`${jws.protected}.${jws.payload}`);\n\n // The signature and hash should match the bit-entropy of the key\n // https://tools.ietf.org/html/rfc7518#section-3\n const signatureType = demonstratingProofOfPossessionConfiguration.signAlgorithm;\n\n const signature = await w.crypto.subtle.sign(signatureType, privateKey, data);\n // returns an ArrayBuffer containing a JOSE (not X509) signature,\n // which must be converted to Uint8 to be useful\n // @ts-ignore\n jws.signature = uint8ToUrlBase64(new Uint8Array(signature));\n // JWT is just a \"compressed\", \"protected\" JWS\n // @ts-ignore\n return `${jws.protected}.${jws.payload}.${jws.signature}`;\n };\n\nexport const JWT = { sign };\n\n// @ts-ignore\nconst generate =\n (w: any) => async (generateKeyAlgorithm: RsaHashedKeyGenParams | EcKeyGenParams) => {\n const keyType = generateKeyAlgorithm;\n const exportable = true;\n const privileges = ['sign', 'verify'];\n // @ts-ignore\n const key = await w.crypto.subtle.generateKey(keyType, exportable, privileges);\n // returns an abstract and opaque WebCrypto object,\n // which in most cases you'll want to export as JSON to be able to save\n return await w.crypto.subtle.exportKey('jwk', key.privateKey);\n };\n\n// Create a Public Key from a Private Key\n//\n// chops off the private parts\n// @ts-ignore\nconst neuter = jwk => {\n const copy = Object.assign({}, jwk);\n delete copy.d;\n copy.key_ops = ['verify'];\n return copy;\n};\n\nconst EC = {\n generate,\n neuter,\n};\n// @ts-ignore\nconst thumbprint = (w: any) => async (jwk, digestAlgorithm: AlgorithmIdentifier) => {\n let sortedPub;\n // lexigraphically sorted, no spaces\n switch (jwk.kty) {\n case 'EC':\n sortedPub = '{\"crv\":\"CRV\",\"kty\":\"EC\",\"x\":\"X\",\"y\":\"Y\"}'\n .replace('CRV', jwk.crv)\n .replace('X', jwk.x)\n .replace('Y', jwk.y);\n break;\n case 'RSA':\n sortedPub = '{\"e\":\"E\",\"kty\":\"RSA\",\"n\":\"N\"}'.replace('E', jwk.e).replace('N', jwk.n);\n break;\n default:\n throw new Error('Unknown or not implemented JWK type');\n }\n // The hash should match the size of the key,\n // but we're only dealing with P-256\n const hash = await w.crypto.subtle.digest(digestAlgorithm, strToUint8(sortedPub));\n return uint8ToUrlBase64(new Uint8Array(hash));\n};\n\nexport const JWK = { thumbprint };\n\nexport const generateJwkAsync =\n (w: any) => async (generateKeyAlgorithm: RsaHashedKeyGenParams | EcKeyGenParams) => {\n // @ts-ignore\n const jwk = await EC.generate(w)(generateKeyAlgorithm);\n // console.info('Private Key:', JSON.stringify(jwk));\n // @ts-ignore\n // console.info('Public Key:', JSON.stringify(EC.neuter(jwk)));\n return jwk;\n };\n\nexport const generateJwtDemonstratingProofOfPossessionAsync =\n (w: any) =>\n (demonstratingProofOfPossessionConfiguration: DemonstratingProofOfPossessionConfiguration) =>\n async (jwk: any, method = 'POST', url: string, extrasClaims = {}) => {\n const claims = {\n // https://www.rfc-editor.org/rfc/rfc9449.html#name-concept\n jti: btoa(guid()),\n htm: method,\n htu: url,\n iat: Math.round(Date.now() / 1000),\n ...extrasClaims,\n };\n // @ts-ignore\n const kid = await JWK.thumbprint(w)(\n jwk,\n demonstratingProofOfPossessionConfiguration.digestAlgorithm,\n );\n // @ts-ignore\n const jwt = await JWT.sign(w)(\n jwk,\n { kid: kid },\n claims,\n demonstratingProofOfPossessionConfiguration,\n );\n // console.info('JWT:', jwt);\n return jwt;\n };\n\nconst guid = () => {\n // RFC4122: The version 4 UUID is meant for generating UUIDs from truly-random or\n // pseudo-random numbers.\n // The algorithm is as follows:\n // Set the two most significant bits (bits 6 and 7) of the\n // clock_seq_hi_and_reserved to zero and one, respectively.\n // Set the four most significant bits (bits 12 through 15) of the\n // time_hi_and_version field to the 4-bit version number from\n // Section 4.1.3. Version4\n // Set all the other bits to randomly (or pseudo-randomly) chosen\n // values.\n // UUID = time-low \"-\" time-mid \"-\"time-high-and-version \"-\"clock-seq-reserved and low(2hexOctet)\"-\" node\n // time-low = 4hexOctet\n // time-mid = 2hexOctet\n // time-high-and-version = 2hexOctet\n // clock-seq-and-reserved = hexOctet:\n // clock-seq-low = hexOctet\n // node = 6hexOctet\n // Format: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\n // y could be 1000, 1001, 1010, 1011 since most significant two bits needs to be 10\n // y values are 8, 9, A, B\n const guidHolder = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';\n const hex = '0123456789abcdef';\n let r = 0;\n let guidResponse = '';\n for (let i = 0; i < 36; i++) {\n if (guidHolder[i] !== '-' && guidHolder[i] !== '4') {\n // each x and y needs to be random\n r = (Math.random() * 16) | 0;\n }\n\n if (guidHolder[i] === 'x') {\n guidResponse += hex[r];\n } else if (guidHolder[i] === 'y') {\n // clock-seq-and-reserved first hex is filtered and remaining hex values are random\n r &= 0x3; // bit and with 0011 to set pos 2 to zero ?0??\n r |= 0x8; // set pos 3 to 1 as 1???\n guidResponse += hex[r];\n } else {\n guidResponse += guidHolder[i];\n }\n }\n\n return guidResponse;\n};\n","import { uint8ToUrlBase64 } from './jwt';\n\nexport function textEncodeLite(str: string) {\n const buf = new ArrayBuffer(str.length);\n const bufView = new Uint8Array(buf);\n\n for (let i = 0; i < str.length; i++) {\n bufView[i] = str.charCodeAt(i);\n }\n return bufView;\n}\n\nexport function base64urlOfHashOfASCIIEncodingAsync(code: string): Promise<string> {\n return new Promise((resolve, reject) => {\n crypto.subtle.digest('SHA-256', textEncodeLite(code)).then(\n buffer => {\n return resolve(uint8ToUrlBase64(new Uint8Array(buffer)));\n },\n error => reject(error),\n );\n });\n}\n","import { defaultDemonstratingProofOfPossessionConfiguration } from './jwt';\nimport { Domain, DomainDetails } from './types.js';\n\nconst isDpop = (trustedDomain: Domain[] | DomainDetails): boolean => {\n if (Array.isArray(trustedDomain)) {\n return false;\n }\n return trustedDomain.demonstratingProofOfPossession ?? false;\n};\n\nexport const getDpopConfiguration = (trustedDomain: Domain[] | DomainDetails) => {\n if (!isDpop(trustedDomain)) {\n return null;\n }\n\n if (Array.isArray(trustedDomain)) {\n return null;\n }\n\n return (\n trustedDomain.demonstratingProofOfPossessionConfiguration ??\n defaultDemonstratingProofOfPossessionConfiguration\n );\n};\n\nexport const getDpopOnlyWhenDpopHeaderPresent = (trustedDomain: Domain[] | DomainDetails) => {\n if (!isDpop(trustedDomain)) {\n return null;\n }\n\n if (Array.isArray(trustedDomain)) {\n return null;\n }\n\n return trustedDomain.demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent ?? true;\n};\n","export function normalizeUrl(url: string) {\n try {\n return new URL(url).toString();\n } catch (error) {\n console.error(`Failed to normalize url: ${url}`, error);\n return url;\n }\n}\n","import { acceptAnyDomainToken, openidWellknownUrlEndWith, scriptFilename } from '../constants';\nimport { Database, Domain, DomainDetails, OidcConfig, TrustedDomains } from '../types';\nimport { normalizeUrl } from './normalizeUrl';\n\nexport function checkDomain(domains: Domain[], endpoint: string) {\n if (!endpoint) {\n return;\n }\n\n const domain = domains.find(domain => {\n let testable: RegExp;\n\n if (typeof domain === 'string') {\n testable = new RegExp(`^${domain}`);\n } else {\n testable = domain;\n }\n\n return testable.test?.(endpoint);\n });\n if (!domain) {\n throw new Error(\n 'Domain ' + endpoint + ' is not trusted, please add domain in ' + scriptFilename,\n );\n }\n}\n\nexport const getDomains = (\n trustedDomain: Domain[] | DomainDetails,\n type: 'oidc' | 'accessToken',\n) => {\n if (Array.isArray(trustedDomain)) {\n return trustedDomain;\n }\n\n return trustedDomain[`${type}Domains`] ?? trustedDomain.domains ?? [];\n};\n\nexport const getCurrentDatabaseDomain = (\n database: Database,\n url: string,\n trustedDomains: TrustedDomains,\n) => {\n if (url.endsWith(openidWellknownUrlEndWith)) {\n return null;\n }\n const datatases = [];\n for (const [key, currentDatabase] of Object.entries<OidcConfig>(database)) {\n const oidcServerConfiguration = currentDatabase.oidcServerConfiguration;\n\n if (!oidcServerConfiguration) {\n continue;\n }\n\n if (\n oidcServerConfiguration.tokenEndpoint &&\n url === normalizeUrl(oidcServerConfiguration.tokenEndpoint)\n ) {\n continue;\n }\n if (\n oidcServerConfiguration.revocationEndpoint &&\n url === normalizeUrl(oidcServerConfiguration.revocationEndpoint)\n ) {\n continue;\n }\n const trustedDomain = trustedDomains == null ? [] : trustedDomains[key.split('#')[0]];\n\n const domains = getDomains(trustedDomain, 'accessToken');\n const domainsToSendTokens = oidcServerConfiguration.userInfoEndpoint\n ? [normalizeUrl(oidcServerConfiguration.userInfoEndpoint), ...domains]\n : [...domains];\n\n let hasToSendToken = false;\n if (domainsToSendTokens.find(f => f === acceptAnyDomainToken)) {\n hasToSendToken = true;\n } else {\n for (let i = 0; i < domainsToSendTokens.length; i++) {\n let domain = domainsToSendTokens[i];\n\n if (typeof domain === 'string') {\n domain = new RegExp(`^${domain}`);\n }\n\n if (domain.test?.(url)) {\n hasToSendToken = true;\n break;\n }\n }\n }\n\n if (hasToSendToken) {\n if (currentDatabase.tokens) {\n datatases.push(currentDatabase);\n }\n }\n }\n return datatases;\n};\n","import { FetchHeaders } from '../types';\n\nfunction serializeHeaders(headers: Headers) {\n const headersObj: Record<string, string> = {};\n for (const key of (headers as FetchHeaders).keys()) {\n if (headers.has(key)) {\n headersObj[key] = headers.get(key) as string;\n }\n }\n return headersObj;\n}\nexport { serializeHeaders };\n","const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));\nexport { sleep };\n","/**\n * Count occurances of letter in string\n * @param str\n * @param find\n * @returns\n */\nexport function countLetter(str: string, find: string) {\n return str.split(find).length - 1;\n}\n","/* eslint-disable simple-import-sort/exports */\nimport { TOKEN, TokenRenewMode } from '../constants';\nimport {\n AccessTokenPayload,\n IdTokenPayload,\n OidcConfig,\n OidcConfiguration,\n OidcServerConfiguration,\n Tokens,\n} from '../types';\nimport { countLetter } from './strings';\n\nexport const parseJwt = (payload: string) => {\n return JSON.parse(b64DecodeUnicode(payload.replaceAll(/-/g, '+').replaceAll(/_/g, '/')));\n};\nfunction b64DecodeUnicode(str: string) {\n return decodeURIComponent(\n Array.prototype.map\n .call(atob(str), c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))\n .join(''),\n );\n}\n\nfunction computeTimeLeft(refreshTimeBeforeTokensExpirationInSecond: number, expiresAt: number) {\n const currentTimeUnixSecond = new Date().getTime() / 1000;\n return Math.round(expiresAt - refreshTimeBeforeTokensExpirationInSecond - currentTimeUnixSecond);\n}\n\nfunction isTokensValid(tokens: Tokens | null) {\n if (!tokens) {\n return false;\n }\n return computeTimeLeft(0, tokens.expiresAt) > 0;\n}\n\nconst extractTokenPayload = (token?: string) => {\n try {\n if (!token) {\n return null;\n }\n if (countLetter(token, '.') === 2) {\n return parseJwt(token.split('.')[1]);\n } else {\n return null;\n }\n } catch (e) {\n console.warn(e);\n }\n return null;\n};\n\n// https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation (excluding rules #1, #4, #5, #7, #8, #12, and #13 which did not apply).\n// https://github.com/openid/AppAuth-JS/issues/65\nconst isTokensOidcValid = (\n tokens: Tokens,\n nonce: string | null,\n oidcServerConfiguration: OidcServerConfiguration,\n): { isValid: boolean; reason: string } => {\n if (tokens.idTokenPayload) {\n const idTokenPayload = tokens.idTokenPayload;\n // 2: The Issuer Identifier for the OpenID Provider (which is typically obtained during Discovery) MUST exactly match the value of the iss (issuer) Claim.\n if (idTokenPayload && oidcServerConfiguration.issuer !== idTokenPayload.iss) {\n return {\n isValid: false,\n reason: `Issuer does not match (oidcServerConfiguration issuer) ${oidcServerConfiguration.issuer} !== (idTokenPayload issuer) ${idTokenPayload.iss}`,\n };\n }\n // 3: The Client MUST validate that the aud (audience) Claim contains its client_id value registered at the Issuer identified by the iss (issuer) Claim as an audience. The aud (audience) Claim MAY contain an array with more than one element. The ID Token MUST be rejected if the ID Token does not list the Client as a valid audience, or if it contains additional audiences not trusted by the Client.\n\n // 6: If the ID Token is received via direct communication between the Client and the Token Endpoint (which it is in this flow), the TLS server validation MAY be used to validate the issuer in place of checking the token signature. The Client MUST validate the signature of all other ID Tokens according to JWS [JWS] using the algorithm specified in the JWT alg Header Parameter. The Client MUST use the keys provided by the Issuer.\n\n // 9: The current time MUST be before the time represented by the exp Claim.\n const currentTimeUnixSecond = new Date().getTime() / 1000;\n if (idTokenPayload && idTokenPayload.exp && idTokenPayload.exp < currentTimeUnixSecond) {\n return {\n isValid: false,\n reason: `Token expired at (idTokenPayload exp) ${idTokenPayload.exp} < (currentTimeUnixSecond) ${currentTimeUnixSecond}`,\n };\n }\n // 10: The iat Claim can be used to reject tokens that were issued too far away from the current time, limiting the amount of time that nonces need to be stored to prevent attacks. The acceptable range is Client specific.\n const timeInSevenDays = 60 * 60 * 24 * 7;\n if (\n idTokenPayload &&\n idTokenPayload.iat &&\n idTokenPayload.iat + timeInSevenDays < currentTimeUnixSecond\n ) {\n return {\n isValid: false,\n reason: `Token is used from too long time (idTokenPayload iat + timeInSevenDays) ${idTokenPayload.iat + timeInSevenDays} < (currentTimeUnixSecond) ${currentTimeUnixSecond}`,\n };\n }\n // 11: If a nonce value was sent in the Authentication Request, a nonce Claim MUST be present and its value checked to verify that it is the same value as the one that was sent in the Authentication Request. The Client SHOULD check the nonce value for replay attacks. The precise method for detecting replay attacks is Client specific.\n if (idTokenPayload && nonce && idTokenPayload.nonce && idTokenPayload.nonce !== nonce) {\n return {\n isValid: false,\n reason: `Nonce does not match (nonce) ${nonce} !== (idTokenPayload nonce) ${idTokenPayload.nonce}`,\n };\n }\n }\n return { isValid: true, reason: '' };\n};\n\nfunction extractedIssueAt(\n tokens: Tokens,\n accessTokenPayload: AccessTokenPayload | null,\n _idTokenPayload: IdTokenPayload,\n) {\n if (!tokens.issued_at) {\n if (accessTokenPayload && accessTokenPayload.iat) {\n return accessTokenPayload.iat;\n } else if (_idTokenPayload && _idTokenPayload.iat) {\n return _idTokenPayload.iat;\n } else {\n const currentTimeUnixSecond = new Date().getTime() / 1000;\n return currentTimeUnixSecond;\n }\n } else if (typeof tokens.issued_at == 'string') {\n return parseInt(tokens.issued_at, 10);\n }\n return tokens.issued_at;\n}\n\nfunction _hideTokens(\n tokens: Tokens,\n currentDatabaseElement: OidcConfig,\n configurationName: string,\n) {\n if (!tokens.issued_at) {\n const currentTimeUnixSecond = new Date().getTime() / 1000;\n tokens.issued_at = currentTimeUnixSecond;\n } else if (typeof tokens.issued_at == 'string') {\n tokens.issued_at = parseInt(tokens.issued_at, 10);\n }\n\n const accessTokenPayload = extractTokenPayload(tokens.access_token);\n const secureTokens = {\n ...tokens,\n accessTokenPayload,\n };\n if (currentDatabaseElement.hideAccessToken) {\n secureTokens.access_token = `${TOKEN.ACCESS_TOKEN}_${configurationName}`;\n }\n tokens.accessTokenPayload = accessTokenPayload;\n\n // When id_token is not rotated we reuse old id_token\n const oldTokens = currentDatabaseElement.tokens;\n let id_token: string | null;\n if (oldTokens != null && 'id_token' in oldTokens && !('id_token' in tokens)) {\n id_token = oldTokens.id_token;\n } else {\n id_token = tokens.id_token;\n }\n tokens.id_token = id_token;\n\n let _idTokenPayload = null;\n if (id_token) {\n _idTokenPayload = extractTokenPayload(id_token);\n tokens.idTokenPayload = _idTokenPayload != null ? { ..._idTokenPayload } : null;\n if (_idTokenPayload && _idTokenPayload.nonce && currentDatabaseElement.nonce != null) {\n const keyNonce = `${TOKEN.NONCE_TOKEN}_${currentDatabaseElement.configurationName}`;\n _idTokenPayload.nonce = keyNonce;\n }\n secureTokens.idTokenPayload = _idTokenPayload;\n }\n if (tokens.refresh_token) {\n secureTokens.refresh_token = `${TOKEN.REFRESH_TOKEN}_${configurationName}`;\n }\n\n tokens.issued_at = extractedIssueAt(tokens, accessTokenPayload, _idTokenPayload);\n\n const expireIn =\n typeof tokens.expires_in == 'string' ? parseInt(tokens.expires_in, 10) : tokens.expires_in;\n\n const idTokenExpiresAt =\n _idTokenPayload && _idTokenPayload.exp ? _idTokenPayload.exp : Number.MAX_VALUE;\n const accessTokenExpiresAt =\n accessTokenPayload && accessTokenPayload.exp\n ? accessTokenPayload.exp\n : tokens.issued_at + expireIn;\n\n let expiresAt: number;\n const tokenRenewMode = (currentDatabaseElement.oidcConfiguration as OidcConfiguration)\n .token_renew_mode;\n if (tokenRenewMode === TokenRenewMode.access_token_invalid) {\n expiresAt = accessTokenExpiresAt;\n } else if (tokenRenewMode === TokenRenewMode.id_token_invalid) {\n expiresAt = idTokenExpiresAt;\n } else {\n expiresAt = idTokenExpiresAt < accessTokenExpiresAt ? idTokenExpiresAt : accessTokenExpiresAt;\n }\n secureTokens.expiresAt = expiresAt;\n\n tokens.expiresAt = expiresAt;\n const nonce = currentDatabaseElement.nonce ? currentDatabaseElement.nonce.nonce : null;\n const { isValid, reason } = isTokensOidcValid(\n tokens,\n nonce as string,\n currentDatabaseElement.oidcServerConfiguration as OidcServerConfiguration,\n ); // TODO: Type assertion, could be null.\n if (!isValid) {\n throw Error(`Tokens are not OpenID valid, reason: ${reason}`);\n }\n\n // When refresh_token is not rotated we reuse old refresh_token\n if (oldTokens != null && 'refresh_token' in oldTokens && !('refresh_token' in tokens)) {\n const refreshToken = oldTokens.refresh_token;\n\n currentDatabaseElement.tokens = {\n ...tokens,\n refresh_token: refreshToken,\n };\n } else {\n currentDatabaseElement.tokens = tokens;\n }\n\n currentDatabaseElement.status = 'LOGGED_IN';\n return secureTokens;\n}\n\nconst demonstratingProofOfPossessionNonceResponseHeader = 'DPoP-Nonce';\nfunction hideTokens(currentDatabaseElement: OidcConfig) {\n const configurationName = currentDatabaseElement.configurationName;\n return (response: Response) => {\n if (response.status !== 200) {\n return response;\n }\n const newHeaders = new Headers(response.headers);\n if (response.headers.has(demonstratingProofOfPossessionNonceResponseHeader)) {\n currentDatabaseElement.demonstratingProofOfPossessionNonce = response.headers.get(\n demonstratingProofOfPossessionNonceResponseHeader,\n );\n newHeaders.delete(demonstratingProofOfPossessionNonceResponseHeader);\n }\n\n return response.json().then<Response>((tokens: Tokens) => {\n const secureTokens = _hideTokens(tokens, currentDatabaseElement, configurationName);\n const body = JSON.stringify(secureTokens);\n return new Response(body, {\n status: response.status,\n statusText: response.statusText,\n headers: newHeaders,\n });\n });\n };\n}\n\nexport {\n b64DecodeUnicode,\n computeTimeLeft,\n isTokensValid,\n extractTokenPayload,\n isTokensOidcValid,\n hideTokens,\n _hideTokens,\n};\n","import { OidcConfig } from '../types';\nimport { sleep } from './sleep';\nimport { isTokensValid } from './tokens';\n\nexport const TOKEN_RENEWAL_TIMEOUT_MS = 5000;\nexport const TOKEN_RENEWAL_POLL_INTERVAL_MS = 200;\n\n/**\n * Polls until the tokens in the given config are valid, or until a timeout elapses.\n *\n * @returns `null` when tokens become valid; a synthetic 401 `Response` on timeout or\n * when the tokens are cleared while waiting (e.g. due to a parallel logout).\n */\nexport async function waitForValidTokens(\n config: OidcConfig,\n maxWaitMs = TOKEN_RENEWAL_TIMEOUT_MS,\n pollIntervalMs = TOKEN_RENEWAL_POLL_INTERVAL_MS,\n): Promise<Response | null> {\n const startTime = Date.now();\n while (config.tokens && !isTokensValid(config.tokens)) {\n if (Date.now() - startTime >= maxWaitMs) {\n return new Response(null, {\n status: 401,\n statusText: 'Token expired - service worker renewal timeout',\n });\n }\n await sleep(pollIntervalMs);\n }\n\n if (!config.tokens?.access_token) {\n return new Response(null, {\n status: 401,\n statusText: 'Missing access token',\n });\n }\n\n return null;\n}\n","import { Database, OidcConfig } from './types';\nimport { normalizeUrl } from './utils';\n\nconst getMatchingOidcConfigurations = (database: Database, url: string): OidcConfig[] => {\n return Object.values(database).filter(config => {\n const { oidcServerConfiguration } = config || {};\n const { tokenEndpoint, revocationEndpoint } = oidcServerConfiguration || {};\n\n const normalizedUrl = normalizeUrl(url);\n return (\n (tokenEndpoint && normalizedUrl.startsWith(normalizeUrl(tokenEndpoint))) ||\n (revocationEndpoint && normalizedUrl.startsWith(normalizeUrl(revocationEndpoint)))\n );\n });\n};\n\nexport { getMatchingOidcConfigurations as getCurrentDatabasesTokenEndpoint };\n","export function replaceCodeVerifier(codeVerifier: string, newCodeVerifier: string): string {\n const regex = /[?&]code_verifier=([^&]+)/i;\n return codeVerifier.replace(regex, `&code_verifier=${newCodeVerifier}`);\n}\n\nexport const extractConfigurationNameFromCodeVerifier = (chaine: string): string => {\n const regex = /[?&]code_verifier=CODE_VERIFIER_SECURED_BY_OIDC_SERVICE_WORKER_([^&]+)/;\n const match = chaine.match(regex);\n\n if (match && match.length > 0) {\n return decodeURIComponent(match[1]);\n } else {\n return '';\n }\n};\n","export default '7.27.2';\n","import { acceptAnyDomainToken, scriptFilename, TOKEN } from './constants';\nimport { base64urlOfHashOfASCIIEncodingAsync } from './crypto';\nimport { getDpopConfiguration, getDpopOnlyWhenDpopHeaderPresent } from './dpop';\nimport { generateJwkAsync, generateJwtDemonstratingProofOfPossessionAsync } from './jwt';\nimport { getCurrentDatabasesTokenEndpoint } from './oidcConfig';\nimport { Database, MessageEventData, OidcConfig, TrustedDomains } from './types';\nimport {\n checkDomain,\n getCurrentDatabaseDomain,\n getDomains,\n hideTokens,\n normalizeUrl,\n serializeHeaders,\n sleep,\n waitForValidTokens,\n} from './utils';\nimport {\n extractConfigurationNameFromCodeVerifier,\n replaceCodeVerifier,\n} from './utils/codeVerifier';\nimport version from './version';\n\n// @ts-ignore\nif (typeof trustedTypes !== 'undefined' && typeof trustedTypes.createPolicy === 'function') {\n // @ts-ignore\n trustedTypes.createPolicy('default', {\n createScriptURL: function (url: string) {\n if (url === scriptFilename) {\n return url;\n } else {\n throw new Error('Untrusted script URL blocked: ' + url);\n }\n },\n });\n}\n\nconst _self = self as ServiceWorkerGlobalScope & typeof globalThis;\n\n// `trustedDomains` is declared in the externally loaded script (OidcTrustedDomains.js)\ndeclare let trustedDomains: TrustedDomains;\n\n_self.importScripts(scriptFilename);\n\nconst id = Math.round(new Date().getTime() / 1000).toString();\nconsole.log('init service worker with id', id);\nconst keepAliveJsonFilename = 'OidcKeepAliveServiceWorker.json';\nconst database: Database = {};\n\n/**\n * Keeps the service worker alive by responding with a cached response after a sleep.\n */\nconst keepAliveAsync = async (event: FetchEvent) => {\n const originalRequest = event.request;\n const isFromVanilla = originalRequest.headers.has('oidc-vanilla');\n const init = { status: 200, statusText: 'oidc-service-worker' };\n const response = new Response('{}', init);\n\n if (!isFromVanilla) {\n const originalRequestUrl = new URL(originalRequest.url);\n const minSleepSeconds = Number(originalRequestUrl.searchParams.get('minSleepSeconds')) || 240;\n for (let i = 0; i < minSleepSeconds; i++) {\n await sleep(1000 + Math.floor(Math.random() * 1000));\n const cache = await caches.open('oidc_dummy_cache');\n await cache.put(event.request, response.clone());\n }\n }\n return response;\n};\n\n/**\n * Generates DPoP headers when a DPoP configuration is present.\n */\nasync function generateDpopAsync(\n originalRequest: Request,\n currentDatabase: OidcConfig | null,\n url: string,\n extrasClaims = {},\n) {\n const headersExtras = serializeHeaders(originalRequest.headers);\n if (\n currentDatabase?.demonstratingProofOfPossessionConfiguration &&\n currentDatabase.demonstratingProofOfPossessionJwkJson &&\n (!currentDatabase.demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent ||\n (currentDatabase.demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent &&\n headersExtras.dpop))\n ) {\n const dpopConfiguration = currentDatabase.demonstratingProofOfPossessionConfiguration;\n const jwk = currentDatabase.demonstratingProofOfPossessionJwkJson;\n const method = originalRequest.method;\n const dpop = await generateJwtDemonstratingProofOfPossessionAsync(self)(dpopConfiguration)(\n jwk,\n method,\n url,\n extrasClaims,\n );\n\n headersExtras.dpop = dpop;\n if (currentDatabase.demonstratingProofOfPossessionNonce != null) {\n headersExtras.nonce = currentDatabase.demonstratingProofOfPossessionNonce;\n }\n }\n return headersExtras;\n}\n\n/**\n * Intercepts fetch requests to inject access tokens and handle token endpoints.\n */\nconst handleFetch = (event: FetchEvent): void => {\n /**\n * Exit early for requests that do not need to have an auth token attached.\n */\n const bypassedDestinations = ['image', 'font', 'media', 'document', 'iframe', 'script'];\n if (bypassedDestinations.includes(event.request.destination)) {\n return; // Don't call event.respondWith() - let browser handle naturally\n }\n event.respondWith(\n (async (): Promise<Response> => {\n try {\n const originalRequest = event.request;\n const url = normalizeUrl(originalRequest.url);\n\n // 1) Handle keep-alive requests\n if (url.includes(keepAliveJsonFilename)) {\n return keepAliveAsync(event);\n }\n\n // Check if an access token is available for this request\n const currentDatabasesForRequestAccessToken = getCurrentDatabaseDomain(\n database,\n url,\n trustedDomains,\n );\n\n const authorization = originalRequest.headers.get('authorization');\n let authenticationMode = 'Bearer';\n let key = 'default';\n\n if (authorization) {\n const split = authorization.split(' ');\n authenticationMode = split[0];\n if (split[1]?.includes('ACCESS_TOKEN_SECURED_BY_OIDC_SERVICE_WORKER_')) {\n key = split[1].split('ACCESS_TOKEN_SECURED_BY_OIDC_SERVICE_WORKER_')[1];\n }\n }\n\n const currentDatabaseForRequestAccessToken = currentDatabasesForRequestAccessToken?.find(\n c => c.configurationName.endsWith(key),\n );\n\n // Inject the access token into the request if one is available\n if (currentDatabaseForRequestAccessToken?.tokens?.access_token) {\n // Wait for token to become valid (a parallel refresh may be in progress)\n const tokenError = await waitForValidTokens(currentDatabaseForRequestAccessToken);\n if (tokenError) {\n return tokenError;\n }\n\n // Adjust request mode for CORS if configured\n let requestMode = originalRequest.mode;\n if (\n originalRequest.mode !== 'navigate' &&\n currentDatabaseForRequestAccessToken.convertAllRequestsToCorsExceptNavigate\n ) {\n requestMode = 'cors';\n }\n\n // Build request headers\n let headers: { [p: string]: string };\n\n // Skip the access token for navigate requests when setAccessTokenToNavigateRequests is false\n if (\n originalRequest.mode === 'navigate' &&\n !currentDatabaseForRequestAccessToken.setAccessTokenToNavigateRequests\n ) {\n headers = {\n ...serializeHeaders(originalRequest.headers),\n };\n } else {\n if (\n authenticationMode.toLowerCase() === 'dpop' ||\n (!currentDatabaseForRequestAccessToken.demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent &&\n currentDatabaseForRequestAccessToken.demonstratingProofOfPossessionConfiguration)\n ) {\n // DPoP mode\n const claimsExtras = {\n ath: await base64urlOfHashOfASCIIEncodingAsync(\n currentDatabaseForRequestAccessToken.tokens.access_token,\n ),\n };\n const dpopHeaders = await generateDpopAsync(\n originalRequest,\n currentDatabaseForRequestAccessToken,\n url,\n claimsExtras,\n );\n headers = {\n ...dpopHeaders,\n authorization: `DPoP ${currentDatabaseForRequestAccessToken.tokens.access_token}`,\n };\n } else {\n // Bearer mode\n headers = {\n ...serializeHeaders(originalRequest.headers),\n authorization: `${authenticationMode} ${currentDatabaseForRequestAccessToken.tokens.access_token}`,\n };\n }\n }\n\n let init: RequestInit;\n if (originalRequest.mode === 'navigate') {\n init = {\n headers: headers,\n };\n } else {\n init = {\n headers: headers,\n mode: requestMode,\n };\n }\n\n const newRequest = new Request(originalRequest, init);\n return fetch(newRequest);\n }\n\n // Pass through non-POST requests without modification\n if (event.request.method !== 'POST') {\n return fetch(originalRequest);\n }\n\n // Handle POST requests to known token/revocation endpoints\n const currentDatabases = getCurrentDatabasesTokenEndpoint(database, url);\n const numberDatabase = currentDatabases.length;\n\n if (numberDatabase > 0) {\n const responsePromise = new Promise<Response>((resolve, reject) => {\n const clonedRequest = originalRequest.clone();\n clonedRequest\n .text()\n .then(async actualBody => {\n let currentDatabase: OidcConfig | null = null;\n try {\n // Replace hidden token placeholders with the real token values\n if (\n actualBody.includes(TOKEN.REFRESH_TOKEN) ||\n actualBody.includes(TOKEN.ACCESS_TOKEN)\n ) {\n let headers = serializeHeaders(originalRequest.headers);\n let newBody = actualBody;\n\n for (let i = 0; i < numberDatabase; i++) {\n const currentDb = currentDatabases[i];\n if (currentDb?.tokens) {\n const claimsExtras = {\n ath: await base64urlOfHashOfASCIIEncodingAsync(\n currentDb.tokens.access_token,\n ),\n };\n headers = await generateDpopAsync(\n originalRequest,\n currentDb,\n url,\n claimsExtras,\n );\n\n const keyRefreshToken = encodeURIComponent(\n `${TOKEN.REFRESH_TOKEN}_${currentDb.configurationName}`,\n );\n if (actualBody.includes(keyRefreshToken)) {\n newBody = newBody.replace(\n keyRefreshToken,\n encodeURIComponent(currentDb.tokens.refresh_token as string),\n );\n currentDatabase = currentDb;\n break;\n }\n\n const keyAccessToken = encodeURIComponent(\n `${TOKEN.ACCESS_TOKEN}_${currentDb.configurationName}`,\n );\n if (actualBody.includes(keyAccessToken)) {\n newBody = newBody.replace(\n keyAccessToken,\n encodeURIComponent(currentDb.tokens.access_token),\n );\n currentDatabase = currentDb;\n break;\n }\n }\n }\n\n const fetchPromise = fetch(originalRequest, {\n body: newBody,\n method: clonedRequest.method,\n headers,\n mode: clonedRequest.mode,\n cache: clonedRequest.cache,\n redirect: clonedRequest.redirect,\n referrer: clonedRequest.referrer,\n credentials: clonedRequest.credentials,\n integrity: clonedRequest.integrity,\n });\n\n // Forward revocation requests without modifying the response body\n if (\n currentDatabase?.oidcServerConfiguration?.revocationEndpoint &&\n url.startsWith(\n normalizeUrl(currentDatabase.oidcServerConfiguration.revocationEndpoint),\n )\n ) {\n const resp = await fetchPromise;\n const txt = await resp.text();\n resolve(new Response(txt, resp));\n return;\n }\n\n // Hide real token values in the response\n const hidden = await fetchPromise.then(\n hideTokens(currentDatabase as OidcConfig),\n );\n resolve(hidden);\n return;\n }\n\n // Handle authorization code exchange: replace the PKCE code_verifier placeholder\n const isCodeVerifier = actualBody.includes('code_verifier=');\n if (isCodeVerifier) {\n const currentLoginCallbackConfigurationName =\n extractConfigurationNameFromCodeVerifier(actualBody);\n if (\n !currentLoginCallbackConfigurationName ||\n currentLoginCallbackConfigurationName === ''\n ) {\n throw new Error('No configuration name found in code_verifier');\n }\n currentDatabase = database[currentLoginCallbackConfigurationName];\n let newBody = actualBody;\n const codeVerifier = currentDatabase.codeVerifier;\n if (codeVerifier != null) {\n newBody = replaceCodeVerifier(newBody, codeVerifier);\n }\n\n const headersExtras = await generateDpopAsync(\n originalRequest,\n currentDatabase,\n url,\n );\n const resp = await fetch(originalRequest, {\n body: newBody,\n method: clonedRequest.method,\n headers: headersExtras,\n mode: clonedRequest.mode,\n cache: clonedRequest.cache,\n redirect: clonedRequest.redirect,\n referrer: clonedRequest.referrer,\n credentials: clonedRequest.credentials,\n integrity: clonedRequest.integrity,\n });\n const hidden = await hideTokens(currentDatabase)(resp);\n resolve(hidden);\n return;\n }\n\n // Pass through all other POST requests unchanged\n const normalResp = await fetch(originalRequest, {\n body: actualBody,\n method: clonedRequest.method,\n headers: serializeHeaders(originalRequest.headers),\n mode: clonedRequest.mode,\n cache: clonedRequest.cache,\n redirect: clonedRequest.redirect,\n referrer: clonedRequest.referrer,\n credentials: clonedRequest.credentials,\n integrity: clonedRequest.integrity,\n });\n resolve(normalResp);\n } catch (err) {\n reject(err);\n }\n })\n .catch(reject);\n });\n\n return responsePromise;\n }\n\n // Default: pass through the request unchanged\n return fetch(originalRequest);\n } catch (err) {\n // Surface unexpected errors as a 500 rather than silently hanging\n console.error('[OidcServiceWorker] handleFetch error:', err);\n return new Response('Service Worker Error', { status: 500 });\n }\n })(),\n );\n};\n\nconst handleMessage = async (event: ExtendableMessageEvent) => {\n const port = event.ports[0];\n const data = event.data as MessageEventData;\n\n if (event.data?.type === 'SKIP_WAITING') {\n await _self.skipWaiting();\n port?.postMessage?.({});\n return;\n } else if (event.data.type === 'claim') {\n _self.clients.claim().then(() => port.postMessage({}));\n return;\n }\n\n const configurationName = data.configurationName.split('#')[0];\n\n if (trustedDomains == null) {\n trustedDomains = {};\n }\n\n const trustedDomain = trustedDomains[configurationName];\n const allowMultiTabLogin = Array.isArray(trustedDomain)\n ? false\n : trustedDomain.allowMultiTabLogin;\n\n const tabId = allowMultiTabLogin ? data.tabId : 'default';\n const configurationNameWithTabId = `${configurationName}#tabId=${tabId}`;\n\n let currentDatabase = database[configurationNameWithTabId];\n if (!currentDatabase) {\n const showAccessToken = Array.isArray(trustedDomain) ? false : trustedDomain.showAccessToken;\n const doNotSetAccessTokenToNavigateRequests = Array.isArray(trustedDomain)\n ? true\n : trustedDomain.setAccessTokenToNavigateRequests;\n const convertAllRequestsToCorsExceptNavigate = Array.isArray(trustedDomain)\n ? false\n : trustedDomain.convertAllRequestsToCorsExceptNavigate;\n\n database[configurationNameWithTabId] = {\n tokens: null,\n state: null,\n codeVerifier: null,\n oidcServerConfiguration: null,\n oidcConfiguration: undefined,\n nonce: null,\n status: null,\n configurationName: configurationNameWithTabId,\n hideAccessToken: !showAccessToken,\n setAccessTokenToNavigateRequests: doNotSetAccessTokenToNavigateRequests ?? true,\n convertAllRequestsToCorsExceptNavigate: convertAllRequestsToCorsExceptNavigate ?? false,\n demonstratingProofOfPossessionNonce: null,\n demonstratingProofOfPossessionJwkJson: null,\n demonstratingProofOfPossessionConfiguration: null,\n demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent: false,\n allowMultiTabLogin: allowMultiTabLogin ?? false,\n };\n currentDatabase = database[configurationNameWithTabId];\n\n if (!trustedDomains[configurationName]) {\n trustedDomains[configurationName] = [];\n }\n }\n\n switch (data.type) {\n case 'clear':\n currentDatabase.tokens = null;\n currentDatabase.state = null;\n currentDatabase.codeVerifier = null;\n currentDatabase.nonce = null;\n currentDatabase.demonstratingProofOfPossessionNonce = null;\n currentDatabase.demonstratingProofOfPossessionJwkJson = null;\n currentDatabase.demonstratingProofOfPossessionConfiguration = null;\n currentDatabase.demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent = false;\n currentDatabase.status = data.data.status;\n port.postMessage({ configurationName });\n return;\n\n case 'init': {\n const oidcServerConfiguration = data.data.oidcServerConfiguration;\n const domains = getDomains(trustedDomain, 'oidc');\n\n if (!domains.some(domain => domain === acceptAnyDomainToken)) {\n [\n oidcServerConfiguration.tokenEndpoint,\n oidcServerConfiguration.revocationEndpoint,\n oidcServerConfiguration.userInfoEndpoint,\n oidcServerConfiguration.issuer,\n ].forEach(u => {\n checkDomain(domains, u);\n });\n }\n\n currentDatabase.oidcServerConfiguration = oidcServerConfiguration;\n currentDatabase.oidcConfiguration = data.data.oidcConfiguration;\n\n if (currentDatabase.demonstratingProofOfPossessionConfiguration == null) {\n const demonstratingProofOfPossessionConfiguration = getDpopConfiguration(trustedDomain);\n if (demonstratingProofOfPossessionConfiguration != null) {\n if (currentDatabase.oidcConfiguration.demonstrating_proof_of_possession) {\n console.warn(\n 'In service worker, demonstrating_proof_of_possession must be configured from trustedDomains file',\n );\n }\n currentDatabase.demonstratingProofOfPossessionConfiguration =\n demonstratingProofOfPossessionConfiguration;\n currentDatabase.demonstratingProofOfPossessionJwkJson = await generateJwkAsync(self)(\n demonstratingProofOfPossessionConfiguration.generateKeyAlgorithm,\n );\n currentDatabase.demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent =\n getDpopOnlyWhenDpopHeaderPresent(trustedDomain) ?? false;\n }\n }\n\n if (!currentDatabase.tokens) {\n port.postMessage({\n tokens: null,\n status: currentDatabase.status,\n configurationName,\n version,\n });\n } else {\n const tokens = { ...currentDatabase.tokens };\n if (currentDatabase.hideAccessToken) {\n tokens.access_token = `${TOKEN.ACCESS_TOKEN}_${configurationName}#tabId=${tabId}`;\n }\n if (tokens.refresh_token) {\n tokens.refresh_token = `${TOKEN.REFRESH_TOKEN}_${configurationName}#tabId=${tabId}`;\n }\n if (tokens?.idTokenPayload?.nonce && currentDatabase.nonce != null) {\n tokens.idTokenPayload.nonce = `${TOKEN.NONCE_TOKEN}_${configurationName}#tabId=${tabId}`;\n }\n port.postMessage({\n tokens,\n status: currentDatabase.status,\n configurationName,\n version,\n });\n }\n return;\n }\n\n case 'setDemonstratingProofOfPossessionNonce': {\n currentDatabase.demonstratingProofOfPossessionNonce =\n data.data.demonstratingProofOfPossessionNonce;\n port.postMessage({ configurationName });\n return;\n }\n\n case 'getDemonstratingProofOfPossessionNonce': {\n const demonstratingProofOfPossessionNonce =\n currentDatabase.demonstratingProofOfPossessionNonce;\n port.postMessage({\n configurationName,\n demonstratingProofOfPossessionNonce,\n });\n return;\n }\n\n case 'setState': {\n currentDatabase.state = data.data.state;\n port.postMessage({ configurationName });\n return;\n }\n\n case 'getState': {\n const state = currentDatabase.state;\n port.postMessage({ configurationName, state });\n return;\n }\n\n case 'setCodeVerifier': {\n currentDatabase.codeVerifier = data.data.codeVerifier;\n port.postMessage({ configurationName });\n return;\n }\n\n case 'getCodeVerifier': {\n const codeVerifier =\n currentDatabase.codeVerifier != null\n ? `${TOKEN.CODE_VERIFIER}_${configurationName}#tabId=${tabId}`\n : null;\n port.postMessage({\n configurationName,\n codeVerifier,\n });\n return;\n }\n\n case 'setSessionState': {\n currentDatabase.sessionState = data.data.sessionState;\n port.postMessage({ configurationName });\n return;\n }\n\n case 'getSessionState': {\n const sessionState = currentDatabase.sessionState;\n port.postMessage({ configurationName, sessionState });\n return;\n }\n\n case 'setNonce': {\n const nonce = data.data.nonce;\n if (nonce) {\n currentDatabase.nonce = nonce;\n }\n port.postMessage({ configurationName });\n return;\n }\n\n case 'getNonce': {\n const keyNonce = `${TOKEN.NONCE_TOKEN}_${configurationName}#tabId=${tabId}`;\n const nonce = currentDatabase.nonce ? keyNonce : null;\n port.postMessage({ configurationName, nonce });\n return;\n }\n\n default:\n return;\n }\n};\n\n// Event listeners\n_self.addEventListener('fetch', handleFetch);\n_self.addEventListener('message', handleMessage);\n"],"names":["domain","database","trustedDomains","getCurrentDatabasesTokenEndpoint"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"waitForValidTokens.spec.d.ts","sourceRoot":"","sources":["../../../../src/utils/__tests__/waitForValidTokens.spec.ts"],"names":[],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { OidcConfig } from '../types';
|
|
2
|
+
export declare const TOKEN_RENEWAL_TIMEOUT_MS = 5000;
|
|
3
|
+
export declare const TOKEN_RENEWAL_POLL_INTERVAL_MS = 200;
|
|
4
|
+
/**
|
|
5
|
+
* Polls until the tokens in the given config are valid, or until a timeout elapses.
|
|
6
|
+
*
|
|
7
|
+
* @returns `null` when tokens become valid; a synthetic 401 `Response` on timeout or
|
|
8
|
+
* when the tokens are cleared while waiting (e.g. due to a parallel logout).
|
|
9
|
+
*/
|
|
10
|
+
export declare function waitForValidTokens(config: OidcConfig, maxWaitMs?: number, pollIntervalMs?: number): Promise<Response | null>;
|
|
11
|
+
//# sourceMappingURL=waitForValidTokens.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"waitForValidTokens.d.ts","sourceRoot":"","sources":["../../../src/utils/waitForValidTokens.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAItC,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAC7C,eAAO,MAAM,8BAA8B,MAAM,CAAC;AAElD;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,UAAU,EAClB,SAAS,SAA2B,EACpC,cAAc,SAAiC,GAC9C,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAoB1B"}
|
package/dist/src/version.d.ts
CHANGED
package/package.json
CHANGED
package/src/OidcServiceWorker.ts
CHANGED
|
@@ -9,10 +9,10 @@ import {
|
|
|
9
9
|
getCurrentDatabaseDomain,
|
|
10
10
|
getDomains,
|
|
11
11
|
hideTokens,
|
|
12
|
-
isTokensValid,
|
|
13
12
|
normalizeUrl,
|
|
14
13
|
serializeHeaders,
|
|
15
14
|
sleep,
|
|
15
|
+
waitForValidTokens,
|
|
16
16
|
} from './utils';
|
|
17
17
|
import {
|
|
18
18
|
extractConfigurationNameFromCodeVerifier,
|
|
@@ -36,7 +36,7 @@ if (typeof trustedTypes !== 'undefined' && typeof trustedTypes.createPolicy ===
|
|
|
36
36
|
|
|
37
37
|
const _self = self as ServiceWorkerGlobalScope & typeof globalThis;
|
|
38
38
|
|
|
39
|
-
//
|
|
39
|
+
// `trustedDomains` is declared in the externally loaded script (OidcTrustedDomains.js)
|
|
40
40
|
declare let trustedDomains: TrustedDomains;
|
|
41
41
|
|
|
42
42
|
_self.importScripts(scriptFilename);
|
|
@@ -47,7 +47,7 @@ const keepAliveJsonFilename = 'OidcKeepAliveServiceWorker.json';
|
|
|
47
47
|
const database: Database = {};
|
|
48
48
|
|
|
49
49
|
/**
|
|
50
|
-
*
|
|
50
|
+
* Keeps the service worker alive by responding with a cached response after a sleep.
|
|
51
51
|
*/
|
|
52
52
|
const keepAliveAsync = async (event: FetchEvent) => {
|
|
53
53
|
const originalRequest = event.request;
|
|
@@ -68,7 +68,7 @@ const keepAliveAsync = async (event: FetchEvent) => {
|
|
|
68
68
|
};
|
|
69
69
|
|
|
70
70
|
/**
|
|
71
|
-
*
|
|
71
|
+
* Generates DPoP headers when a DPoP configuration is present.
|
|
72
72
|
*/
|
|
73
73
|
async function generateDpopAsync(
|
|
74
74
|
originalRequest: Request,
|
|
@@ -103,8 +103,7 @@ async function generateDpopAsync(
|
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
/**
|
|
106
|
-
*
|
|
107
|
-
* On encapsule toute la logique dans un `respondWith((async () => { ... })())`.
|
|
106
|
+
* Intercepts fetch requests to inject access tokens and handle token endpoints.
|
|
108
107
|
*/
|
|
109
108
|
const handleFetch = (event: FetchEvent): void => {
|
|
110
109
|
/**
|
|
@@ -120,12 +119,12 @@ const handleFetch = (event: FetchEvent): void => {
|
|
|
120
119
|
const originalRequest = event.request;
|
|
121
120
|
const url = normalizeUrl(originalRequest.url);
|
|
122
121
|
|
|
123
|
-
// 1)
|
|
122
|
+
// 1) Handle keep-alive requests
|
|
124
123
|
if (url.includes(keepAliveJsonFilename)) {
|
|
125
124
|
return keepAliveAsync(event);
|
|
126
125
|
}
|
|
127
126
|
|
|
128
|
-
//
|
|
127
|
+
// Check if an access token is available for this request
|
|
129
128
|
const currentDatabasesForRequestAccessToken = getCurrentDatabaseDomain(
|
|
130
129
|
database,
|
|
131
130
|
url,
|
|
@@ -148,17 +147,15 @@ const handleFetch = (event: FetchEvent): void => {
|
|
|
148
147
|
c => c.configurationName.endsWith(key),
|
|
149
148
|
);
|
|
150
149
|
|
|
151
|
-
//
|
|
150
|
+
// Inject the access token into the request if one is available
|
|
152
151
|
if (currentDatabaseForRequestAccessToken?.tokens?.access_token) {
|
|
153
|
-
//
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
) {
|
|
158
|
-
await sleep(200);
|
|
152
|
+
// Wait for token to become valid (a parallel refresh may be in progress)
|
|
153
|
+
const tokenError = await waitForValidTokens(currentDatabaseForRequestAccessToken);
|
|
154
|
+
if (tokenError) {
|
|
155
|
+
return tokenError;
|
|
159
156
|
}
|
|
160
157
|
|
|
161
|
-
//
|
|
158
|
+
// Adjust request mode for CORS if configured
|
|
162
159
|
let requestMode = originalRequest.mode;
|
|
163
160
|
if (
|
|
164
161
|
originalRequest.mode !== 'navigate' &&
|
|
@@ -167,10 +164,10 @@ const handleFetch = (event: FetchEvent): void => {
|
|
|
167
164
|
requestMode = 'cors';
|
|
168
165
|
}
|
|
169
166
|
|
|
170
|
-
//
|
|
167
|
+
// Build request headers
|
|
171
168
|
let headers: { [p: string]: string };
|
|
172
169
|
|
|
173
|
-
//
|
|
170
|
+
// Skip the access token for navigate requests when setAccessTokenToNavigateRequests is false
|
|
174
171
|
if (
|
|
175
172
|
originalRequest.mode === 'navigate' &&
|
|
176
173
|
!currentDatabaseForRequestAccessToken.setAccessTokenToNavigateRequests
|
|
@@ -179,13 +176,12 @@ const handleFetch = (event: FetchEvent): void => {
|
|
|
179
176
|
...serializeHeaders(originalRequest.headers),
|
|
180
177
|
};
|
|
181
178
|
} else {
|
|
182
|
-
// On injecte le token
|
|
183
179
|
if (
|
|
184
180
|
authenticationMode.toLowerCase() === 'dpop' ||
|
|
185
181
|
(!currentDatabaseForRequestAccessToken.demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent &&
|
|
186
182
|
currentDatabaseForRequestAccessToken.demonstratingProofOfPossessionConfiguration)
|
|
187
183
|
) {
|
|
188
|
-
//
|
|
184
|
+
// DPoP mode
|
|
189
185
|
const claimsExtras = {
|
|
190
186
|
ath: await base64urlOfHashOfASCIIEncodingAsync(
|
|
191
187
|
currentDatabaseForRequestAccessToken.tokens.access_token,
|
|
@@ -202,7 +198,7 @@ const handleFetch = (event: FetchEvent): void => {
|
|
|
202
198
|
authorization: `DPoP ${currentDatabaseForRequestAccessToken.tokens.access_token}`,
|
|
203
199
|
};
|
|
204
200
|
} else {
|
|
205
|
-
//
|
|
201
|
+
// Bearer mode
|
|
206
202
|
headers = {
|
|
207
203
|
...serializeHeaders(originalRequest.headers),
|
|
208
204
|
authorization: `${authenticationMode} ${currentDatabaseForRequestAccessToken.tokens.access_token}`,
|
|
@@ -226,17 +222,16 @@ const handleFetch = (event: FetchEvent): void => {
|
|
|
226
222
|
return fetch(newRequest);
|
|
227
223
|
}
|
|
228
224
|
|
|
229
|
-
//
|
|
225
|
+
// Pass through non-POST requests without modification
|
|
230
226
|
if (event.request.method !== 'POST') {
|
|
231
227
|
return fetch(originalRequest);
|
|
232
228
|
}
|
|
233
229
|
|
|
234
|
-
//
|
|
230
|
+
// Handle POST requests to known token/revocation endpoints
|
|
235
231
|
const currentDatabases = getCurrentDatabasesTokenEndpoint(database, url);
|
|
236
232
|
const numberDatabase = currentDatabases.length;
|
|
237
233
|
|
|
238
234
|
if (numberDatabase > 0) {
|
|
239
|
-
// On gère tout dans une promesse
|
|
240
235
|
const responsePromise = new Promise<Response>((resolve, reject) => {
|
|
241
236
|
const clonedRequest = originalRequest.clone();
|
|
242
237
|
clonedRequest
|
|
@@ -244,7 +239,7 @@ const handleFetch = (event: FetchEvent): void => {
|
|
|
244
239
|
.then(async actualBody => {
|
|
245
240
|
let currentDatabase: OidcConfig | null = null;
|
|
246
241
|
try {
|
|
247
|
-
//
|
|
242
|
+
// Replace hidden token placeholders with the real token values
|
|
248
243
|
if (
|
|
249
244
|
actualBody.includes(TOKEN.REFRESH_TOKEN) ||
|
|
250
245
|
actualBody.includes(TOKEN.ACCESS_TOKEN)
|
|
@@ -305,21 +300,20 @@ const handleFetch = (event: FetchEvent): void => {
|
|
|
305
300
|
integrity: clonedRequest.integrity,
|
|
306
301
|
});
|
|
307
302
|
|
|
308
|
-
//
|
|
303
|
+
// Forward revocation requests without modifying the response body
|
|
309
304
|
if (
|
|
310
305
|
currentDatabase?.oidcServerConfiguration?.revocationEndpoint &&
|
|
311
306
|
url.startsWith(
|
|
312
307
|
normalizeUrl(currentDatabase.oidcServerConfiguration.revocationEndpoint),
|
|
313
308
|
)
|
|
314
309
|
) {
|
|
315
|
-
// On ne modifie pas le corps
|
|
316
310
|
const resp = await fetchPromise;
|
|
317
311
|
const txt = await resp.text();
|
|
318
312
|
resolve(new Response(txt, resp));
|
|
319
313
|
return;
|
|
320
314
|
}
|
|
321
315
|
|
|
322
|
-
//
|
|
316
|
+
// Hide real token values in the response
|
|
323
317
|
const hidden = await fetchPromise.then(
|
|
324
318
|
hideTokens(currentDatabase as OidcConfig),
|
|
325
319
|
);
|
|
@@ -327,7 +321,7 @@ const handleFetch = (event: FetchEvent): void => {
|
|
|
327
321
|
return;
|
|
328
322
|
}
|
|
329
323
|
|
|
330
|
-
//
|
|
324
|
+
// Handle authorization code exchange: replace the PKCE code_verifier placeholder
|
|
331
325
|
const isCodeVerifier = actualBody.includes('code_verifier=');
|
|
332
326
|
if (isCodeVerifier) {
|
|
333
327
|
const currentLoginCallbackConfigurationName =
|
|
@@ -366,7 +360,7 @@ const handleFetch = (event: FetchEvent): void => {
|
|
|
366
360
|
return;
|
|
367
361
|
}
|
|
368
362
|
|
|
369
|
-
//
|
|
363
|
+
// Pass through all other POST requests unchanged
|
|
370
364
|
const normalResp = await fetch(originalRequest, {
|
|
371
365
|
body: actualBody,
|
|
372
366
|
method: clonedRequest.method,
|
|
@@ -386,14 +380,13 @@ const handleFetch = (event: FetchEvent): void => {
|
|
|
386
380
|
.catch(reject);
|
|
387
381
|
});
|
|
388
382
|
|
|
389
|
-
// On renvoie simplement la promesse
|
|
390
383
|
return responsePromise;
|
|
391
384
|
}
|
|
392
385
|
|
|
393
|
-
//
|
|
386
|
+
// Default: pass through the request unchanged
|
|
394
387
|
return fetch(originalRequest);
|
|
395
388
|
} catch (err) {
|
|
396
|
-
//
|
|
389
|
+
// Surface unexpected errors as a 500 rather than silently hanging
|
|
397
390
|
console.error('[OidcServiceWorker] handleFetch error:', err);
|
|
398
391
|
return new Response('Service Worker Error', { status: 500 });
|
|
399
392
|
}
|
|
@@ -401,7 +394,6 @@ const handleFetch = (event: FetchEvent): void => {
|
|
|
401
394
|
);
|
|
402
395
|
};
|
|
403
396
|
|
|
404
|
-
// ---- Gestion des messages depuis la page
|
|
405
397
|
const handleMessage = async (event: ExtendableMessageEvent) => {
|
|
406
398
|
const port = event.ports[0];
|
|
407
399
|
const data = event.data as MessageEventData;
|
|
@@ -496,7 +488,6 @@ const handleMessage = async (event: ExtendableMessageEvent) => {
|
|
|
496
488
|
currentDatabase.oidcServerConfiguration = oidcServerConfiguration;
|
|
497
489
|
currentDatabase.oidcConfiguration = data.data.oidcConfiguration;
|
|
498
490
|
|
|
499
|
-
// Cas DPoP
|
|
500
491
|
if (currentDatabase.demonstratingProofOfPossessionConfiguration == null) {
|
|
501
492
|
const demonstratingProofOfPossessionConfiguration = getDpopConfiguration(trustedDomain);
|
|
502
493
|
if (demonstratingProofOfPossessionConfiguration != null) {
|
|
@@ -623,6 +614,6 @@ const handleMessage = async (event: ExtendableMessageEvent) => {
|
|
|
623
614
|
}
|
|
624
615
|
};
|
|
625
616
|
|
|
626
|
-
//
|
|
617
|
+
// Event listeners
|
|
627
618
|
_self.addEventListener('fetch', handleFetch);
|
|
628
619
|
_self.addEventListener('message', handleMessage);
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { waitForValidTokens } from '../waitForValidTokens';
|
|
4
|
+
import { OidcConfigBuilder, TokenBuilder } from './testHelper';
|
|
5
|
+
|
|
6
|
+
describe('waitForValidTokens', () => {
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
// Fake all timers including Date so that Date.now() advances with the clock
|
|
9
|
+
vi.useFakeTimers();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
vi.useRealTimers();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('returns null immediately when tokens are already valid', async () => {
|
|
17
|
+
const config = new OidcConfigBuilder()
|
|
18
|
+
.withTestingDefault()
|
|
19
|
+
.withTokens(new TokenBuilder().withNonExpiredToken().withAccessToken('valid-token').build())
|
|
20
|
+
.build();
|
|
21
|
+
|
|
22
|
+
const result = await waitForValidTokens(config);
|
|
23
|
+
|
|
24
|
+
expect(result).toBeNull();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('returns null when tokens become valid before the timeout', async () => {
|
|
28
|
+
const config = new OidcConfigBuilder()
|
|
29
|
+
.withTestingDefault()
|
|
30
|
+
.withTokens(new TokenBuilder().withExpiredToken().withAccessToken('expired-token').build())
|
|
31
|
+
.build();
|
|
32
|
+
|
|
33
|
+
const resultPromise = waitForValidTokens(config, 5000, 200);
|
|
34
|
+
|
|
35
|
+
// Let the first poll complete
|
|
36
|
+
await vi.advanceTimersByTimeAsync(200);
|
|
37
|
+
// Simulate a successful token renewal
|
|
38
|
+
config.tokens = new TokenBuilder()
|
|
39
|
+
.withNonExpiredToken()
|
|
40
|
+
.withAccessToken('renewed-token')
|
|
41
|
+
.build();
|
|
42
|
+
await vi.advanceTimersByTimeAsync(200);
|
|
43
|
+
|
|
44
|
+
const result = await resultPromise;
|
|
45
|
+
|
|
46
|
+
expect(result).toBeNull();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('returns 401 with timeout statusText when tokens remain expired past maxWaitMs', async () => {
|
|
50
|
+
const config = new OidcConfigBuilder()
|
|
51
|
+
.withTestingDefault()
|
|
52
|
+
.withTokens(new TokenBuilder().withExpiredToken().withAccessToken('expired-token').build())
|
|
53
|
+
.build();
|
|
54
|
+
|
|
55
|
+
const resultPromise = waitForValidTokens(config, 1000, 200);
|
|
56
|
+
|
|
57
|
+
// Advance past maxWaitMs=1000ms so the elapsed-time check triggers the timeout response
|
|
58
|
+
await vi.advanceTimersByTimeAsync(1200);
|
|
59
|
+
|
|
60
|
+
const result = await resultPromise;
|
|
61
|
+
|
|
62
|
+
expect(result).not.toBeNull();
|
|
63
|
+
expect(result?.status).toBe(401);
|
|
64
|
+
expect(result?.statusText).toBe('Token expired - service worker renewal timeout');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('returns 401 with missing-token statusText when tokens are cleared during the wait', async () => {
|
|
68
|
+
const config = new OidcConfigBuilder()
|
|
69
|
+
.withTestingDefault()
|
|
70
|
+
.withTokens(new TokenBuilder().withExpiredToken().withAccessToken('expired-token').build())
|
|
71
|
+
.build();
|
|
72
|
+
|
|
73
|
+
const resultPromise = waitForValidTokens(config, 5000, 200);
|
|
74
|
+
|
|
75
|
+
// Let the first poll complete while tokens are still expired
|
|
76
|
+
await vi.advanceTimersByTimeAsync(200);
|
|
77
|
+
// Simulate a parallel logout clearing the tokens
|
|
78
|
+
config.tokens = null;
|
|
79
|
+
// Let the second poll run so the loop can observe the cleared tokens
|
|
80
|
+
await vi.advanceTimersByTimeAsync(200);
|
|
81
|
+
|
|
82
|
+
const result = await resultPromise;
|
|
83
|
+
|
|
84
|
+
expect(result).not.toBeNull();
|
|
85
|
+
expect(result?.status).toBe(401);
|
|
86
|
+
expect(result?.statusText).toBe('Missing access token');
|
|
87
|
+
});
|
|
88
|
+
});
|
package/src/utils/index.ts
CHANGED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { OidcConfig } from '../types';
|
|
2
|
+
import { sleep } from './sleep';
|
|
3
|
+
import { isTokensValid } from './tokens';
|
|
4
|
+
|
|
5
|
+
export const TOKEN_RENEWAL_TIMEOUT_MS = 5000;
|
|
6
|
+
export const TOKEN_RENEWAL_POLL_INTERVAL_MS = 200;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Polls until the tokens in the given config are valid, or until a timeout elapses.
|
|
10
|
+
*
|
|
11
|
+
* @returns `null` when tokens become valid; a synthetic 401 `Response` on timeout or
|
|
12
|
+
* when the tokens are cleared while waiting (e.g. due to a parallel logout).
|
|
13
|
+
*/
|
|
14
|
+
export async function waitForValidTokens(
|
|
15
|
+
config: OidcConfig,
|
|
16
|
+
maxWaitMs = TOKEN_RENEWAL_TIMEOUT_MS,
|
|
17
|
+
pollIntervalMs = TOKEN_RENEWAL_POLL_INTERVAL_MS,
|
|
18
|
+
): Promise<Response | null> {
|
|
19
|
+
const startTime = Date.now();
|
|
20
|
+
while (config.tokens && !isTokensValid(config.tokens)) {
|
|
21
|
+
if (Date.now() - startTime >= maxWaitMs) {
|
|
22
|
+
return new Response(null, {
|
|
23
|
+
status: 401,
|
|
24
|
+
statusText: 'Token expired - service worker renewal timeout',
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
await sleep(pollIntervalMs);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!config.tokens?.access_token) {
|
|
31
|
+
return new Response(null, {
|
|
32
|
+
status: 401,
|
|
33
|
+
statusText: 'Missing access token',
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return null;
|
|
38
|
+
}
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export default '7.27.
|
|
1
|
+
export default '7.27.2';
|