URL encode forwarded parameters

Closes #41755

Signed-off-by: Pedro Igor <pigor.craveiro@gmail.com>
This commit is contained in:
Pedro Igor 2025-09-22 09:58:33 -03:00 committed by GitHub
parent 86516bb3dc
commit 19da322d88
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 47 additions and 21 deletions

View File

@ -83,8 +83,9 @@ import jakarta.ws.rs.core.UriBuilder;
import jakarta.ws.rs.core.UriInfo;
import java.io.IOException;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
@ -333,8 +334,9 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
.queryParam(OAUTH2_PARAMETER_RESPONSE_TYPE, "code")
.queryParam(OAUTH2_PARAMETER_CLIENT_ID, getConfig().getClientId())
.queryParam(OAUTH2_PARAMETER_REDIRECT_URI, request.getRedirectUri());
AuthenticationSessionModel authenticationSession = request.getAuthenticationSession();
String loginHint = authenticationSession.getClientNote(OIDCLoginProtocol.LOGIN_HINT_PARAM);
String loginHint = request.getAuthenticationSession().getClientNote(OIDCLoginProtocol.LOGIN_HINT_PARAM);
if (getConfig().isLoginHint() && loginHint != null) {
uriBuilder.queryParam(OIDCLoginProtocol.LOGIN_HINT_PARAM, loginHint);
}
@ -345,31 +347,19 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
String prompt = getConfig().getPrompt();
if (prompt == null || prompt.isEmpty()) {
prompt = request.getAuthenticationSession().getClientNote(OAuth2Constants.PROMPT);
prompt = authenticationSession.getClientNote(OAuth2Constants.PROMPT);
}
if (prompt != null) {
uriBuilder.queryParam(OAuth2Constants.PROMPT, prompt);
}
String acr = request.getAuthenticationSession().getClientNote(OAuth2Constants.ACR_VALUES);
if (acr != null) {
uriBuilder.queryParam(OAuth2Constants.ACR_VALUES, acr);
}
String forwardParameterConfig = getConfig().getForwardParameters() != null ? getConfig().getForwardParameters(): "";
List<String> forwardParameters = Arrays.asList(forwardParameterConfig.split("\\s*,\\s*"));
for(String forwardParameter: forwardParameters) {
String name = AuthorizationEndpoint.LOGIN_SESSION_NOTE_ADDITIONAL_REQ_PARAMS_PREFIX + forwardParameter.trim();
String parameter = request.getAuthenticationSession().getClientNote(name);
if(parameter != null && !parameter.isEmpty()) {
uriBuilder.queryParam(forwardParameter, parameter);
}
}
setForwardParameters(authenticationSession, uriBuilder);
if (getConfig().isPkceEnabled()) {
String codeVerifier = PkceUtils.generateCodeVerifier();
String codeChallengeMethod = getConfig().getPkceMethod();
request.getAuthenticationSession().setClientNote(BROKER_CODE_CHALLENGE_PARAM, codeVerifier);
request.getAuthenticationSession().setClientNote(BROKER_CODE_CHALLENGE_METHOD_PARAM, codeChallengeMethod);
authenticationSession.setClientNote(BROKER_CODE_CHALLENGE_PARAM, codeVerifier);
authenticationSession.setClientNote(BROKER_CODE_CHALLENGE_METHOD_PARAM, codeChallengeMethod);
String codeChallenge = PkceUtils.encodeCodeChallenge(codeVerifier, codeChallengeMethod);
uriBuilder.queryParam(OAuth2Constants.CODE_CHALLENGE, codeChallenge);
@ -379,6 +369,25 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
return uriBuilder;
}
private void setForwardParameters(AuthenticationSessionModel authenticationSession, UriBuilder uriBuilder) {
C config = getConfig();
String forwardParameterConfig = config.getForwardParameters() != null ? config.getForwardParameters(): OAuth2Constants.ACR_VALUES;
for (String forwardParameter: List.of(forwardParameterConfig.split("\\s*,\\s*"))) {
String name = AuthorizationEndpoint.LOGIN_SESSION_NOTE_ADDITIONAL_REQ_PARAMS_PREFIX + forwardParameter.trim();
String parameter = authenticationSession.getClientNote(name);
if (parameter == null) {
// try a value set as a client note
parameter = authenticationSession.getClientNote(forwardParameter);
}
if (parameter != null && !parameter.isEmpty()) {
uriBuilder.queryParam(forwardParameter, URLEncoder.encode(parameter, StandardCharsets.UTF_8));
}
}
}
/**
* Get JSON property as text. JSON numbers and booleans are converted to text. Empty string is converted to null.
*

View File

@ -8,11 +8,15 @@ import static org.keycloak.testsuite.broker.BrokerTestConstants.IDP_OIDC_PROVIDE
import static org.keycloak.testsuite.broker.BrokerTestTools.createIdentityProvider;
import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import org.keycloak.OAuth2Constants;
import org.keycloak.admin.client.resource.UsersResource;
import org.keycloak.models.IdentityProviderSyncMode;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.representations.idm.IdentityProviderRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.Assert;
@ -36,7 +40,7 @@ public class KcOidcBrokerParameterForwardTest extends AbstractBrokerTest {
IdentityProviderRepresentation idp = createIdentityProvider(IDP_OIDC_ALIAS, IDP_OIDC_PROVIDER_ID);
Map<String, String> config = idp.getConfig();
applyDefaultConfiguration(config, syncMode);
config.put("forwardParameters", FORWARDED_PARAMETER +", " + PARAMETER_NOT_SET);
config.put("forwardParameters", FORWARDED_PARAMETER +", " + PARAMETER_NOT_SET + ", " + OAuth2Constants.ACR_VALUES + ", " + OIDCLoginProtocol.CLAIMS_PARAM + ",forwarded_encoded");
return idp;
}
}
@ -46,7 +50,15 @@ public class KcOidcBrokerParameterForwardTest extends AbstractBrokerTest {
oauth.clientId("broker-app");
loginPage.open(bc.consumerRealmName());
String queryString = "&" + FORWARDED_PARAMETER + "=" + FORWARDED_PARAMETER_VALUE + "&" + PARAMETER_NOT_FORWARDED + "=" + "value";
String claimsValue = "{\"userinfo\":{\"http://itsme.services/v2/claim/BENationalNumber\":null}}";
String urlEncodedClaims = URLEncoder.encode(claimsValue, StandardCharsets.UTF_8);
String forwardedEncodedParam = "forwarded_encoded";
String forwardedEncodedParamValue = "encoded value";
String forwardedEncodedParamvalueEncoded = URLEncoder.encode(forwardedEncodedParamValue, StandardCharsets.UTF_8);
String queryString = "&" + FORWARDED_PARAMETER + "=" + FORWARDED_PARAMETER_VALUE + "&" + PARAMETER_NOT_FORWARDED + "=" + "value"
+ "&" + OAuth2Constants.ACR_VALUES + "=" + "phr"
+ "&" + OIDCLoginProtocol.CLAIMS_PARAM + "=" + urlEncodedClaims
+ "&" + forwardedEncodedParam + "=" + forwardedEncodedParamValue;
driver.navigate().to(driver.getCurrentUrl() + queryString);
log.debug("Clicking social " + bc.getIDPAlias());
@ -59,7 +71,12 @@ public class KcOidcBrokerParameterForwardTest extends AbstractBrokerTest {
assertThat(FORWARDED_PARAMETER + "=" + FORWARDED_PARAMETER_VALUE + " should be part of the url",
driver.getCurrentUrl(), containsString(FORWARDED_PARAMETER + "=" + FORWARDED_PARAMETER_VALUE));
assertThat(OAuth2Constants.ACR_VALUES + "=" + "phr" + " should be part of the url",
driver.getCurrentUrl(), containsString(OAuth2Constants.ACR_VALUES + "=" + "phr"));
assertThat(OIDCLoginProtocol.CLAIMS_PARAM + "=" + urlEncodedClaims + " should be part of the url",
driver.getCurrentUrl(), containsString(OIDCLoginProtocol.CLAIMS_PARAM + "=" + urlEncodedClaims));
assertThat(forwardedEncodedParam + "=" + forwardedEncodedParamValue + "should be part of the url",
driver.getCurrentUrl(), containsString(forwardedEncodedParam + "=" + URLEncoder.encode(forwardedEncodedParamvalueEncoded, StandardCharsets.UTF_8)));
assertThat("\"" + PARAMETER_NOT_SET + "\"" + " should NOT be part of the url",
driver.getCurrentUrl(), not(containsString(PARAMETER_NOT_SET)));