{"record":{"id":"8e544c1bee7ea198","repo":"1Panel-dev/1Panel","slug":"invalid-saml2-navigation-response","errorCode":null,"errorMessage":"Invalid SAML2 navigation response","messagePattern":"Invalid SAML2 navigation response","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"frontend/src/utils/saml2.ts","lineNumber":26,"sourceCode":"          postURL: string;\n          fields: Record<string, string>;\n      };\n\nconst validateNavigationURL = (value: string) => {\n    const url = new URL(value, window.location.origin);\n    if (!['http:', 'https:'].includes(url.protocol)) {\n        throw new Error('Unsupported SAML2 navigation protocol');\n    }\n    return url.toString();\n};\n\nexport const submitSAML2Navigation = (navigation: SAML2Navigation, targetWindow: Window = window) => {\n    if (navigation.binding === 'redirect' && navigation.redirectURL) {\n        targetWindow.location.assign(validateNavigationURL(navigation.redirectURL));\n        return;\n    }\n    if (navigation.binding !== 'post' || !navigation.postURL || !navigation.fields) {\n        throw new Error('Invalid SAML2 navigation response');\n    }\n\n    const form = targetWindow.document.createElement('form');\n    form.method = 'POST';\n    form.action = validateNavigationURL(navigation.postURL);\n    form.style.display = 'none';\n\n    Object.entries(navigation.fields || {}).forEach(([name, value]) => {\n        const input = targetWindow.document.createElement('input');\n        input.type = 'hidden';\n        input.name = name;\n        input.value = value;\n        form.appendChild(input);\n    });\n\n    targetWindow.document.body.appendChild(form);\n    form.submit();\n    form.remove();","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/1Panel-dev/1Panel/blob/5ac7c808815b0691009cd390414f03471312262f/frontend/src/utils/saml2.ts#L8-L44","documentation":"submitSAML2Navigation (frontend/src/utils/saml2.ts:26) throws when the navigation object is neither a valid redirect (binding 'redirect' with redirectURL) nor a valid post (binding 'post' with postURL and fields). This guards against malformed backend responses before any DOM form is built; it indicates the SAML2 backend produced a navigation payload missing required keys, not a user input error.","triggerScenarios":"Backend returns binding 'post' with an empty fields object; postURL missing/empty; binding value other than 'redirect'/'post'; redirect binding with an empty redirectURL falls through to this check.","commonSituations":"SAML2 login response truncated or version-skewed between frontend expectations and backend DTO; backend error payload mistaken for a navigation payload; IdP returning a binding the panel does not support.","solutions":["Log/inspect the navigation object received from the backend (devtools network tab on the SAML2 endpoint)","Verify the backend serializes {binding:'redirect',redirectURL} or {binding:'post',postURL,fields} exactly — empty strings and undefined both trigger it","Check for frontend/backend version mismatch after a partial upgrade of the panel"],"exampleFix":"// backend payload before\n{ binding: 'post', postURL: 'https://idp/sso', fields: {} } // throws\n// after\n{ binding: 'post', postURL: 'https://idp/sso', fields: { SAMLRequest: '...' } }","handlingStrategy":"type-guard","validationCode":"const hasRedirect = (n: SAML2Navigation) => n.binding === 'redirect' && Boolean(n.redirectURL);\nconst hasPost = (n: SAML2Navigation) => n.binding === 'post' && Boolean(n.postURL) && Boolean(n.fields) && Object.keys(n.fields).length > 0;","typeGuard":"const isSAML2Navigation = (v: unknown): v is SAML2Navigation => {\n    if (typeof v !== 'object' || v === null) return false;\n    const n = v as Record<string, unknown>;\n    if (n.binding === 'redirect') return typeof n.redirectURL === 'string' && n.redirectURL.length > 0;\n    if (n.binding === 'post') return typeof n.postURL === 'string' && n.postURL.length > 0\n        && typeof n.fields === 'object' && n.fields !== null && Object.keys(n.fields).length > 0;\n    return false;\n};","tryCatchPattern":"if (!isSAML2Navigation(payload)) { throw new Error('Malformed SAML2 navigation from server — check panel/IdP version match'); }\nsubmitSAML2Navigation(payload);","preventionTips":["Zod/validate the backend navigation DTO at the API boundary","Pin frontend and backend versions together during upgrades","Log the raw payload shape when this fires to spot contract drift"],"tags":["saml2","backend-contract","validation"],"backgroundTag":null,"analyzedSha":"5ac7c808815b0691009cd390414f03471312262f","analyzedAt":"2026-08-15T14:02:06.953Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}