Skip to content

Respond with 400 status code and invalid_request for malformed content types where specifications require it - #51410

Merged
vmuzikar merged 1 commit into
keycloak:mainfrom
michalvavrik:dev-auto/keycloak-49964/main
Aug 7, 2026
Merged

Respond with 400 status code and invalid_request for malformed content types where specifications require it#51410
vmuzikar merged 1 commit into
keycloak:mainfrom
michalvavrik:dev-auto/keycloak-49964/main

Conversation

@michalvavrik

Copy link
Copy Markdown
Member

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

Ensures malformed OAuth/OIDC Content-Type headers produce specification-compliant 400 invalid_request responses instead of HTTP 500 errors.

Changes:

  • Adds reusable content-type validation across affected endpoints.
  • Adds device-endpoint exception handling and resilient security-header processing.
  • Adds integration coverage for seven OAuth/OIDC endpoints.

Reviewed changes

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

Show a summary per file
File Description
tests/base/src/test/java/org/keycloak/tests/oauth/MalformedContentTypeTest.java Tests malformed headers across endpoints.
services/src/main/java/org/keycloak/protocol/oidc/utils/ContentTypeValidationUtil.java Adds media-type validation utility.
services/src/main/java/org/keycloak/protocol/oidc/par/endpoints/ParEndpoint.java Validates PAR content types.
services/src/main/java/org/keycloak/protocol/oidc/grants/device/endpoints/DeviceEndpoint.java Maps unsupported media errors to OAuth responses.
services/src/main/java/org/keycloak/protocol/oidc/grants/ciba/endpoints/BackchannelAuthenticationEndpoint.java Validates CIBA content types.
services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenRevocationEndpoint.java Validates revocation content types.
services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java Validates logout content types.
services/src/main/java/org/keycloak/headers/DefaultSecurityHeadersProvider.java Handles malformed request media types safely.
Suppressed comments (1)

tests/base/src/test/java/org/keycloak/tests/oauth/MalformedContentTypeTest.java:93

  • This uses a refresh token where the endpoint requires an OIDC logout token. If the malformed header is accidentally accepted, logout-token validation still returns the same 400 invalid_request, so this test can pass without exercising the behavior under test; use a valid logout token or otherwise distinguish the content-type error from token-validation errors.
        BackchannelLogoutResponse response = oauth.backchannelLogoutRequest(tokenResponse.getRefreshToken())

Copilot AI review requested due to automatic review settings August 4, 2026 06:54
@michalvavrik
michalvavrik force-pushed the dev-auto/keycloak-49964/main branch from c46f1a7 to 2a4f231 Compare August 4, 2026 06:54

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

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

Suppressed comments (2)

