fix(helm): remove wildcard redirect URIs - #676
Open
vincentchalamon wants to merge 1 commit into
Open
Conversation
vincentchalamon
force-pushed
the
fix/keycloak-wildcard-redirect-uris
branch
2 times, most recently
from
July 30, 2026 14:02
d19cd22 to
d19a5ec
Compare
vincentchalamon
force-pushed
the
fix/keycloak-wildcard-redirect-uris
branch
3 times, most recently
from
July 30, 2026 17:53
a6b093b to
cb44614
Compare
GHSA-2rmv-4743-38c6: the `api-platform-pwa` and `api-platform-swagger` clients were shipped with `redirectUris: ["*"]` and `webOrigins: ["*"]`, so an attacker could have a live authorization code delivered to any origin. Every `api-platform-*` client now lists explicit redirect URIs and web origins. `api-platform-pwa` keeps only the better-auth callback, `api-platform-swagger` only the Swagger UI oauth2-redirect asset, and `api-platform-api` — a resource server that never runs a browser flow — none at all. Both user-facing clients enforce PKCE S256, and `api-platform-pwa` declares its post-logout redirect URIs explicitly, since Keycloak otherwise falls back to the redirect URIs and would reject the logout route. Keycloak accepts no wildcard in the host part of a redirect URI while the deployed host is dynamic (pr-<N>-demo.api-platform.com), so the realm ships the unresolvable `app-host.invalid` placeholder and the Helm ConfigMap swaps it for the host of the release. `.invalid` is reserved by RFC 2606, so the entry left over in local development cannot leak a code anywhere. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
vincentchalamon
force-pushed
the
fix/keycloak-wildcard-redirect-uris
branch
from
July 31, 2026 08:25
cb44614 to
454b1ae
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes the private advisory GHSA-2rmv-4743-38c6 (High, CVSS 8.1, CWE-601).
realm-demo.jsonshippedredirectUris: ["*"]andwebOrigins: ["*"]onapi-platform-pwa(confidential, BFF, standard flow) and onapi-platform-swagger— a public client, so no secret is even needed to exchange a stolen code. A victim completing the login flow had a live authorization code delivered to an attacker-controlled origin, exchangeable for a realaccess_token/id_token.The Keycloak upgrade merged in #679 does not close this. Measured against the deployed
pr-679environment, running the upgraded Keycloak 26.6.4:while the properly scoped built-in
accountclient answers400 Invalid parameter: redirect_urion the same server. Redirect validation is active; the wildcard is what bypasses it. This PR is the actual remediation.Changes
Every
api-platform-*client now lists explicit redirect URIs and web origins:api-platform-pwaS256enforced, explicitpost.logout.redirect.urisapi-platform-swaggeroauth2-redirect.htmlasset onlyS256enforcedapi-platform-apistandardFlowEnabled: falsepost.logout.redirect.urisis required, not decorative: without it Keycloak falls back to the redirect URIs, and/api/auth/keycloak-logoutredirects to/books, which would then be rejected. The##separator is Keycloak'sConstants.CFG_DELIMITER.PKCE enforcement is safe on both: better-auth already sends
code_challenge_method=S256(@better-auth/core/dist/oauth2/create-authorization-url.mjs), andapi_platform.oauth.pkceis already enabled while Swagger UI only implements S256.Dynamic host
Keycloak accepts no wildcard in the host part of a redirect URI, while the deployed host is dynamic (
pr-<N>-demo.api-platform.com). The realm ships theapp-host.invalidplaceholder and the Helm ConfigMap swaps it for the release host, reusing the(first .Values.ingress.hosts).hostthat already drivesoidc-server-urlin the same ConfigMap — sodeploy.ymlneeds no change..invalidis reserved by RFC 2606, so the entry left over in local development is unresolvable and cannot leak a code.localhostis kept on purpose:e2e/playwright.config.jsusesbaseURL: 'https://localhost'and the E2E job runs against it.api-platform.github.iois left exactly where it is — thecorslist ofdeploy.yml, feedingphp.corsAllowOriginandmercure.extraDirectives, both untouched. No OIDC flow originates from that origin, so it gets no Keycloak entry; addinghttps://api-platform.github.ioto the Swagger client'swebOriginsis a one-liner if the Hydra console ever needs a token.Points worth discussing
api-platform-apilosesstandardFlowEnabled. It only serves as theuma-ticketaudience and asOIDC_AUD; no browser flow uses it. If anything regresses, restoringstandardFlowEnabled: truewhile keepingredirectUris: []is enough./bundles/apiplatform/swagger-ui/oauth2-redirect.html, fromSwaggerUi/index.html.twig). If a future upgrade moves that asset, Authorize on/docsbreaks silently — worth adding to the upgrade checklist.app-host.invalidvs templating the realm. The alternatives weretplon the whole file or keycloak-config-cli variable substitution; the placeholder keepsrealm-demo.jsonvalid JSON for the raw--import-realmthat compose uses, which the other options do not.Verification
Exploit closed — the negative control of the advisory PoC fails for all three clients:
A legitimate redirect URI is accepted, and PKCE enforcement is observable: omitting
code_challenge_methodyieldserror=invalid_request&error_description=Missing+parameter:+code_challenge_method.Suites (realm reimported from scratch on Keycloak 26.6.4,
@readand@writeeach on a fresh stack as CI does):@read: 24 passed / 2 flaky ·@write: 8 passed / 3 flaky / 0 failed. Same flaky set as before — login-page timing, green on retry.doctrine:schema:validate·redocly lint· php-cs-fixer all clean.helm lint, andhelm template --set ingress.hosts[0].host=pr-676-demo.api-platform.comleaves noapp-host.invalidwhile rendering both the PWA callback and the Swagger UI redirect for that host.Follow-up
Adding the
deploylabel exercises the dynamic-host substitution on a realpr-<N>-demo.api-platform.combefore merging.