{"record":{"id":"ba6dd31b3fae6a80","repo":"spring-projects/spring-security","slug":"invalid-response","errorCode":"invalid_response","errorMessage":"invalidResponse(ex.getMessage())","messagePattern":"invalidResponse\\(ex\\.getMessage\\(\\)\\)","errorType":"error_code","errorClass":"Saml2AuthenticationException","httpStatus":null,"severity":"error","filePath":"saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/BaseOpenSamlAuthenticationTokenConverter.java","lineNumber":219,"sourceCode":"\t\tthis.requestMatcher = requestMatcher;\n\t}\n\n\tvoid setShouldConvertGetRequests(boolean shouldConvertGetRequests) {\n\t\tthis.shouldConvertGetRequests = shouldConvertGetRequests;\n\t}\n\n\tprivate @Nullable String decode(HttpServletRequest request) {\n\t\tString encoded = request.getParameter(Saml2ParameterNames.SAML_RESPONSE);\n\t\tboolean isGet = HttpMethod.GET.matches(request.getMethod());\n\t\tif (!this.shouldConvertGetRequests && isGet) {\n\t\t\treturn null;\n\t\t}\n\t\tSaml2Utils.DecodingConfigurer decoding = Saml2Utils.withEncoded(encoded).requireBase64(true).inflate(isGet);\n\t\ttry {\n\t\t\treturn decoding.decode();\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new Saml2AuthenticationException(Saml2Error.invalidResponse(ex.getMessage()), ex);\n\t\t}\n\t}\n\n}\n","sourceCodeStart":201,"sourceCodeEnd":224,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/BaseOpenSamlAuthenticationTokenConverter.java#L201-L224","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before: treating decode failure as a 500\ntry { converter.convert(request); } catch (Exception e) { throw e; }\n// after: map invalid_response to authentication failure handling\ntry {\n    return converter.convert(request);\n} catch (Saml2AuthenticationException ex) {\n    if (\"invalid_response\".equals(ex.getSaml2Error().getCode())) {\n        logger.warn(\"Invalid SAMLResponse: {}\", ex.getSaml2Error().getDescription(), ex.getCause());\n        authenticationFailureHandler.onAuthenticationFailure(request, response, ex);\n        return null;\n    }\n    throw ex;\n}","handlingStrategy":"try-catch","validationCode":"String samlResponse = request.getParameter(\"SAMLResponse\");\nif (samlResponse == null || samlResponse.isEmpty() || samlResponse.length() % 4 == 1) {\n    throw new Saml2AuthenticationException(Saml2Error.invalidResponse(\"malformed SAMLResponse parameter\"), null);\n}","typeGuard":"boolean hasDecodableSamlResponse(HttpServletRequest r) {\n    String p = r.getParameter(\"SAMLResponse\");\n    return p != null && !p.isEmpty() && p.length() % 4 != 1;\n}","tryCatchPattern":"try { return decoding.decode(); } catch (Saml2AuthenticationException ex) { log.warn(\"invalid_response: {}\", ex.getSaml2Error().getDescription(), ex.getCause()); failureHandler.onAuthenticationFailure(request, response, ex); return null; }","preventionTips":["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"],"tags":["saml","authentication","invalid-response","decoding"],"backgroundTag":"invalid-argument-format","analyzedSha":"96852e8860138a482cb13d1479573f24ff6443c6","analyzedAt":"2026-09-10T23:25:23.477Z","contentChangedAt":"2026-09-10T23:25:23.477Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}