Enable PKCE by default for Keycloak JS (#26412)

Closes #26411

Signed-off-by: Jon Koops <jonkoops@gmail.com>
This commit is contained in:
Jon Koops 2024-01-23 14:04:13 +01:00 committed by GitHub
parent cc7d6a9b79
commit 5bf2d4b6ec
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 28 additions and 23 deletions

View file

@ -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}]. 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 = Truststore Improvements

View file

@ -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`. * 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`). * 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: * 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]. * 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. * 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]. * 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].

View file

@ -13,7 +13,6 @@ import { routes } from "./routes";
await Promise.all([ await Promise.all([
keycloak.init({ keycloak.init({
onLoad: "check-sso", onLoad: "check-sso",
pkceMethod: "S256",
}), }),
i18n.init(), i18n.init(),
]); ]);

View file

@ -11,7 +11,6 @@ export const keycloak = new Keycloak({
export async function initKeycloak() { export async function initKeycloak() {
const authenticated = await keycloak.init({ const authenticated = await keycloak.init({
onLoad: "check-sso", onLoad: "check-sso",
pkceMethod: "S256",
}); });
// Force the user to login if not authenticated. // Force the user to login if not authenticated.

View file

@ -22,7 +22,7 @@ export type KeycloakOnLoad = 'login-required'|'check-sso';
export type KeycloakResponseMode = 'query'|'fragment'; export type KeycloakResponseMode = 'query'|'fragment';
export type KeycloakResponseType = 'code'|'id_token token'|'code id_token token'; export type KeycloakResponseType = 'code'|'id_token token'|'code id_token token';
export type KeycloakFlow = 'standard'|'implicit'|'hybrid'; export type KeycloakFlow = 'standard'|'implicit'|'hybrid';
export type KeycloakPkceMethod = 'S256'; export type KeycloakPkceMethod = 'S256' | false;
export interface KeycloakConfig { export interface KeycloakConfig {
/** /**
@ -169,9 +169,8 @@ export interface KeycloakInitOptions {
flow?: KeycloakFlow; flow?: KeycloakFlow;
/** /**
* Configures the Proof Key for Code Exchange (PKCE) method to use. * Configures the Proof Key for Code Exchange (PKCE) method to use. This will default to 'S256'.
* The currently allowed method is 'S256'. * Can be disabled by passing `false`.
* If not configured, PKCE will not be used.
*/ */
pkceMethod?: KeycloakPkceMethod; pkceMethod?: KeycloakPkceMethod;

View file

@ -134,9 +134,11 @@ function Keycloak (config) {
if (initOptions.pkceMethod) { if (initOptions.pkceMethod) {
if (initOptions.pkceMethod !== "S256") { 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; kc.pkceMethod = initOptions.pkceMethod;
} else {
kc.pkceMethod = "S256";
} }
if (typeof initOptions.enableLogging === 'boolean') { if (typeof initOptions.enableLogging === 'boolean') {
@ -374,19 +376,18 @@ function Keycloak (config) {
} }
function generatePkceChallenge(pkceMethod, codeVerifier) { function generatePkceChallenge(pkceMethod, codeVerifier) {
switch (pkceMethod) { if (pkceMethod !== "S256") {
// The use of the "plain" method is considered insecure and therefore not supported. throw new TypeError(`Invalid value for 'pkceMethod', expected 'S256' but got '${pkceMethod}'.`);
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';
} }
// 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){ function buildClaimsParameter(requestedAcr){

View file

@ -127,7 +127,7 @@
realm: realm, realm: realm,
clientId: 'account-console' clientId: 'account-console'
}); });
keycloak.init({onLoad: 'check-sso', pkceMethod: 'S256', promiseType: 'native'}).then((authenticated) => { keycloak.init({onLoad: 'check-sso'}).then((authenticated) => {
isReactLoading = true; isReactLoading = true;
toggleReact(); toggleReact();
if (!keycloak.authenticated) { if (!keycloak.authenticated) {