I’m developing a Spring Boot application deployed behind an AWS API Gateway (HTTP API v2) with Lambda (handler based on SpringBootLambdaContainerHandler and HttpApiV2ProxyRequest).
I’m using OAuth2 with Casdoor, but I’m running into an issue with the state parameter:
- Casdoor redirects back with a
stategenerated by Spring Security. - API Gateway seems to pass the
stateURL-encoded (=→%3D). - When Spring Security tries to match the returned
statewith the stored one, I get:authorization_request_not_found.
It works correctly locally. I also tested generating a state without special characters, and in that case the login flow worked behind Lambda/API Gateway.
Logs
DEBUG o.s.security.web.FilterChainProxy - Securing GET /oauth2/authorization/casdoor?
DEBUG o.s.s.web.DefaultRedirectStrategy - Redirecting to https://casdoor.example.com/login/oauth/authorize?...&state=abd7CZ2NFOsuFT2ivWcun89d8t7Ndnhn4o08AyrXb6A%3D&redirect_uri=https://api.example.com/login/oauth2/code/casdoor
INFO LambdaContainerHandler - IP xxx.xxx.xxx.xxx -- "GET /oauth2/authorization/casdoor" 302
DEBUG o.s.security.web.FilterChainProxy - Securing GET /login?
INFO LambdaContainerHandler - IP xxx.xxx.xxx.xxx -- "GET /login" 302
DEBUG o.s.security.web.FilterChainProxy - Securing GET /login/oauth2/code/casdoor?code=xxxxx&state=abd7CZ2NFOsuFT2ivWcun89d8t7Ndnhn4o08AyrXb6A=
ERROR SecurityConfig - OAuth2 login FAILURE
org.springframework.security.oauth2.core.OAuth2AuthenticationException: [authorization_request_not_found]
My Lambda handler
public class LambdaHandler implements RequestStreamHandler {
private static final SpringBootLambdaContainerHandler<HttpApiV2ProxyRequest, AwsProxyResponse>
handler;
static {
try {
handler = SpringBootLambdaContainerHandler.getHttpApiV2ProxyHandler(PojaApplication.class);
} catch (ContainerInitializationException e) {
throw new RuntimeException("Initialization of Spring Boot Application failed", e);
}
}
@Override
public void handleRequest(InputStream input, OutputStream output, Context context)
throws IOException {
handler.proxyStream(input, output, context);
}
}
My Spring Security configuration
package com.example.demo.endpoint.rest.security;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private static final Logger log = LoggerFactory.getLogger(SecurityConfig.class);
private final String casdoorClientId;
private final String casdoorLogoutUrl;
public SecurityConfig(
@Value("${spring.security.oauth2.client.registration.casdoor.clientid}")
String casdoorClientId,
@Value("${casdoor.logout.url}") String casdoorLogoutUrl) {
this.casdoorClientId = casdoorClientId;
this.casdoorLogoutUrl = casdoorLogoutUrl;
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf(Customizer.withDefaults())
.authorizeHttpRequests(
authz ->
authz
.requestMatchers("/casdoor-logout")
.permitAll()
.requestMatchers("/")
.permitAll()
.anyRequest()
.authenticated())
.oauth2Login(
oauth2 ->
oauth2
.successHandler(
(request, response, authentication) -> {
log.info("OAuth2 login SUCCESS");
log.info("User: {}", authentication.getName());
log.info("Authorities: {}", authentication.getAuthorities());
response.sendRedirect("/welcome");
})
.failureHandler(
(request, response, exception) -> {
log.error("OAuth2 login FAILURE");
log.error("Message: {}", exception.getMessage());
new SimpleUrlAuthenticationFailureHandler("/oauth2/authorization/casdoor")
.onAuthenticationFailure(request, response, exception);
log.info("Forced redirect to /oauth2/authorization/casdoor executed");
}));
return http.build();
}
}
What I tried and expected
I ran the OAuth2 login flow locally, and it worked perfectly: Spring Security generated a state, Casdoor redirected back, and the state was correctly matched.
Behind AWS Lambda + HTTP API v2, using the default generated state (with characters like =), the login fails. Generating a state without special characters works correctly.
I expected Spring Security to automatically handle the state parameter and match it correctly behind Lambda + API Gateway.
What actually happens: the state arrives in the Lambda request, but Spring Security cannot match it with the stored state.
Question:
Could anyone please advise how to automatically handle the encoding/decoding of the state parameter so that Spring Security OAuth2 receives it correctly behind Lambda + HTTP API v2, and the Casdoor authentication works without having to generate the state manually?
Thank you in advance for your help!