We have two filter chains (beans) configured in our SecurityConfig
:
oauth2ProtocolEndpointsSecurityFilterChain
with order 1 (authServer config)userEndpointsSecurityFilterChain
with order 2 (config for external OIDC IdPs)The second chain configures external OIDC providers, so when a user tries to log in to an app using our Auth server, they are forwarded to an external OIDC provider for authentication). This worked great until we changed response_mode
to form_post
in our custom AuthorizationRequestResolver
(query
is default, but is less secure):
additionalParameters.put(RESPONSE_MODE, "form_post");
This is now what happens. When the external OIDC provider redirects back to our auth server with the code value, we're getting an access denied exception. This is what I find in my logs with trace level enabled:
{"@timestamp":"2025-01-03T14:16:11.229589993+01:00","@version":"1","message":"Sending AnonymousAuthenticationToken [Principal=anonymousUser, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=10.131.0.2, SessionId=null], Granted Authorities=[ROLE_ANONYMOUS]] to authentication entry point since access is denied","logger_name":"org.springframework.security.web.access.ExceptionTranslationFilter","thread_name":"http-nio-8080-exec-4","level":"TRACE","level_value":5000,"stack_trace":"org.springframework.security.authorization.AuthorizationDeniedException: Access Denied\n\tat org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:99)\n\tat
Below this error I'm finding something I think is the root of the problem – a csrf requirement:
Invalid CSRF token found for https://nettskjema-authorization-dev.uio.no/login/oauth2/code/idporten
I have tried to disable csrf, just to figure out what happens. This have no affect in the second bean (filter) of course, but the first one. So I'm replacing the following config with another one, with CSRF completely disabled:
.csrf(csrf -> csrf.ignoringRequestMatchers(endpointsMatcher))
.csrf(csrf -> csrf.disable())
But the invalid CSRF token error is still there.
I have debugged the traffic by using DevTools, so this is the post request that is sent back from our external IdP (Curl to simplify)
curl 'https://nettskjema-authorization-dev.uio.no/login/oauth2/code/idporten' --compressed -X POST -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:133.0) Gecko/20100101 Firefox/133.0' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Accept-Language: no,en;q=0.7,en-US;q=0.3' -H 'Accept-Encoding: gzip, deflate, br, zstd' -H 'Content-Type: application/x-www-form-urlencoded' -H 'Origin: https://login.test.idporten.no' -H 'DNT: 1' -H 'Sec-GPC: 1' -H 'Connection: keep-alive' -H 'Referer: https://login.test.idporten.no/' -H 'Upgrade-Insecure-Requests: 1' -H 'Sec-Fetch-Dest: document' -H 'Sec-Fetch-Mode: navigate' -H 'Sec-Fetch-Site: cross-site' -H 'Priority: u=0, i' --data-raw 'iss=https%3A%2F%2Ftest.idporten.no&code=******&state=*******'
I find this very strange. In another Spring application (not Spring Auth Server), we're not having any issues with response_mode=form_post
. But this does not work with an OIDC client registered in Spring Auth Server.
I figured it out! form_post
does not work because we had configured the following bean:
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
serializer.setSameSite(LAX.attributeValue());
return serializer;
}
SameSite
must be NONE
when response_mode=form_post
is being used, something I read about here:
https://docs.feide.no/general/faq/samesite_cookie.html#changes-that-need-to-be-made-on-the-service-provider-side
So I added the following in application.properties
since we're running on Spring Boot:
server.servlet.session.cookie.same-site="none"
I also had to add this to the second filter chain, something similar what is done with authorizationServerConfigurer.getEndpointsMatcher()
:
.csrf(csrf -> csrf.ignoringRequestMatchers("/login/oauth2/**"))