diff --git a/docs/documentation/topics/templates/document-attributes.adoc b/docs/documentation/topics/templates/document-attributes.adoc
index 43cd7961476..3cb8a7a5ca9 100644
--- a/docs/documentation/topics/templates/document-attributes.adoc
+++ b/docs/documentation/topics/templates/document-attributes.adoc
@@ -54,6 +54,8 @@
:adminguide_clearcache_link: {adminguide_link}#_clear-cache
:apidocs_name: API Documentation
:apidocs_link: https://www.keycloak.org/docs/{project_version}/api_documentation/
+:allproviderconfigguide_name: All provider configuration Guide
+:allproviderconfigguide_link: https://www.keycloak.org/server/all-provider-config
:bootstrapadminrecovery_name: Admin Bootstrap and Recovery
:bootstrapadminrecovery_link: https://www.keycloak.org/server/bootstrap-admin-recovery
:client_certificate_lookup_link: https://www.keycloak.org/server/reverseproxy#_enabling_client_certificate_lookup
diff --git a/docs/documentation/upgrading/topics/changes/changes-26_0_16.adoc b/docs/documentation/upgrading/topics/changes/changes-26_0_16.adoc
new file mode 100644
index 00000000000..416b7cf2261
--- /dev/null
+++ b/docs/documentation/upgrading/topics/changes/changes-26_0_16.adoc
@@ -0,0 +1,14 @@
+// ------------------------ Notable changes ------------------------ //
+== Notable changes
+
+Notable changes where an internal behavior changed to prevent common misconfigurations, fix bugs or simplify running {project_name}.
+
+=== Maximum length of the parameters in the OIDC authentication request
+
+When the OIDC authentication request (or OAuth2 authorization request) is sent, there is now limit for the maximum length of every standard OIDC/OAuth2 parameter. The maximum length of each standard parameter is 4000 characters,
+which is very big number and can be lowered in the future releases. For now, it is kept big for the backwards compatibility. The only exception is the `login_hint` parameter, which is limited
+to the maximum length of 255 characters. This is aligned with the maximum length for the `username` and `email` attributes configured in the default user profile configuration.
+
+If you want to make those number higher or lower, you can start the server with the option `req-params-default-max-size` for the default maximum length of the standard
+OIDC/OAuth2 parameters or you can use something like `req-params-max-size` for one specific parameter. See the `login-protocol` provider configuration
+of the link:{allproviderconfigguide_link}[{allproviderconfigguide_name}] for more details.
diff --git a/docs/documentation/upgrading/topics/changes/changes.adoc b/docs/documentation/upgrading/topics/changes/changes.adoc
index 55a62914d08..fa057cf281c 100644
--- a/docs/documentation/upgrading/topics/changes/changes.adoc
+++ b/docs/documentation/upgrading/topics/changes/changes.adoc
@@ -1,7 +1,9 @@
[[migration-changes]]
== Migration Changes
-=== Migrating to 26.0.13
+=== Migrating to 26.0.16
+
+include::changes-26_0_16.adoc[leveloffset=2]
include::changes-26_0_13.adoc[leveloffset=2]
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java
index 5ddbc97e232..0a311b44181 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java
@@ -46,6 +46,8 @@ import org.keycloak.protocol.oidc.mappers.UserPropertyMapper;
import org.keycloak.protocol.oidc.mappers.UserRealmRoleMappingMapper;
import org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper;
import org.keycloak.protocol.oidc.mappers.SubMapper;
+import org.keycloak.provider.ProviderConfigProperty;
+import org.keycloak.provider.ProviderConfigurationBuilder;
import org.keycloak.representations.IDToken;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.services.ServicesLogger;
@@ -53,11 +55,17 @@ import org.keycloak.services.managers.AuthenticationManager;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.keycloak.models.ImpersonationSessionNote.IMPERSONATOR_ID;
import static org.keycloak.models.ImpersonationSessionNote.IMPERSONATOR_USERNAME;
+import static org.keycloak.protocol.oidc.OIDCProviderConfig.DEFAULT_ADDITIONAL_REQ_PARAMS_FAIL_FAST;
+import static org.keycloak.protocol.oidc.OIDCProviderConfig.DEFAULT_ADDITIONAL_REQ_PARAMS_MAX_NUMBER;
+import static org.keycloak.protocol.oidc.OIDCProviderConfig.DEFAULT_ADDITIONAL_REQ_PARAMS_MAX_OVERALL_SIZE;
+import static org.keycloak.protocol.oidc.OIDCProviderConfig.DEFAULT_ADDITIONAL_REQ_PARAMS_MAX_SIZE;
+import static org.keycloak.protocol.oidc.OIDCProviderConfig.DEFAULT_REQ_PARAMS_DEFAULT_MAX_SIZE;
/**
* @author Bill Burke
@@ -109,10 +117,12 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
public static final String ROLES_SCOPE_CONSENT_TEXT = "${rolesScopeConsentText}";
public static final String ORGANIZATION_SCOPE_CONSENT_TEXT = "${organizationScopeConsentText}";
- public static final String CONFIG_OIDC_REQ_PARAMS_MAX_NUMBER = "add-req-params-max-number";
- public static final String CONFIG_OIDC_REQ_PARAMS_MAX_SIZE = "add-req-params-max-size";
- public static final String CONFIG_OIDC_REQ_PARAMS_MAX_OVERALL_SIZE = "add-req-params-max-overall-size";
- public static final String CONFIG_OIDC_REQ_PARAMS_FAIL_FAST = "add-req-params-fail-fast";
+ public static final String CONFIG_OIDC_REQ_PARAMS_DEFAULT_MAX_SIZE = "req-params-default-max-size";
+ public static final String CONFIG_OIDC_REQ_PARAMS_MAX_SIZE_PREFIX = "req-params-max-size";
+ public static final String CONFIG_OIDC_ADD_REQ_PARAMS_MAX_NUMBER = "add-req-params-max-number";
+ public static final String CONFIG_OIDC_ADD_REQ_PARAMS_MAX_SIZE = "add-req-params-max-size";
+ public static final String CONFIG_OIDC_ADD_REQ_PARAMS_MAX_OVERALL_SIZE = "add-req-params-max-overall-size";
+ public static final String CONFIG_OIDC_ADD_REQ_PARAMS_FAIL_FAST = "add-req-params-fail-fast";
private OIDCProviderConfig providerConfig;
@@ -500,4 +510,48 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
}
}
+ @Override
+ public List getConfigMetadata() {
+ return ProviderConfigurationBuilder.create()
+ .property()
+ .name(CONFIG_OIDC_REQ_PARAMS_DEFAULT_MAX_SIZE)
+ .type("int")
+ .helpText("Maximum default length of the standard OIDC parameter sent to the OIDC authentication request. This applies to most of the standard parameters like for example 'state', 'nonce' etc." +
+ " The exception is 'login_hint' parameter, which has maximum length of 255 characters.")
+ .defaultValue(DEFAULT_REQ_PARAMS_DEFAULT_MAX_SIZE)
+ .add()
+ .property()
+ .name(CONFIG_OIDC_REQ_PARAMS_MAX_SIZE_PREFIX + "--" + OIDCLoginProtocol.LOGIN_HINT_PARAM)
+ .type("int")
+ .helpText("Maximum length of the standard OIDC authentication request parameter overriden for the specified parameter. Useful if some standard OIDC parameter should have different limit than '" + CONFIG_OIDC_REQ_PARAMS_DEFAULT_MAX_SIZE +
+ "'. It is needed to add the name of the parameter after this prefix into the configuration. In this example, the '" + OIDCLoginProtocol.LOGIN_HINT_PARAM + "' parameter is used, but this format is supported for any known standard OIDC/OAuth2 parameter.")
+ .add()
+ .property()
+ .name(CONFIG_OIDC_ADD_REQ_PARAMS_MAX_NUMBER)
+ .type("int")
+ .helpText("Maximum number of additional request parameters sent to the OIDC authentication request. As 'additional request parameter' is meant some custom parameter not directly treated as standard OIDC/OAuth2 protocol parameter. Additional parameters might be useful for example to add custom claims to the OIDC token (in case that also particular protocol mappers are configured).")
+ .defaultValue(DEFAULT_ADDITIONAL_REQ_PARAMS_MAX_NUMBER)
+ .add()
+ .property()
+ .name(CONFIG_OIDC_ADD_REQ_PARAMS_MAX_SIZE)
+ .type("int")
+ .helpText("Maximum size of single additional request parameter value See '" + CONFIG_OIDC_ADD_REQ_PARAMS_MAX_NUMBER + "' for more details about additional request parameters")
+ .defaultValue(DEFAULT_ADDITIONAL_REQ_PARAMS_MAX_SIZE)
+ .add()
+ .property()
+ .name(CONFIG_OIDC_ADD_REQ_PARAMS_MAX_OVERALL_SIZE)
+ .type("int")
+ .helpText("Maximum size of all additional request parameters values together. See '" + CONFIG_OIDC_ADD_REQ_PARAMS_MAX_NUMBER + "' for more details about additional request parameters")
+ .defaultValue(DEFAULT_ADDITIONAL_REQ_PARAMS_MAX_OVERALL_SIZE)
+ .add()
+ .property()
+ .name(CONFIG_OIDC_ADD_REQ_PARAMS_FAIL_FAST)
+ .type("boolean")
+ .helpText("Whether the fail-fast strategy should be enforced in case if the limit for some standard OIDC parameter or additional OIDC parameter is not met for the parameters sent to the OIDC authentication request." +
+ " If false, then all additional request parameters to not meet the configuration are silently ignored. If true, an exception will be raised and OIDC authentication request will not be allowed.")
+ .defaultValue(DEFAULT_ADDITIONAL_REQ_PARAMS_FAIL_FAST)
+ .add()
+ .build();
+ }
+
}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCProviderConfig.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCProviderConfig.java
index 3f542c13a73..f9bd4e6322e 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCProviderConfig.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCProviderConfig.java
@@ -1,5 +1,7 @@
package org.keycloak.protocol.oidc;
+import java.util.Map;
+
import org.keycloak.Config;
/**
@@ -7,6 +9,24 @@ import org.keycloak.Config;
*/
public class OIDCProviderConfig {
+ private final Config.Scope config;
+
+ /**
+ * Maximum default length of the standard OIDC parameter sent to the OIDC authentication request.
+ */
+ public static final int DEFAULT_REQ_PARAMS_DEFAULT_MAX_SIZE = 4000;
+
+ private final int reqParamsDefaultMaxSize;
+
+ /**
+ * Overriden values for maximum sizes of specified standard OIDC parameters. The value for the specified parameter can be still overriden
+ * by administrator in the configuration of the {@link OIDCLoginProtocolFactory}. In case that value is not overriden in the configuration or in this map,
+ * then the value specified by the {@link OIDCLoginProtocolFactory#CONFIG_OIDC_REQ_PARAMS_DEFAULT_MAX_SIZE} is used
+ */
+ private Map DEFAULT_MAX_PARAMS_SIZES = Map.of(
+ OIDCLoginProtocol.LOGIN_HINT_PARAM, 255 // Aligned with user-profile configuration for username and email
+ );
+
/**
* Default value for {@link #additionalReqParamsMaxNumber} if case no configuration property is set.
*/
@@ -50,10 +70,12 @@ public class OIDCProviderConfig {
public OIDCProviderConfig(Config.Scope config) {
- this.additionalReqParamsMaxNumber = config.getInt(OIDCLoginProtocolFactory.CONFIG_OIDC_REQ_PARAMS_MAX_NUMBER, DEFAULT_ADDITIONAL_REQ_PARAMS_MAX_NUMBER);
- this.additionalReqParamsMaxSize = config.getInt(OIDCLoginProtocolFactory.CONFIG_OIDC_REQ_PARAMS_MAX_SIZE, DEFAULT_ADDITIONAL_REQ_PARAMS_MAX_SIZE);
- this.additionalReqParamsMaxOverallSize = config.getInt(OIDCLoginProtocolFactory.CONFIG_OIDC_REQ_PARAMS_MAX_OVERALL_SIZE, DEFAULT_ADDITIONAL_REQ_PARAMS_MAX_OVERALL_SIZE);
- this.additionalReqParamsFailFast = config.getBoolean(OIDCLoginProtocolFactory.CONFIG_OIDC_REQ_PARAMS_FAIL_FAST, DEFAULT_ADDITIONAL_REQ_PARAMS_FAIL_FAST);
+ this.config = config;
+ this.reqParamsDefaultMaxSize = config.getInt(OIDCLoginProtocolFactory.CONFIG_OIDC_REQ_PARAMS_DEFAULT_MAX_SIZE, DEFAULT_REQ_PARAMS_DEFAULT_MAX_SIZE);
+ this.additionalReqParamsMaxNumber = config.getInt(OIDCLoginProtocolFactory.CONFIG_OIDC_ADD_REQ_PARAMS_MAX_NUMBER, DEFAULT_ADDITIONAL_REQ_PARAMS_MAX_NUMBER);
+ this.additionalReqParamsMaxSize = config.getInt(OIDCLoginProtocolFactory.CONFIG_OIDC_ADD_REQ_PARAMS_MAX_SIZE, DEFAULT_ADDITIONAL_REQ_PARAMS_MAX_SIZE);
+ this.additionalReqParamsMaxOverallSize = config.getInt(OIDCLoginProtocolFactory.CONFIG_OIDC_ADD_REQ_PARAMS_MAX_OVERALL_SIZE, DEFAULT_ADDITIONAL_REQ_PARAMS_MAX_OVERALL_SIZE);
+ this.additionalReqParamsFailFast = config.getBoolean(OIDCLoginProtocolFactory.CONFIG_OIDC_ADD_REQ_PARAMS_FAIL_FAST, DEFAULT_ADDITIONAL_REQ_PARAMS_FAIL_FAST);
}
public int getAdditionalReqParamsMaxNumber() {
@@ -71,4 +93,27 @@ public class OIDCProviderConfig {
public int getAdditionalReqParamsMaxOverallSize() {
return additionalReqParamsMaxOverallSize;
}
+
+ /**
+ * @param paramName Parameter name. Expected to be one of the known OIDC parameters
+ *
+ * @return maximum length for the specified OIDC parameter
+ */
+ public int getMaxLengthForTheParameter(String paramName) {
+ // Configured value for the particular OIDC parameter
+ Integer paramMaxSize = config.getInt(OIDCLoginProtocolFactory.CONFIG_OIDC_REQ_PARAMS_MAX_SIZE_PREFIX + "--" + paramName);
+
+ // Stick to default. See if we have default value overriden
+ if (paramMaxSize == null) {
+ paramMaxSize = DEFAULT_MAX_PARAMS_SIZES.get(paramName);
+ }
+
+ // Fallback to default for all standard OIDC parameters
+ if (paramMaxSize == null) {
+ paramMaxSize = reqParamsDefaultMaxSize;
+ }
+
+ return paramMaxSize;
+ }
+
}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParser.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParser.java
index 204e11cdd30..827604c7c9d 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParser.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestParser.java
@@ -60,6 +60,7 @@ public abstract class AuthzEndpointRequestParser {
private static final Logger logger = Logger.getLogger(AuthzEndpointRequestParser.class);
+ protected final OIDCProviderConfig config;
protected final int additionalReqParamsMaxNumber;
protected final int additionalReqParamsMaxSize;
protected final boolean additionalReqParamsFailFast;
@@ -101,7 +102,7 @@ public abstract class AuthzEndpointRequestParser {
protected AuthzEndpointRequestParser(KeycloakSession keycloakSession) {
OIDCLoginProtocol loginProtocol = (OIDCLoginProtocol) keycloakSession.getProvider(LoginProtocol.class, OIDCLoginProtocol.LOGIN_PROTOCOL);
- OIDCProviderConfig config = loginProtocol.getConfig();
+ this.config = loginProtocol.getConfig();
this.additionalReqParamsMaxNumber = config.getAdditionalReqParamsMaxNumber();
this.additionalReqParamsMaxSize = config.getAdditionalReqParamsMaxSize();
this.additionalReqParamsFailFast = config.isAdditionalReqParamsFailFast();
@@ -109,7 +110,7 @@ public abstract class AuthzEndpointRequestParser {
}
public void parseRequest(AuthorizationEndpointRequest request) {
- String clientId = getParameter(OIDCLoginProtocol.CLIENT_ID_PARAM);
+ String clientId = getAndValidateParameter(OIDCLoginProtocol.CLIENT_ID_PARAM);
if (clientId != null && request.clientId != null && !request.clientId.equals(clientId)) {
throw new IllegalArgumentException("The client_id parameter doesn't match the one from OIDC 'request' or 'request_uri'");
}
@@ -117,31 +118,30 @@ public abstract class AuthzEndpointRequestParser {
request.clientId = clientId;
}
- String responseType = getParameter(OIDCLoginProtocol.RESPONSE_TYPE_PARAM);
+ String responseType = getAndValidateParameter(OIDCLoginProtocol.RESPONSE_TYPE_PARAM);
validateResponseTypeParameter(responseType, request);
if (responseType != null) {
request.responseType = responseType;
}
- request.responseMode = replaceIfNotNull(request.responseMode, getParameter(OIDCLoginProtocol.RESPONSE_MODE_PARAM));
- request.redirectUriParam = replaceIfNotNull(request.redirectUriParam, getParameter(OIDCLoginProtocol.REDIRECT_URI_PARAM));
- request.state = replaceIfNotNull(request.state, getParameter(OIDCLoginProtocol.STATE_PARAM));
- request.scope = replaceIfNotNull(request.scope, getParameter(OIDCLoginProtocol.SCOPE_PARAM));
- request.loginHint = replaceIfNotNull(request.loginHint, getParameter(OIDCLoginProtocol.LOGIN_HINT_PARAM));
- request.prompt = replaceIfNotNull(request.prompt, getParameter(OIDCLoginProtocol.PROMPT_PARAM));
- request.idpHint = replaceIfNotNull(request.idpHint, getParameter(AdapterConstants.KC_IDP_HINT));
- request.action = replaceIfNotNull(request.action, getParameter(Constants.KC_ACTION));
- request.nonce = replaceIfNotNull(request.nonce, getParameter(OIDCLoginProtocol.NONCE_PARAM));
+ request.responseMode = replaceIfNotNull(request.responseMode, getAndValidateParameter(OIDCLoginProtocol.RESPONSE_MODE_PARAM));
+ request.redirectUriParam = replaceIfNotNull(request.redirectUriParam, getAndValidateParameter(OIDCLoginProtocol.REDIRECT_URI_PARAM));
+ request.state = replaceIfNotNull(request.state, getAndValidateParameter(OIDCLoginProtocol.STATE_PARAM));
+ request.scope = replaceIfNotNull(request.scope, getAndValidateParameter(OIDCLoginProtocol.SCOPE_PARAM));
+ request.loginHint = replaceIfNotNull(request.loginHint, getAndValidateParameter(OIDCLoginProtocol.LOGIN_HINT_PARAM));
+ request.prompt = replaceIfNotNull(request.prompt, getAndValidateParameter(OIDCLoginProtocol.PROMPT_PARAM));
+ request.idpHint = replaceIfNotNull(request.idpHint, getAndValidateParameter(AdapterConstants.KC_IDP_HINT));
+ request.action = replaceIfNotNull(request.action, getAndValidateParameter(Constants.KC_ACTION));
+ request.nonce = replaceIfNotNull(request.nonce, getAndValidateParameter(OIDCLoginProtocol.NONCE_PARAM));
request.maxAge = replaceIfNotNull(request.maxAge, getIntParameter(OIDCLoginProtocol.MAX_AGE_PARAM));
- request.claims = replaceIfNotNull(request.claims, getParameter(OIDCLoginProtocol.CLAIMS_PARAM));
- request.acr = replaceIfNotNull(request.acr, getParameter(OIDCLoginProtocol.ACR_PARAM));
- request.display = replaceIfNotNull(request.display, getParameter(OAuth2Constants.DISPLAY));
- request.uiLocales = replaceIfNotNull(request.uiLocales, getParameter(OAuth2Constants.UI_LOCALES_PARAM));
+ request.claims = replaceIfNotNull(request.claims, getAndValidateParameter(OIDCLoginProtocol.CLAIMS_PARAM));
+ request.acr = replaceIfNotNull(request.acr, getAndValidateParameter(OIDCLoginProtocol.ACR_PARAM));
+ request.display = replaceIfNotNull(request.display, getAndValidateParameter(OAuth2Constants.DISPLAY));
+ request.uiLocales = replaceIfNotNull(request.uiLocales, getAndValidateParameter(OAuth2Constants.UI_LOCALES_PARAM));
// https://tools.ietf.org/html/rfc7636#section-6.1
- request.codeChallenge = replaceIfNotNull(request.codeChallenge, getParameter(OIDCLoginProtocol.CODE_CHALLENGE_PARAM));
- request.codeChallengeMethod = replaceIfNotNull(request.codeChallengeMethod, getParameter(OIDCLoginProtocol.CODE_CHALLENGE_METHOD_PARAM));
-
+ request.codeChallenge = replaceIfNotNull(request.codeChallenge, getAndValidateParameter(OIDCLoginProtocol.CODE_CHALLENGE_PARAM));
+ request.codeChallengeMethod = replaceIfNotNull(request.codeChallengeMethod, getAndValidateParameter(OIDCLoginProtocol.CODE_CHALLENGE_METHOD_PARAM));
extractAdditionalReqParams(request.additionalReqParams);
}
@@ -215,6 +215,24 @@ public abstract class AuthzEndpointRequestParser {
return newVal==null ? previousVal : newVal;
}
+ protected String getAndValidateParameter(String paramName) {
+ String paramValue = getParameter(paramName);
+
+ if (paramValue != null) {
+ int maxLength = config.getMaxLengthForTheParameter(paramName);
+ if (paramValue.length() > maxLength) {
+ logger.warnf("The size of OIDC parameter '%s' size is longer (%d) than allowed (%d). %s", paramName, paramValue.length(), maxLength, additionalReqParamsFailFast ? "Request not allowed." : "Ignoring the parameter.");
+ if (additionalReqParamsFailFast) {
+ throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "The size of OIDC parameter '" + paramName + "' is longer than allowed.", Response.Status.BAD_REQUEST);
+ } else {
+ return null;
+ }
+ }
+ }
+
+ return paramValue;
+ }
+
protected abstract String getParameter(String paramName);
protected abstract Integer getIntParameter(String paramName);
diff --git a/test-framework/src/main/java/org/keycloak/test/framework/page/LoginPage.java b/test-framework/src/main/java/org/keycloak/test/framework/page/LoginPage.java
index d1128d57fe6..50bc08eb57a 100644
--- a/test-framework/src/main/java/org/keycloak/test/framework/page/LoginPage.java
+++ b/test-framework/src/main/java/org/keycloak/test/framework/page/LoginPage.java
@@ -28,4 +28,12 @@ public class LoginPage extends AbstractPage {
submitButton.click();
}
+
+ public String getUsername() {
+ return usernameInput.getAttribute("value");
+ }
+
+ public void clearUsernameInput() {
+ usernameInput.clear();
+ }
}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java
index d95b9900d23..e5439258ed1 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java
@@ -176,6 +176,8 @@ public class OAuthClient {
private String clientSessionHost;
+ private String loginHint;
+
private String maxAge;
private String prompt;
@@ -289,6 +291,7 @@ public class OAuthClient {
responseType = OAuth2Constants.CODE;
responseMode = null;
nonce = null;
+ loginHint = null;
request = null;
requestUri = null;
claims = null;
@@ -1543,6 +1546,9 @@ public class OAuthClient {
if (nonce != null) {
b.queryParam(OIDCLoginProtocol.NONCE_PARAM, nonce);
}
+ if (loginHint != null) {
+ b.queryParam(LOGIN_HINT_PARAM, loginHint);
+ }
String scopeParam = openid ? TokenUtil.attachOIDCScope(scope) : scope;
if (scopeParam != null && !scopeParam.isEmpty()) {
@@ -1769,6 +1775,11 @@ public class OAuthClient {
return this;
}
+ public OAuthClient loginHint(String loginHint) {
+ this.loginHint = loginHint;
+ return this;
+ }
+
public OAuthClient maxAge(String maxAge) {
this.maxAge = maxAge;
return this;
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthzEndpointRequestParserTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthzEndpointRequestParserTest.java
index 70390d5ead1..ac63eeb9c32 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthzEndpointRequestParserTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthzEndpointRequestParserTest.java
@@ -3,14 +3,18 @@ package org.keycloak.testsuite.authz;
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.core.Response;
import org.apache.commons.lang.RandomStringUtils;
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Assert;
import org.junit.Test;
+import org.keycloak.admin.client.resource.UserResource;
+import org.keycloak.common.util.SecretGenerator;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
+import org.keycloak.testsuite.admin.ApiUtil;
+import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.util.AdminClientUtil;
import org.keycloak.testsuite.util.Matchers;
-import org.keycloak.testsuite.util.RealmBuilder;
-
-import java.util.HashMap;
+import org.keycloak.testsuite.util.OAuthClient;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
@@ -19,6 +23,9 @@ import static org.hamcrest.Matchers.is;
public class AuthzEndpointRequestParserTest extends AbstractTestRealmKeycloakTest {
+ @Page
+ protected LoginPage loginPage;
+
@Override
public void configureTestRealm(RealmRepresentation testRealm) {
}
@@ -46,4 +53,46 @@ public class AuthzEndpointRequestParserTest extends AbstractTestRealmKeycloakTes
}
+ @Test
+ public void testParamsLength() {
+ // Login hint with length 200 allowed, state with length 200 allowed
+ String loginHint200 = SecretGenerator.getInstance().randomString(200);
+ String state200 = SecretGenerator.getInstance().randomString(200);
+ oauth
+ .loginHint(loginHint200)
+ .stateParamHardcoded(state200)
+ .openLoginForm();
+ assertLogin(loginHint200, state200);
+
+ // Login hint with length 500 not allowed, state with length 500 allowed
+ String loginHint500 = SecretGenerator.getInstance().randomString(500);
+ String state500 = SecretGenerator.getInstance().randomString(500);
+ oauth
+ .loginHint(loginHint500)
+ .stateParamHardcoded(state500)
+ .openLoginForm();
+ assertLogin("", state500);
+
+ // state with length 4100 not allowed
+ String state4100 = SecretGenerator.getInstance().randomString(4100);
+ oauth
+ .stateParamHardcoded(state4100)
+ .openLoginForm();
+ assertLogin("", null);
+ }
+
+ protected void assertLogin(String loginHintExpected, String stateExpected) {
+ loginPage.assertCurrent();
+ Assert.assertEquals(loginHintExpected, loginPage.getUsername());
+ loginPage.login("test-user@localhost", "password");
+
+ // String currentUrl = driver.getCurrentUrl();
+ OAuthClient.AuthorizationEndpointResponse response = new OAuthClient.AuthorizationEndpointResponse(oauth);
+ String state = response.getState();
+ Assert.assertEquals(stateExpected, state);
+
+ UserResource user = ApiUtil.findUserByUsernameId(testRealm(), "test-user@localhost");
+ user.logout();
+ }
+
}