Skip to content

🐞 Keycloak - Error when retrieving authorized elections when client secret has special characters - #2854

Open
xalsina-sequent wants to merge 3 commits into
mainfrom
fix/meta-1252/main
Open

🐞 Keycloak - Error when retrieving authorized elections when client secret has special characters#2854
xalsina-sequent wants to merge 3 commits into
mainfrom
fix/meta-1252/main

Conversation

@xalsina-sequent

Copy link
Copy Markdown
Contributor

Copilot AI review requested due to automatic review settings July 10, 2026 08:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes Keycloak client_credentials token retrieval failures when the client secret contains reserved application/x-www-form-urlencoded characters by centralizing token-request construction (with proper percent-encoding) into a shared oauth2-client module and updating Keycloak extensions to use it.

Changes:

  • Introduces packages/keycloak-extensions/oauth2-client with shared client_credentials token request + parsing utilities (and regression tests for reserved-character secrets).
  • Replaces duplicated token-request logic in voter-enrollment and conditional-authenticators with the shared client.
  • Wires the new module into Maven builds/shading and adds it as a dependency where needed.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
packages/keycloak-extensions/voter-enrollment/src/main/java/sequent/keycloak/voter_enrollment/LookupAndUpdateUser.java Switches token retrieval to the shared ClientCredentialsTokenClient.
packages/keycloak-extensions/voter-enrollment/pom.xml Adds dependency on the new oauth2-client module.
packages/keycloak-extensions/pom.xml Adds oauth2-client as a module and to the shaded artifact includes.
packages/keycloak-extensions/oauth2-client/src/main/java/sequent/keycloak/oauth2/ClientCredentialsTokenClient.java New shared implementation for form-encoding + token extraction.
packages/keycloak-extensions/oauth2-client/src/test/java/sequent/keycloak/oauth2/ClientCredentialsTokenClientTest.java New regression/unit tests covering encoding and token extraction failures.
packages/keycloak-extensions/oauth2-client/pom.xml New Maven module definition for oauth2-client.
packages/keycloak-extensions/conditional-authenticators/src/main/java/sequent/keycloak/protocol/oidc/mappers/AuthorizedElectionsUserAttributeMapper.java Switches token retrieval to the shared ClientCredentialsTokenClient.
packages/keycloak-extensions/conditional-authenticators/pom.xml Adds dependency on oauth2-client and ensures it is included in shading.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +36 to +53
<build>
<plugins>
<plugin>
<groupId>com.diffplug.spotless</groupId>
<artifactId>spotless-maven-plugin</artifactId>
<version>2.43.0</version>
<configuration>
<java>
<googleJavaFormat>
<version>1.23.0</version>
<style>GOOGLE</style>
</googleJavaFormat>
</java>
</configuration>
</plugin>
</plugins>
</build>

Comment on lines +46 to +48
String responseBody =
httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString()).join().body();
return extractAccessToken(responseBody);
Comment on lines +20 to +24
* Shared client for the OAuth2 client_credentials grant against a Keycloak realm's token
* endpoint. Extracted so the request-encoding and response-parsing logic exists in exactly one
* place; see meta#1252 for the incident (an unencoded client secret containing '%' broke
* application/x-www-form-urlencoded decoding) that motivated pulling this out of two separately
* copy-pasted implementations.
Comment on lines +59 to +63
IllegalStateException ex =
assertThrows(
IllegalStateException.class,
() -> ClientCredentialsTokenClient.extractAccessToken(responseBody));
assertEquals(true, ex.getMessage().contains("invalid_client"));
Comment on lines 319 to 324
try {
log.info("responseBody " + responseBody);
accessToken = JsonSerialization.readValue(responseBody, Map.class).get("access_token");
log.info("authenticate " + accessToken.toString());
return accessToken.toString();
return ClientCredentialsTokenClient.requestAccessToken(
client, url, this.clientId, this.clientSecret);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("Failed to parse Keycloak token response", e);
}
Comment on lines 996 to 1002
try {
log.info("responseBody " + responseBody);
accessToken = JsonSerialization.readValue(responseBody, Map.class).get("access_token");
log.info("authenticate " + accessToken.toString());
this.access_token = accessToken.toString();
this.access_token =
ClientCredentialsTokenClient.requestAccessToken(
client, url, this.clientId, this.clientSecret);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("Failed to parse Keycloak token response", e);
}
Comment on lines 19 to 22
<modules>
<module>aws-ses-email-sender-provider</module>
<module>oauth2-client</module>
<module>conditional-authenticators</module>
xalsina-sequent and others added 2 commits July 15, 2026 10:11
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants