spring-projects/spring-security · error · OAuth2AuthenticationException
invalid_request
invalid_request
Error message
OpenID Connect 1.0 Logout Request Parameter: post_logout_redirect_uri
What it means
Raised by OidcLogoutAuthenticationValidator.validatePostLogoutRedirectUri during an OIDC RP-initiated logout when the post_logout_redirect_uri supplied in the logout request is not registered verbatim in the client's RegisteredClient.postLogoutRedirectUris. Per the OpenID Connect RP-Initiated Logout spec, the URI must exactly match a pre-registered value, and the error uses invalid_request.
Source
Thrown at oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationValidator.java:69
public static final Consumer<OidcLogoutAuthenticationContext> DEFAULT_POST_LOGOUT_REDIRECT_URI_VALIDATOR = OidcLogoutAuthenticationValidator::validatePostLogoutRedirectUri;
private final Consumer<OidcLogoutAuthenticationContext> authenticationValidator = DEFAULT_POST_LOGOUT_REDIRECT_URI_VALIDATOR;
@Override
public void accept(OidcLogoutAuthenticationContext authenticationContext) {
this.authenticationValidator.accept(authenticationContext);
}
private static void validatePostLogoutRedirectUri(OidcLogoutAuthenticationContext authenticationContext) {
OidcLogoutAuthenticationToken oidcLogoutAuthentication = authenticationContext.getAuthentication();
RegisteredClient registeredClient = authenticationContext.getRegisteredClient();
if (StringUtils.hasText(oidcLogoutAuthentication.getPostLogoutRedirectUri())
&& !registeredClient.getPostLogoutRedirectUris()
.contains(oidcLogoutAuthentication.getPostLogoutRedirectUri())) {
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST,
"OpenID Connect 1.0 Logout Request Parameter: post_logout_redirect_uri",
"https://openid.net/specs/openid-connect-rpinitiated-1_0.html#ValidationAndErrorHandling");
throw new OAuth2AuthenticationException(error);
}
}
}
View on GitHub (pinned to 96852e8860)
Solutions
- Add the exact URI to the client registration: registeredClient.postLogoutRedirectUri("https://app.example.com/logged-out") and re-register/persist the client
- Send the post_logout_redirect_uri in the logout request exactly as registered (byte-for-byte: scheme, host, port, path, no trailing slash drift)
- Log the received value and diff it against RegisteredClient.getPostLogoutRedirectUris() to find the mismatch
- If the URI is legitimately variable, register all allowed variants (each host/port/path combination)
Example fix
// before
RegisteredClient.withRegisteredClient(existing)
.redirectUris(uris -> uris.add("https://app.example.com/callback"))
.build(); // no post-logout redirect URI registered
// after
RegisteredClient.withRegisteredClient(existing)
.redirectUris(uris -> uris.add("https://app.example.com/callback"))
.postLogoutRedirectUri("https://app.example.com/logged-out")
.build(); Defensive patterns
Strategy: validation
Validate before calling
Set<String> registered = Set.of("https://app.example.com/logged-out");
String requested = params.getFirst("post_logout_redirect_uri");
if (requested != null && !registered.contains(requested)) {
// do not send post_logout_redirect_uri, or fix it to match registration
params.remove("post_logout_redirect_uri");
} Type guard
boolean isRegisteredPostLogoutUri(String uri, RegisteredClient client) {
return uri != null && client.getPostLogoutRedirectUris().contains(uri);
} Try / catch
try {
redirectStrategy.sendRedirect(request, response, logoutUrl);
} catch (OAuth2AuthenticationException e) {
if (e.getError().getDescription().contains("post_logout_redirect_uri")) {
// fall back to logout without redirect URI
response.sendRedirect("/connect/logout?id_token_hint=" + idToken);
}
} Prevention
- Configure postLogoutRedirectUri for every client alongside redirectUri
- Send the exact registered string — avoid building URLs dynamically (ports, trailing slashes)
- Manage client registrations centrally so post-logout URIs aren't forgotten in envs
- Compare http vs https and localhost vs 127.0.0.1 carefully during local testing
When it happens
Trigger: A logout request like GET /connect/logout?id_token_hint=...&post_logout_redirect_uri=X where X differs from (or was never added to) registeredClient.getPostLogoutRedirectUris — even a trailing-slash, case, or scheme (http vs https) difference fails the contains() check.
Common situations: Front-end apps passing a dynamically built URL (different port, localhost vs 127.0.0.1, trailing slash) instead of the exact registered URI; clients migrated from OAuth2-only config where postLogoutRedirectUris was never configured; staging vs production hosts mismatch.
Understand the failure class
Background: "Invalid query parameter" / "Failed to parse value of ...": fixing bad query string parameters across APIs — this error's family across 36 libraries.
Related errors
- Invalid Client Registration: + fieldName
- Invalid Client Registration: + fieldName
- server_error
- server_error
- Failed to decode SAMLResponse
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/e9ea57cad166cabe.
Report an issue: GitHub.