{"record":{"id":"a48fbeb15b1888f1","repo":"RocketChat/Rocket.Chat","slug":"missing-redirect-parameter","errorCode":null,"errorMessage":"Missing redirect parameter","messagePattern":"Missing redirect parameter","errorType":"http","errorClass":null,"httpStatus":400,"severity":"warning","filePath":"apps/meteor/server/lib/saml/lib/SAML.ts","lineNumber":421,"sourceCode":"\t\t\t\t\tLocation: Meteor.absoluteUrl(),\n\t\t\t\t});\n\t\t\t\tres.end();\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate static processSLORedirectAction(req: IIncomingMessage, res: ServerResponse, service: IServiceProviderOptions): void {\n\t\tconst { idpSLORedirectURL } = service;\n\t\tconst userRedirect = req.query.redirect as string;\n\n\t\tif (!idpSLORedirectURL) {\n\t\t\tres.writeHead(500);\n\t\t\tres.end('SLO redirect not configured');\n\t\t\treturn;\n\t\t}\n\n\t\tif (!userRedirect || typeof userRedirect !== 'string') {\n\t\t\tres.writeHead(400);\n\t\t\tres.end('Missing redirect parameter');\n\t\t\treturn;\n\t\t}\n\n\t\tlet configuredURL: URL;\n\t\tlet requestURL: URL;\n\n\t\ttry {\n\t\t\tconfiguredURL = new URL(idpSLORedirectURL);\n\t\t\trequestURL = new URL(userRedirect);\n\t\t} catch {\n\t\t\tres.writeHead(400);\n\t\t\tres.end('Invalid URL format');\n\t\t\treturn;\n\t\t}\n\n\t\tif (configuredURL.origin !== requestURL.origin) {\n\t\t\tres.writeHead(403);","sourceCodeStart":403,"sourceCodeEnd":439,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/2a7de457074cbb4d4373fbd9a4e5bea292c9c764/apps/meteor/server/lib/saml/lib/SAML.ts#L403-L439","documentation":"The SAML SLO redirect endpoint requires a `redirect` query parameter (the post-logout destination). The official client sends it as `?redirect=<encoded absolute URL>` (apps/meteor/client/meteor/login/saml.ts:80). When the parameter is absent or not a single string, the endpoint responds 400 'Missing redirect parameter'.","triggerScenarios":"Calling `/_saml/sloRedirect/<provider>/` with no query string; sending `?redirect=` with an empty value; sending the destination under a different parameter name (e.g. ?returnTo=); duplicating the parameter so it parses as an array.","commonSituations":"Custom logout buttons or bookmarks hitting the SLO endpoint directly; code written against an older flow that did not require the parameter; proxies or rewrites stripping the query string.","solutions":["Append the required parameter, URL-encoded: `/_saml/sloRedirect/<provider>/?redirect=${encodeURIComponent('https://chat.example.com/')}`","Send exactly one `redirect` parameter - repeated values can arrive as an array and fail the string check","If a proxy rewrites the URL, make sure it preserves the query string"],"exampleFix":"// before\nwindow.location.href = absoluteUrl(`_saml/sloRedirect/${provider}/`);\n\n// after\nwindow.location.replace(absoluteUrl(`_saml/sloRedirect/${provider}/?redirect=${encodeURIComponent(result)}`));","handlingStrategy":"validation","validationCode":"const sloUrl = (provider: string, redirect: string) =>\n  `/_saml/sloRedirect/${encodeURIComponent(provider)}/?redirect=${encodeURIComponent(redirect)}`;","typeGuard":"const hasRedirectParam = (u: string): boolean => new URL(u, location.origin).searchParams.has('redirect');","tryCatchPattern":null,"preventionTips":["Always build SLO URLs through one helper that appends the redirect parameter","URL-encode the redirect value exactly once","Ensure proxies preserve query strings when rewriting URLs"],"tags":["saml","logout","query-parameter","http-400"],"backgroundTag":"missing-query-parameter","analyzedSha":"2a7de457074cbb4d4373fbd9a4e5bea292c9c764","analyzedAt":"2026-08-18T15:26:39.429Z","contentChangedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}