services/src/main/java/org/keycloak/protocol/oidc/utils/ContentTypeValidationUtil.java:25

  • This hard-coded ErrorResponseException drops CORS headers. PAR, token revocation, and legacy logout use CorsErrorResponseException for normal failures, but they invoke this helper before their CORS-aware error handling, so browsers cannot read the new malformed-header response; let CORS-enabled callers build a CORS-aware invalid_request response.
        } catch (IllegalArgumentException e) {
            throw new ErrorResponseException(Errors.INVALID_REQUEST, "The content-type header value did not correspond to a valid media type", Response.Status.BAD_REQUEST);
        }
        if (!requestMediaType.isCompatible(requiredMediaType)) {
            throw new ErrorResponseException(Errors.INVALID_REQUEST, "The content-type header value does not match consumed media type " + requiredMediaType, Response.Status.BAD_REQUEST);

tests/base/src/test/java/org/keycloak/tests/oauth/MalformedContentTypeTest.java:129

  • This added test does not exercise the incompatible-media branch in ContentTypeValidationUtil: DeviceEndpoint never calls that utility, and its new exception mapper handles the @Consumes rejection before endpoint dispatch. Add a direct utility test (or remove the unreachable branch) rather than treating this as coverage for lines 24–25.
                .header("Content-Type", MediaType.TEXT_HTML)
                .send();
        assertOAuthInvalidRequest(response);

Copilot AI review requested due to automatic review settings August 4, 2026 07:01
@michalvavrik
michalvavrik force-pushed the dev-auto/keycloak-49964/main branch from 2a4f231 to 8dbaf53 Compare August 4, 2026 07:01

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

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

Suppressed comments (2)

services/src/main/java/org/keycloak/protocol/oidc/utils/ContentTypeValidationUtil.java:26

  • MediaType.isCompatible treats wildcard values such as */* and application/* as compatible, so these unsupported Content-Type values bypass the new validation even though the endpoints require form encoding. Reject wildcard media types before applying compatibility.
        if (!requestMediaType.isCompatible(requiredMediaType)) {
            throw new ErrorResponseException(Errors.INVALID_REQUEST, "The content-type header value does not match consumed media type " + requiredMediaType, Response.Status.BAD_REQUEST);

services/src/main/java/org/keycloak/protocol/oidc/utils/ContentTypeValidationUtil.java:25

  • @Consumes(APPLICATION_FORM_URLENCODED) rejects a valid incompatible type during resource matching, before these endpoint methods call this utility, so this branch cannot turn text/html into 400 invalid_request for PAR, CIBA, revocation, or logout. The added incompatible-type test exercises only DeviceEndpoint's separate exception mapper; handle NotSupportedException at each applicable resource boundary and cover a utility-backed endpoint as well.
        if (!requestMediaType.isCompatible(requiredMediaType)) {
            throw new ErrorResponseException(Errors.INVALID_REQUEST, "The content-type header value does not match consumed media type " + requiredMediaType, Response.Status.BAD_REQUEST);

@michalvavrik
michalvavrik force-pushed the dev-auto/keycloak-49964/main branch from 8dbaf53 to 2accd2f Compare August 4, 2026 07:34
closes: keycloak#49964

Signed-off-by: Michal Vavřík <dev@michalvavrik.net>
Copilot AI review requested due to automatic review settings August 4, 2026 07:40
@michalvavrik
michalvavrik force-pushed the dev-auto/keycloak-49964/main branch from 2accd2f to 1efc84d Compare August 4, 2026 07:40

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

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

Suppressed comments (1)

tests/base/src/test/java/org/keycloak/tests/oauth/MalformedContentTypeTest.java:123

  • This test exercises DeviceEndpoint's separate NotSupportedException mapper, not the compatibility branch in ContentTypeValidationUtil; every utility call site is still tested only with values that fail during parsing. Add a valid incompatible case against PAR, CIBA, logout, or revocation, or remove that branch if it is not intended to be reachable.
    void deviceAuthorizationEndpointIncompatibleType() {

@keycloak-github-bot

Copy link
Copy Markdown

Unreported flaky test detected

If the flaky tests below are affected by the changes, please review and update the changes accordingly. Otherwise, a maintainer should report the flaky tests prior to merging the PR.

org.keycloak.testsuite.federation.ldap.LDAPGroupMapperTest#test01_ldapOnlyGroupMappings

Keycloak CI - Base IT (5)

org.opentest4j.AssertionFailedError: expected: <4> but was: <3>
	at org.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:151)
	at org.junit.jupiter.api.AssertionFailureBuilder.buildAndThrow(AssertionFailureBuilder.java:132)
	at org.junit.jupiter.api.AssertEquals.failNotEqual(AssertEquals.java:197)
	at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:150)
...

Report flaky test

@keycloak-github-bot keycloak-github-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Unreported flaky test detected, please review

@michalvavrik
michalvavrik requested a review from mposolda August 4, 2026 09:08
@michalvavrik

Copy link
Copy Markdown
Member Author

@mposolda not sure if you are the right person, if you know about more suitable review, just change it, thanks

@shawkins shawkins 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.

So this gets pretty straight-forward once quarkusio/quarkus#55676 is available to us.

LGTM, thanks @michalvavrik

@vmuzikar vmuzikar 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.

As far as I can tell, LGTM. I think it's saafe to merge as it's quite straightforward.

@vmuzikar
vmuzikar merged commit 26c101f into keycloak:main Aug 7, 2026
91 checks passed
@michalvavrik
michalvavrik deleted the dev-auto/keycloak-49964/main branch August 7, 2026 15:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Malformed Content-Type header causes HTTP 500 on token endpoints instead of RFC 6749 §5.2 compliant 400

4 participants