{"record":{"id":"e9ea57cad166cabe","repo":"spring-projects/spring-security","slug":"invalid-request","errorCode":"invalid_request","errorMessage":"OpenID Connect 1.0 Logout Request Parameter: post_logout_redirect_uri","messagePattern":"OpenID Connect 1\\.0 Logout Request Parameter: post_logout_redirect_uri","errorType":"error_code","errorClass":"OAuth2AuthenticationException","httpStatus":400,"severity":"error","filePath":"oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationValidator.java","lineNumber":69,"sourceCode":"\tpublic static final Consumer<OidcLogoutAuthenticationContext> DEFAULT_POST_LOGOUT_REDIRECT_URI_VALIDATOR = OidcLogoutAuthenticationValidator::validatePostLogoutRedirectUri;\n\n\tprivate final Consumer<OidcLogoutAuthenticationContext> authenticationValidator = DEFAULT_POST_LOGOUT_REDIRECT_URI_VALIDATOR;\n\n\t@Override\n\tpublic void accept(OidcLogoutAuthenticationContext authenticationContext) {\n\t\tthis.authenticationValidator.accept(authenticationContext);\n\t}\n\n\tprivate static void validatePostLogoutRedirectUri(OidcLogoutAuthenticationContext authenticationContext) {\n\t\tOidcLogoutAuthenticationToken oidcLogoutAuthentication = authenticationContext.getAuthentication();\n\t\tRegisteredClient registeredClient = authenticationContext.getRegisteredClient();\n\t\tif (StringUtils.hasText(oidcLogoutAuthentication.getPostLogoutRedirectUri())\n\t\t\t\t&& !registeredClient.getPostLogoutRedirectUris()\n\t\t\t\t\t.contains(oidcLogoutAuthentication.getPostLogoutRedirectUri())) {\n\t\t\tOAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST,\n\t\t\t\t\t\"OpenID Connect 1.0 Logout Request Parameter: post_logout_redirect_uri\",\n\t\t\t\t\t\"https://openid.net/specs/openid-connect-rpinitiated-1_0.html#ValidationAndErrorHandling\");\n\t\t\tthrow new OAuth2AuthenticationException(error);\n\t\t}\n\t}\n\n}\n","sourceCodeStart":51,"sourceCodeEnd":74,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationValidator.java#L51-L74","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)"],"exampleFix":"// before\nRegisteredClient.withRegisteredClient(existing)\n    .redirectUris(uris -> uris.add(\"https://app.example.com/callback\"))\n    .build(); // no post-logout redirect URI registered\n// after\nRegisteredClient.withRegisteredClient(existing)\n    .redirectUris(uris -> uris.add(\"https://app.example.com/callback\"))\n    .postLogoutRedirectUri(\"https://app.example.com/logged-out\")\n    .build();","handlingStrategy":"validation","validationCode":"Set<String> registered = Set.of(\"https://app.example.com/logged-out\");\nString requested = params.getFirst(\"post_logout_redirect_uri\");\nif (requested != null && !registered.contains(requested)) {\n    // do not send post_logout_redirect_uri, or fix it to match registration\n    params.remove(\"post_logout_redirect_uri\");\n}","typeGuard":"boolean isRegisteredPostLogoutUri(String uri, RegisteredClient client) {\n    return uri != null && client.getPostLogoutRedirectUris().contains(uri);\n}","tryCatchPattern":"try {\n    redirectStrategy.sendRedirect(request, response, logoutUrl);\n} catch (OAuth2AuthenticationException e) {\n    if (e.getError().getDescription().contains(\"post_logout_redirect_uri\")) {\n        // fall back to logout without redirect URI\n        response.sendRedirect(\"/connect/logout?id_token_hint=\" + idToken);\n    }\n}","preventionTips":["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"],"tags":["oidc","logout","redirect-uri","validation","spring-authorization-server"],"backgroundTag":"invalid-query-parameter","analyzedSha":"96852e8860138a482cb13d1479573f24ff6443c6","analyzedAt":"2026-09-10T23:25:23.477Z","contentChangedAt":"2026-09-10T23:25:23.477Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}