From 729417b20a9bf8e36c7468bfccd956691356fab7 Mon Sep 17 00:00:00 2001 From: Thomas Darimont Date: Mon, 14 Oct 2024 23:29:00 +0200 Subject: [PATCH] Use account-console client for server-side auth check - Also generate PKCE verifier and use challenge parameters Signed-off-by: Thomas Darimont --- .../java/org/keycloak/AbstractOAuthClient.java | 9 +++++++++ .../resources/AbstractSecuredLocalService.java | 16 ++++++++++++++++ .../resources/account/AccountConsole.java | 3 ++- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/keycloak/AbstractOAuthClient.java b/core/src/main/java/org/keycloak/AbstractOAuthClient.java index 40ada8a3ee..4dced83b7b 100644 --- a/core/src/main/java/org/keycloak/AbstractOAuthClient.java +++ b/core/src/main/java/org/keycloak/AbstractOAuthClient.java @@ -42,6 +42,7 @@ public class AbstractOAuthClient { protected String stateCookiePath; protected boolean isSecure; protected boolean publicClient; + protected boolean pkceEnabled; protected String getStateCode() { return counter.getAndIncrement() + "/" + UUID.randomUUID().toString(); } @@ -126,6 +127,14 @@ public class AbstractOAuthClient { this.relativeUrlsUsed = relativeUrlsUsed; } + public boolean isPkceEnabled() { + return pkceEnabled; + } + + public void setPkceEnabled(boolean pkceEnabled) { + this.pkceEnabled = pkceEnabled; + } + protected String stripOauthParametersFromRedirect(String uri) { KeycloakUriBuilder builder = KeycloakUriBuilder.fromUri(uri) .replaceQueryParam(OAuth2Constants.CODE, null) diff --git a/services/src/main/java/org/keycloak/services/resources/AbstractSecuredLocalService.java b/services/src/main/java/org/keycloak/services/resources/AbstractSecuredLocalService.java index c804572101..662b478966 100755 --- a/services/src/main/java/org/keycloak/services/resources/AbstractSecuredLocalService.java +++ b/services/src/main/java/org/keycloak/services/resources/AbstractSecuredLocalService.java @@ -28,6 +28,7 @@ import org.keycloak.models.ClientModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.protocol.oidc.OIDCLoginProtocolService; +import org.keycloak.protocol.oidc.utils.PkceUtils; import org.keycloak.services.managers.Auth; import org.keycloak.services.messages.Messages; import org.keycloak.util.TokenUtil; @@ -44,6 +45,7 @@ import jakarta.ws.rs.core.UriBuilder; import jakarta.ws.rs.core.UriInfo; import java.net.URI; import java.util.Set; +import java.util.UUID; /** * Helper class for securing local services. Provides login basics as well as CSRF check basics @@ -180,6 +182,20 @@ public abstract class AbstractSecuredLocalService { .queryParam(OAuth2Constants.RESPONSE_TYPE, OAuth2Constants.CODE) .queryParam(OAuth2Constants.SCOPE, scopeParam); + if (isPkceEnabled()) { + String pkceChallenge; + try { + // TODO generate PKCE challenge based on server value + String codeVerifier = UUID.randomUUID().toString(); + pkceChallenge = PkceUtils.generateS256CodeChallenge(codeVerifier); + } catch (Exception e) { + throw new RuntimeException(e); + } + uriBuilder + .queryParam(OAuth2Constants.CODE_CHALLENGE, pkceChallenge) + .queryParam(OAuth2Constants.CODE_CHALLENGE_METHOD, OAuth2Constants.PKCE_METHOD_S256); + } + URI url = uriBuilder.build(); NewCookie cookie = new NewCookie(getStateCookieName(), state, getStateCookiePath(uriInfo), null, null, -1, isSecure, true); diff --git a/services/src/main/java/org/keycloak/services/resources/account/AccountConsole.java b/services/src/main/java/org/keycloak/services/resources/account/AccountConsole.java index fab68e688d..53031c5f35 100644 --- a/services/src/main/java/org/keycloak/services/resources/account/AccountConsole.java +++ b/services/src/main/java/org/keycloak/services/resources/account/AccountConsole.java @@ -215,7 +215,8 @@ public class AccountConsole implements AccountResourceProvider { var oauthRedirect = new AbstractSecuredLocalService.OAuthRedirect(); oauthRedirect.setAuthUrl(OIDCLoginProtocolService.authUrl(session.getContext().getUri()).build(realm.getName()).toString()); - oauthRedirect.setClientId(client.getClientId()); + oauthRedirect.setClientId(Constants.ACCOUNT_CONSOLE_CLIENT_ID); + oauthRedirect.setPkceEnabled(true); oauthRedirect.setSecure(realm.getSslRequired().isRequired(session.getContext().getConnection())); return oauthRedirect.redirect(session.getContext().getUri(), targetUri.toString()); }