spring-projects/spring-security · error · Saml2AuthenticationException
invalid_response
invalid_response
Error message
invalidResponse(ex.getMessage())
What it means
BaseOpenSamlAuthenticationTokenConverter.decode() reads the SAMLResponse request parameter, then base64-checks, decodes, and (for GET) inflates it via Saml2Utils. Any exception in that pipeline is converted into a Saml2AuthenticationException carrying the Saml2Error invalid_response with the underlying message. This signals an unparseable or structurally invalid SAML response from the client — an authentication error, not a server bug.
Source
Thrown at saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/BaseOpenSamlAuthenticationTokenConverter.java:219
this.requestMatcher = requestMatcher;
}
void setShouldConvertGetRequests(boolean shouldConvertGetRequests) {
this.shouldConvertGetRequests = shouldConvertGetRequests;
}
private @Nullable String decode(HttpServletRequest request) {
String encoded = request.getParameter(Saml2ParameterNames.SAML_RESPONSE);
boolean isGet = HttpMethod.GET.matches(request.getMethod());
if (!this.shouldConvertGetRequests && isGet) {
return null;
}
Saml2Utils.DecodingConfigurer decoding = Saml2Utils.withEncoded(encoded).requireBase64(true).inflate(isGet);
try {
return decoding.decode();
}
catch (Exception ex) {
throw new Saml2AuthenticationException(Saml2Error.invalidResponse(ex.getMessage()), ex);
}
}
}
View on GitHub (pinned to 96852e8860)
Solutions
- Read the wrapped cause (ex.getCause()) — it distinguishes the exact failure: IllegalArgumentException = bad Base64, Saml2Exception inflate = wrong binding/deflate, size-cap = too-large payload
- Confirm the IdP binding matches the converter config: setShouldConvertGetRequests(true) if the IdP uses Redirect/GET; default POST messages must not be inflated
- Capture the raw SAMLResponse (network trace or IdP log) and validate it independently: URL-decode, Base64-decode, raw-inflate, check XML parses
- Return the failure to the authentication failure handler rather than retrying — this error means the client-supplied response is invalid
- If users hit it intermittently on legitimate flows, check for reverse proxies rewriting '+' to ' ' or truncating long URLs
Example fix
// before: treating decode failure as a 500
try { converter.convert(request); } catch (Exception e) { throw e; }
// after: map invalid_response to authentication failure handling
try {
return converter.convert(request);
} catch (Saml2AuthenticationException ex) {
if ("invalid_response".equals(ex.getSaml2Error().getCode())) {
logger.warn("Invalid SAMLResponse: {}", ex.getSaml2Error().getDescription(), ex.getCause());
authenticationFailureHandler.onAuthenticationFailure(request, response, ex);
return null;
}
throw ex;
} Defensive patterns
Strategy: try-catch
Validate before calling
String samlResponse = request.getParameter("SAMLResponse");
if (samlResponse == null || samlResponse.isEmpty() || samlResponse.length() % 4 == 1) {
throw new Saml2AuthenticationException(Saml2Error.invalidResponse("malformed SAMLResponse parameter"), null);
} Type guard
boolean hasDecodableSamlResponse(HttpServletRequest r) {
String p = r.getParameter("SAMLResponse");
return p != null && !p.isEmpty() && p.length() % 4 != 1;
} Try / catch
try { return decoding.decode(); } catch (Saml2AuthenticationException ex) { log.warn("invalid_response: {}", ex.getSaml2Error().getDescription(), ex.getCause()); failureHandler.onAuthenticationFailure(request, response, ex); return null; } Prevention
- Always inspect ex.getCause() to distinguish Base64 vs inflate vs size failures
- Align setShouldConvertGetRequests with the IdP's binding (Redirect vs POST)
- Configure an authentication failure handler so invalid responses produce a clean auth failure, not a 500
- Monitor invalid_response frequency — spikes indicate tampering, replay, or IdP config drift
- Trace the raw SAMLResponse end-to-end (IdP -> proxy -> app) when diagnosing
When it happens
Trigger: POST/GET to the ACS/processing endpoint where the SAMLResponse parameter is missing-ish, not acceptable Base64 (error 502), not valid raw-DEFLATE when inflate(true) (error 501), exceeds the 1 MiB decompressed cap (error 503), or fails UTF-8 decoding.
Common situations: Users replaying stale or tampered ACS URLs; IdP misconfiguration sending POST binding while the app expects GET-style handling; proxies mangling the parameter; penetration testing / malicious clients posting garbage; clock/replay issues after session expiry producing truncated payloads.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- Failed to decode SAMLResponse
- RunAsImplAuthenticationProvider.incorrectKey
- Authenticated principal required to operate with ACLs
- CasAuthenticationProvider.incorrectKey
- <ticket validation failure message>
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/ba6dd31b3fae6a80.
Report an issue: GitHub.