mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-10 15:32:05 -03:30
Use correct error response for missing assertions in Signed JWT Validation
* Ensure conformance for Signed JWT Validation (#43269) This re-adds the explicit client assertion parameter validation to produce the correct error responses required by RFC7523. See: https://www.rfc-editor.org/rfc/rfc7523.html#section-3.2 The refactoring for the support for Federated JWT Client authentication broke the OIDF conformance tests for https://www.rfc-editor.org/rfc/rfc7523.html. Fixes #43269 Fixes #43270 Signed-off-by: Thomas Darimont <thomas.darimont@googlemail.com> * Ensure conformance for Signed JWT Validation (#43269) Add additional tests for ClientAuthSignedJWTTest. Fixes #43269 Signed-off-by: Thomas Darimont <thomas.darimont@googlemail.com> --------- Signed-off-by: Thomas Darimont <thomas.darimont@googlemail.com>
This commit is contained in:
parent
12ae8b7cc9
commit
85afd62452
@ -1,6 +1,11 @@
|
||||
package org.keycloak.authentication.authenticators.client;
|
||||
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.authentication.AuthenticationFlowError;
|
||||
import org.keycloak.authentication.ClientAuthenticationFlowContext;
|
||||
import org.keycloak.http.HttpRequest;
|
||||
import org.keycloak.protocol.LoginProtocol;
|
||||
import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
@ -38,6 +43,11 @@ public class JWTClientValidator extends AbstractJWTClientValidator {
|
||||
return expectedAudiences;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate() {
|
||||
return clientAssertionParametersValidation() && super.validate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isMultipleAudienceAllowed() {
|
||||
OIDCLoginProtocol loginProtocol = (OIDCLoginProtocol) context.getSession().getProvider(LoginProtocol.class, OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||
@ -64,4 +74,44 @@ public class JWTClientValidator extends AbstractJWTClientValidator {
|
||||
return OIDCAdvancedConfigWrapper.fromClientModel(client).getTokenEndpointAuthSigningAlg();
|
||||
}
|
||||
|
||||
public boolean clientAssertionParametersValidation() {
|
||||
//KEYCLOAK-19461: Needed for quarkus resteasy implementation throws exception when called with mediaType authentication/json in OpenShiftTokenReviewEndpoint
|
||||
if(!isFormDataRequest(context.getHttpRequest())) {
|
||||
Response challengeResponse = ClientAuthUtil.errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), "invalid_client", "Parameter client_assertion_type is missing");
|
||||
context.challenge(challengeResponse);
|
||||
return false;
|
||||
}
|
||||
|
||||
var params = context.getHttpRequest().getDecodedFormParameters();
|
||||
|
||||
String clientAssertionType = params.getFirst(OAuth2Constants.CLIENT_ASSERTION_TYPE);
|
||||
var clientAssertion = params.getFirst(OAuth2Constants.CLIENT_ASSERTION);
|
||||
|
||||
if (clientAssertionType == null) {
|
||||
Response challengeResponse = ClientAuthUtil.errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), "invalid_client", "Parameter client_assertion_type is missing");
|
||||
context.challenge(challengeResponse);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!clientAssertionType.equals(OAuth2Constants.CLIENT_ASSERTION_TYPE_JWT)) {
|
||||
Response challengeResponse = ClientAuthUtil.errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), "invalid_client", "Parameter client_assertion_type has value '"
|
||||
+ clientAssertionType + "' but expected is '" + OAuth2Constants.CLIENT_ASSERTION_TYPE_JWT + "'");
|
||||
context.challenge(challengeResponse);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (clientAssertion == null) {
|
||||
Response challengeResponse = ClientAuthUtil.errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), "invalid_client", "client_assertion parameter missing");
|
||||
context.failure(AuthenticationFlowError.INVALID_CLIENT_CREDENTIALS, challengeResponse);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isFormDataRequest(HttpRequest request) {
|
||||
MediaType mediaType = request.getHttpHeaders().getMediaType();
|
||||
return mediaType != null && mediaType.isCompatible(MediaType.APPLICATION_FORM_URLENCODED_TYPE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -442,6 +442,32 @@ public class ClientAuthSignedJWTTest extends AbstractClientAuthSignedJWTTest {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithClientAndMissingClientAssertionType() throws Exception {
|
||||
List<NameValuePair> parameters = new LinkedList<NameValuePair>();
|
||||
parameters.add(new BasicNameValuePair(OAuth2Constants.CLIENT_ID, "client1"));
|
||||
parameters.add(new BasicNameValuePair(OAuth2Constants.GRANT_TYPE, OAuth2Constants.CLIENT_CREDENTIALS));
|
||||
|
||||
CloseableHttpResponse resp = sendRequest(oauth.getEndpoints().getToken(), parameters);
|
||||
AccessTokenResponse response = new AccessTokenResponse(resp);
|
||||
|
||||
assertError(response, 400, "client1", "invalid_client", Errors.INVALID_CLIENT_CREDENTIALS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithClientAndInvalidClientAssertionType() throws Exception {
|
||||
List<NameValuePair> parameters = new LinkedList<NameValuePair>();
|
||||
parameters.add(new BasicNameValuePair(OAuth2Constants.CLIENT_ID, "client1"));
|
||||
parameters.add(new BasicNameValuePair(OAuth2Constants.GRANT_TYPE, OAuth2Constants.CLIENT_CREDENTIALS));
|
||||
parameters.add(new BasicNameValuePair(OAuth2Constants.CLIENT_ASSERTION_TYPE, "invalid"));
|
||||
|
||||
CloseableHttpResponse resp = sendRequest(oauth.getEndpoints().getToken(), parameters);
|
||||
AccessTokenResponse response = new AccessTokenResponse(resp);
|
||||
|
||||
assertError(response,400, "client1", "invalid_client", Errors.INVALID_CLIENT_CREDENTIALS);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMissingClientAssertion() throws Exception {
|
||||
List<NameValuePair> parameters = new LinkedList<NameValuePair>();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user