From b475f936d5c62c5ccea5d30f87aecd08a1c23c47 Mon Sep 17 00:00:00 2001 From: Jon Koops Date: Thu, 3 Oct 2024 17:07:57 +0200 Subject: [PATCH] Use `crypto.randomUUID()` to generate UUIDs for Keycloak JS (#33518) Closes #33515 Signed-off-by: Jon Koops --- .../topics/changes/changes-26_0_0.adoc | 4 ++-- js/libs/keycloak-js/lib/keycloak.js | 22 +++++++++---------- pnpm-lock.yaml | 3 +++ themes/package.json | 3 ++- themes/src/main/js/web-crypto-shim.js | 9 ++++++++ 5 files changed, 27 insertions(+), 14 deletions(-) diff --git a/docs/documentation/upgrading/topics/changes/changes-26_0_0.adoc b/docs/documentation/upgrading/topics/changes/changes-26_0_0.adoc index 920925ce32d..14719311300 100644 --- a/docs/documentation/upgrading/topics/changes/changes-26_0_0.adoc +++ b/docs/documentation/upgrading/topics/changes/changes-26_0_0.adoc @@ -418,7 +418,7 @@ const keycloak = new Keycloak('http://keycloak-server/path/to/keycloak.json'); == Methods for login are now `async` -Keycloak JS now utilizes the Web Crypto API to calculate the SHA-256 digests needed to support PKCE. Due to the asynchronous nature of this API the following public methods will now always return a `Promise`: +Keycloak JS now utilizes the Web Crypto API for various cryptographic functions. Due to the asynchronous nature of this API the following public methods will now always return a `Promise`: - `login()` - `createLoginUrl()` @@ -443,7 +443,7 @@ Make sure to update your code to `await` these methods. == A secure context is now required -Keycloak JS now requires a link:https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts[secure context] to run. The reason for this is that the library now uses the Web Crypto API to calculate the SHA-256 digests needed to support PKCE. This API is only available in secure contexts, which are contexts that are served over HTTPS, `localhost` or a `.localhost` domain. If you are using the library in a non-secure context you'll need to update your development environment to use a secure context. +Keycloak JS now requires a link:https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts[secure context] to run. The reason for this is that the library now uses the Web Crypto API for various cryptographic functions. This API is only available in secure contexts, which are contexts that are served over HTTPS, `localhost` or a `.localhost` domain. If you are using the library in a non-secure context you'll need to update your development environment to use a secure context. = Stricter startup behavior for build-time options diff --git a/js/libs/keycloak-js/lib/keycloak.js b/js/libs/keycloak-js/lib/keycloak.js index 9bc68233644..8a88d97289f 100755 --- a/js/libs/keycloak-js/lib/keycloak.js +++ b/js/libs/keycloak-js/lib/keycloak.js @@ -53,7 +53,11 @@ function Keycloak (config) { var logWarn = createLogger(console.warn); if (!globalThis.isSecureContext) { - logWarn('[KEYCLOAK] Keycloak JS should only be used in a secure context: https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts'); + logWarn( + "[KEYCLOAK] Keycloak JS must be used in a 'secure context' to function properly as it relies on browser APIs that are otherwise not available.\n" + + "Continuing to run your application insecurely will lead to unexpected behavior and breakage.\n\n" + + "For more information see: https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts" + ); } kc.init = function (initOptions) { @@ -341,9 +345,7 @@ function Keycloak (config) { throw new Error("Web Crypto API is not available."); } - const array = new Uint8Array(len); - crypto.getRandomValues(array); - return array; + return crypto.getRandomValues(new Uint8Array(len)); } function generateCodeVerifier(len) { @@ -1007,13 +1009,11 @@ function Keycloak (config) { } function createUUID() { - var hexDigits = '0123456789abcdef'; - var s = generateRandomString(36, hexDigits).split(""); - s[14] = '4'; - s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); - s[8] = s[13] = s[18] = s[23] = '-'; - var uuid = s.join(''); - return uuid; + if (typeof crypto === "undefined" || typeof crypto.randomUUID === "undefined") { + throw new Error("Web Crypto API is not available."); + } + + return crypto.randomUUID(); } function parseCallback(url) { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e3299444b43..26de3eaa178 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -473,6 +473,9 @@ importers: rfc4648: specifier: ^1.5.3 version: 1.5.3 + uuid: + specifier: ^10.0.0 + version: 10.0.0 devDependencies: '@rollup/plugin-commonjs': specifier: ^26.0.1 diff --git a/themes/package.json b/themes/package.json index 32801a3562c..f9ec82b5d66 100644 --- a/themes/package.json +++ b/themes/package.json @@ -14,7 +14,8 @@ "patternfly": "^3.59.5", "react": "^18.3.1", "react-dom": "^18.3.1", - "rfc4648": "^1.5.3" + "rfc4648": "^1.5.3", + "uuid": "^10.0.0" }, "devDependencies": { "@rollup/plugin-commonjs": "^26.0.1", diff --git a/themes/src/main/js/web-crypto-shim.js b/themes/src/main/js/web-crypto-shim.js index 3426f4ed28c..e582992e68e 100644 --- a/themes/src/main/js/web-crypto-shim.js +++ b/themes/src/main/js/web-crypto-shim.js @@ -1,4 +1,5 @@ import { sha256 } from '@noble/hashes/sha256'; +import { v4 as uuidv4 } from 'uuid'; // Shim for Web Crypto API specifically for Keycloak JS, as this API can sometimes be missing, for example in an insecure context: // https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts @@ -32,3 +33,11 @@ if (typeof crypto.getRandomValues === "undefined") { } }); } + +if (typeof crypto.randomUUID === "undefined") { + Object.defineProperty(crypto, "randomUUID", { + value: () => { + return uuidv4(); + } + }); +}