Summary
After upgrading to 16.1.1/16.1.2, the new CSRF protection for the OAuth2 consent flow introduces a compatibility regression.
Previously, a client could submit the consent decision directly to the /authorize endpoint using only the authenticated iPlanetDirectoryPro session cookie, as documented.
After the CSRF changes, the consent POST now requires a newly generated CSRF token and an additional oauth2_csrf cookie that can only be obtained by first loading the consent page with a GET request.
This breaks existing clients that interact directly with the consent endpoint without rendering the HTML consent page.
Version
OpenAM 16.1.1, 16.1.2
Steps to reproduce
Authenticate normally and obtain a valid iPlanetDirectoryPro session cookie.
Submit the consent request directly:
curl --request POST
--header "Cookie: iPlanetDirectoryPro="
--data "scope=openid"
--data "response_type=code"
--data "client_id=iam-login"
--data "redirect_uri=http://localhost:8080/anything"
--data "state=abc123"
--data "decision=allow"
--data "csrf="
--data "code_challenge="
--data "code_challenge_method=S256"
http://openam:8080/openam/oauth2/realms/root/authorize
Expected behavior
The consent request should succeed, as it did before the CSRF changes, or the server should provide a documented mechanism for non-browser clients to obtain the required CSRF token.
Actual behavior
The request fails with CSRF validation.
Server logs show:
Session id from consent request does not match users session
because neither the session property nor the CSRF cookie exists.
Root cause
The new implementation generates the CSRF token only inside:
CsrfProtection.createCsrfToken()
which is only executed while rendering the consent page.
That method:
generates a random token
stores it in the session
issues the oauth2_csrf cookie
injects the token into the HTML page
Clients that POST directly to /authorize never execute this code path.
As a consequence:
no session property exists
no CSRF cookie exists
every direct POST fails
Additional observations
If consent is disabled (client consent not required), the authorization flow still works correctly because the consent page is skipped.
If consent is required and the browser first performs:
GET /authorize
the response correctly includes:
Set-Cookie: oauth2_csrf=
and the HTML contains the same token.
Submitting that token succeeds.
Why this appears to be a regression
The implementation now assumes that every consent flow starts with rendering the HTML consent page.
However, direct POST consent requests that previously worked are no longer supported, and the public documentation still demonstrates POST-based consent requests using only the authenticated session cookie.
Unless this compatibility break was intentional, the new implementation should either:
preserve compatibility for existing direct POST clients, or
provide an official API for obtaining the CSRF token before submitting consent.
Summary
After upgrading to 16.1.1/16.1.2, the new CSRF protection for the OAuth2 consent flow introduces a compatibility regression.
Previously, a client could submit the consent decision directly to the /authorize endpoint using only the authenticated iPlanetDirectoryPro session cookie, as documented.
After the CSRF changes, the consent POST now requires a newly generated CSRF token and an additional oauth2_csrf cookie that can only be obtained by first loading the consent page with a GET request.
This breaks existing clients that interact directly with the consent endpoint without rendering the HTML consent page.
Version
OpenAM 16.1.1, 16.1.2
Steps to reproduce
Authenticate normally and obtain a valid iPlanetDirectoryPro session cookie.
Submit the consent request directly:
curl --request POST
--header "Cookie: iPlanetDirectoryPro="
--data "scope=openid"
--data "response_type=code"
--data "client_id=iam-login"
--data "redirect_uri=http://localhost:8080/anything"
--data "state=abc123"
--data "decision=allow"
--data "csrf="
--data "code_challenge="
--data "code_challenge_method=S256"
http://openam:8080/openam/oauth2/realms/root/authorize
Expected behavior
The consent request should succeed, as it did before the CSRF changes, or the server should provide a documented mechanism for non-browser clients to obtain the required CSRF token.
Actual behavior
The request fails with CSRF validation.
Server logs show:
Session id from consent request does not match users session
because neither the session property nor the CSRF cookie exists.
Root cause
The new implementation generates the CSRF token only inside:
CsrfProtection.createCsrfToken()
which is only executed while rendering the consent page.
That method:
generates a random token
stores it in the session
issues the oauth2_csrf cookie
injects the token into the HTML page
Clients that POST directly to /authorize never execute this code path.
As a consequence:
no session property exists
no CSRF cookie exists
every direct POST fails
Additional observations
If consent is disabled (client consent not required), the authorization flow still works correctly because the consent page is skipped.
If consent is required and the browser first performs:
GET /authorize
the response correctly includes:
Set-Cookie: oauth2_csrf=
and the HTML contains the same token.
Submitting that token succeeds.
Why this appears to be a regression
The implementation now assumes that every consent flow starts with rendering the HTML consent page.
However, direct POST consent requests that previously worked are no longer supported, and the public documentation still demonstrates POST-based consent requests using only the authenticated session cookie.
Unless this compatibility break was intentional, the new implementation should either:
preserve compatibility for existing direct POST clients, or
provide an official API for obtaining the CSRF token before submitting consent.