[#1080] OAuth2 consent: serve the consent details as JSON - #1081
[#1080] OAuth2 consent: serve the consent details as JSON#1081maximthomas wants to merge 1 commit into
Conversation
…ails as JSON 16.1.1 (CVE-2026-53660) replaced the `csrf` consent parameter with a random per-request token that is only minted while rendering the HTML consent page, so a client that posts the consent decision directly always gets HTTP 400. There was no supported way for it to obtain a token. Add a JSON representation of the consent details to GET /oauth2/authorize, selected with `Accept: application/json`. It carries the token along with the requested scopes and claims, so a non-browser client can post the decision back exactly as the consent form does. The strict per-request token is kept as it is; the legacy `csrf=<session id>` form is not restored. - negotiate strictly: JSON has to be preferred over HTML, so `*/*` and the library defaults that weight the two equally keep getting the consent page - refuse the JSON representation to a foreign Origin, since the OAuth2 endpoints sit behind a CORS filter that can be configured to echo the caller's Origin with credentials - report errors as JSON to JSON clients, and turn the login redirect into 401 login_required, which a non-browser client can act on - e2e coverage of the two-step flow, and correct the dev guide and admin guide, which still described `csrf` as a copy of the iPlanetDirectoryPro cookie
|
Thanks for the thorough PR — but I think we should solve #1080 differently. The regression has a ~5-line fix that restores the documented contract exactly, and once it is in place the JSON consent representation has no remaining problem to solve. I'd rather not take on a new public API for this. A workaround exists already, with no code change at all. The double-submit check needs no server-side state: it simply compares the $ curl --request POST \
--header "Cookie: iPlanetDirectoryPro=<session>; oauth2_csrf=<any random value>" \
--data "csrf=<the same value>" \
--data "decision=allow" \
... \
https://openam.example.com:8443/openam/oauth2/realms/root/authorize(on HTTPS deployments the cookie name is The proposal: accept the resource owner's session token ID as a valid // Backward compatibility with the pre-16.1.1 documented contract: the resource owner's
// session token ID is accepted as proof - but only while the SSO cookie is HttpOnly,
// so the accepted value is never script-readable. See the security note below.
if (ssoToken != null && CookieUtils.isCookieHttpOnly()
&& constantTimeEquals(ssoToken.getTokenID().toString(), csrfValue)) {
return false;
}Why this does not reintroduce CVE-2026-53660. The vulnerability was never "the server accepts the session ID as the CSRF token". It was that the browser flow required the XUI JavaScript to read the The Non-browser clients know their token ID legitimately: Why I'd drop the JSON representation rather than ship both:
The documentation corrections are valuable regardless — the dev guide and admin guide still describe Happy to open a PR with the |
|
closed in favor of #1083 |
Fix #1080
16.1.1 (CVE-2026-53660) replaced the
csrfconsent parameter with a random per-request token that is only minted while rendering the HTML consent page, so a client that posts the consent decision directly always gets HTTP 400. There was no supported way for it to obtain a token.Add a JSON representation of the consent details to GET /oauth2/authorize, selected with
Accept: application/json. It carries the token along with the requested scopes and claims, so a non-browser client can post the decision back exactly as the consent form does. The strict per-request token is kept as it is; the legacycsrf=<session id>form is not restored.*/*and the library defaults that weight the two equally keep getting the consent pagecsrfas a copy of the iPlanetDirectoryPro cookie