diff --git a/docs/documentation/release_notes/topics/24_0_0.adoc b/docs/documentation/release_notes/topics/24_0_0.adoc index c7bc73b9fd..1eb5706c79 100644 --- a/docs/documentation/release_notes/topics/24_0_0.adoc +++ b/docs/documentation/release_notes/topics/24_0_0.adoc @@ -6,9 +6,15 @@ image::images/new-welcome-screen.png["A screenshot of the new welcome page, show If you are using a custom theme, you may need to update it to support the new welcome page. For more details consult the link:{upgradingguide_link}[{upgradingguide_name}]. -= Keycloak JS using `exports` field += Keycloak JS -The Keycloak JS adapter now uses the https://webpack.js.org/guides/package-exports/[`exports` field] in `package.json`. This change improves support for more modern bundlers like Webpack 5 and Vite, but comes with some unavoidable breaking changes. Consult the link:{upgradingguide_link}[{upgradingguide_name}] for more details. +== Using `exports` field in `package.json` + +The Keycloak JS adapter now uses the https://webpack.js.org/guides/package-exports/[`exports` field] in its `package.json`. This change improves support for more modern bundlers like Webpack 5 and Vite, but comes with some unavoidable breaking changes. Consult the link:{upgradingguide_link}[{upgradingguide_name}] for more details. + +== PKCE enabled by default + +The Keycloak JS adapter now sets the `pkceMethod` option to `S256` by default. This enables Proof Key Code Exchange (https://datatracker.ietf.org/doc/html/rfc7636[PKCE]) for all applications using the adapter. If you are using the adapter on a system that doesn't support PKCE, you can set the `pkceMethod` option to `false` to disable it. = Truststore Improvements diff --git a/docs/documentation/securing_apps/topics/oidc/javascript-adapter.adoc b/docs/documentation/securing_apps/topics/oidc/javascript-adapter.adoc index 4fc32d9027..b71becc0a1 100644 --- a/docs/documentation/securing_apps/topics/oidc/javascript-adapter.adoc +++ b/docs/documentation/securing_apps/topics/oidc/javascript-adapter.adoc @@ -369,7 +369,8 @@ to {project_name} will contain the scope parameter `scope=openid address phone`. * flow - Set the OpenID Connect flow. Valid values are `standard`, `implicit` or `hybrid`. * enableLogging - Enables logging messages from Keycloak to the console (default is `false`). * pkceMethod - The method for Proof Key Code Exchange (https://datatracker.ietf.org/doc/html/rfc7636[PKCE]) to use. Configuring this value enables the PKCE mechanism. Available options: - - "S256" - The SHA256 based PKCE method + - "S256" - The SHA256 based PKCE method (default) + - false - PKCE is disabled. * acrValues - Generates the `acr_values` parameter which refers to authentication context class reference and allows clients to declare the required assurance level requirements, e.g. authentication mechanisms. See https://openid.net/specs/openid-connect-modrna-authentication-1_0.html#acr_values[Section 4. acr_values request values and level of assurance in OpenID Connect MODRNA Authentication Profile 1.0]. * messageReceiveTimeout - Set a timeout in milliseconds for waiting for message responses from the Keycloak server. This is used, for example, when waiting for a message during 3rd party cookies check. The default value is 10000. * locale - When onLoad is 'login-required', sets the 'ui_locales' query param in compliance with https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest[section 3.1.2.1 of the OIDC 1.0 specification]. diff --git a/js/apps/account-ui/src/main.tsx b/js/apps/account-ui/src/main.tsx index beb3ee0829..2e0dec914c 100644 --- a/js/apps/account-ui/src/main.tsx +++ b/js/apps/account-ui/src/main.tsx @@ -13,7 +13,6 @@ import { routes } from "./routes"; await Promise.all([ keycloak.init({ onLoad: "check-sso", - pkceMethod: "S256", }), i18n.init(), ]); diff --git a/js/apps/admin-ui/src/keycloak.ts b/js/apps/admin-ui/src/keycloak.ts index bb89ec30cc..d8fe5d3a17 100644 --- a/js/apps/admin-ui/src/keycloak.ts +++ b/js/apps/admin-ui/src/keycloak.ts @@ -11,7 +11,6 @@ export const keycloak = new Keycloak({ export async function initKeycloak() { const authenticated = await keycloak.init({ onLoad: "check-sso", - pkceMethod: "S256", }); // Force the user to login if not authenticated. diff --git a/js/libs/keycloak-js/dist/keycloak.d.ts b/js/libs/keycloak-js/dist/keycloak.d.ts index 25554c8e53..6478a9eb40 100644 --- a/js/libs/keycloak-js/dist/keycloak.d.ts +++ b/js/libs/keycloak-js/dist/keycloak.d.ts @@ -22,7 +22,7 @@ export type KeycloakOnLoad = 'login-required'|'check-sso'; export type KeycloakResponseMode = 'query'|'fragment'; export type KeycloakResponseType = 'code'|'id_token token'|'code id_token token'; export type KeycloakFlow = 'standard'|'implicit'|'hybrid'; -export type KeycloakPkceMethod = 'S256'; +export type KeycloakPkceMethod = 'S256' | false; export interface KeycloakConfig { /** @@ -169,9 +169,8 @@ export interface KeycloakInitOptions { flow?: KeycloakFlow; /** - * Configures the Proof Key for Code Exchange (PKCE) method to use. - * The currently allowed method is 'S256'. - * If not configured, PKCE will not be used. + * Configures the Proof Key for Code Exchange (PKCE) method to use. This will default to 'S256'. + * Can be disabled by passing `false`. */ pkceMethod?: KeycloakPkceMethod; diff --git a/js/libs/keycloak-js/src/keycloak.js b/js/libs/keycloak-js/src/keycloak.js index c93cddf969..acee5a20c8 100755 --- a/js/libs/keycloak-js/src/keycloak.js +++ b/js/libs/keycloak-js/src/keycloak.js @@ -134,9 +134,11 @@ function Keycloak (config) { if (initOptions.pkceMethod) { if (initOptions.pkceMethod !== "S256") { - throw 'Invalid value for pkceMethod'; + throw new TypeError(`Invalid value for 'pkceMethod', expected 'S256' but got '${initOptions.pkceMethod}'.`); } kc.pkceMethod = initOptions.pkceMethod; + } else { + kc.pkceMethod = "S256"; } if (typeof initOptions.enableLogging === 'boolean') { @@ -374,19 +376,18 @@ function Keycloak (config) { } function generatePkceChallenge(pkceMethod, codeVerifier) { - switch (pkceMethod) { - // The use of the "plain" method is considered insecure and therefore not supported. - case "S256": - // hash codeVerifier, then encode as url-safe base64 without padding - var hashBytes = new Uint8Array(sha256.arrayBuffer(codeVerifier)); - var encodedHash = bytesToBase64(hashBytes) - .replace(/\+/g, '-') - .replace(/\//g, '_') - .replace(/\=/g, ''); - return encodedHash; - default: - throw 'Invalid value for pkceMethod'; + if (pkceMethod !== "S256") { + throw new TypeError(`Invalid value for 'pkceMethod', expected 'S256' but got '${pkceMethod}'.`); } + + // hash codeVerifier, then encode as url-safe base64 without padding + const hashBytes = new Uint8Array(sha256.arrayBuffer(codeVerifier)); + const encodedHash = bytesToBase64(hashBytes) + .replace(/\+/g, '-') + .replace(/\//g, '_') + .replace(/\=/g, ''); + + return encodedHash; } function buildClaimsParameter(requestedAcr){ diff --git a/themes/src/main/resources/theme/keycloak.v2/account/index.ftl b/themes/src/main/resources/theme/keycloak.v2/account/index.ftl index b39e2bb197..aeb1544b9e 100644 --- a/themes/src/main/resources/theme/keycloak.v2/account/index.ftl +++ b/themes/src/main/resources/theme/keycloak.v2/account/index.ftl @@ -127,7 +127,7 @@ realm: realm, clientId: 'account-console' }); - keycloak.init({onLoad: 'check-sso', pkceMethod: 'S256', promiseType: 'native'}).then((authenticated) => { + keycloak.init({onLoad: 'check-sso'}).then((authenticated) => { isReactLoading = true; toggleReact(); if (!keycloak.authenticated) {