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

  1. Read the wrapped cause (ex.getCause()) — it distinguishes the exact failure: IllegalArgumentException = bad Base64, Saml2Exception inflate = wrong binding/deflate, size-cap = too-large payload
  2. Confirm the IdP binding matches the converter config: setShouldConvertGetRequests(true) if the IdP uses Redirect/GET; default POST messages must not be inflated
  3. Capture the raw SAMLResponse (network trace or IdP log) and validate it independently: URL-decode, Base64-decode, raw-inflate, check XML parses
  4. Return the failure to the authentication failure handler rather than retrying — this error means the client-supplied response is invalid
  5. 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

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


AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10). Data as JSON: /api/errors/ba6dd31b3fae6a80. Report an issue: GitHub.