spring-bootspring-securitycsrfspring-authorization-server

response_mode=form_post triggers invalid CSRF token in Spring Auth Server


We have two filter chains (beans) configured in our SecurityConfig:

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:

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.


Solution

  • 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/**"))