{"record":{"id":"cf9f6913444148ee","repo":"HeyPuter/puter","slug":"bad-request-cf9f69","errorCode":"bad_request","errorMessage":"Missing `opener_state`","messagePattern":"Missing `opener_state`","errorType":"http","errorClass":"HttpError","httpStatus":400,"severity":"error","filePath":"src/backend/controllers/oidc/OIDCController.ts","lineNumber":211,"sourceCode":"        // parameters.\n        //\n        // Unauthenticated on purpose — it reveals nothing the caller did not\n        // already hand over, and a forged or expired proof yields nothing.\n\n        router.post(\n            '/auth/oidc/verify-popup-return',\n            {\n                subdomain: 'api',\n                rateLimit: {\n                    scope: 'oidc-verify-popup-return',\n                    limit: 60,\n                    window: 60_000,\n                },\n            },\n            async (req: Request, res: Response) => {\n                const proof = req.body?.opener_state;\n                if (typeof proof !== 'string' || !proof) {\n                    throw new HttpError(400, 'Missing `opener_state`', {\n                        legacyCode: 'bad_request',\n                    });\n                }\n                const decoded = this.services.oidc.verifyPopupReturn(proof);\n                if (!decoded) {\n                    throw new HttpError(400, 'Invalid `opener_state`', {\n                        legacyCode: 'bad_request',\n                    });\n                }\n                res.json({\n                    opener_origin: decoded.opener_origin ?? null,\n                    msg_id: decoded.msg_id ?? null,\n                    oidc_login: decoded.oidc_login === true,\n                });\n            },\n        );\n\n        // -- GET /auth/oidc/providers --------------------------------","sourceCodeStart":193,"sourceCodeEnd":229,"githubUrl":"https://github.com/HeyPuter/puter/blob/908ec23eda38526170322c3edf71ba45ecb1ca95/src/backend/controllers/oidc/OIDCController.ts#L193-L229","documentation":"`POST /auth/oidc/verify-popup-return` requires an `opener_state` string in the body — a signed proof token that the popup login flow passes back to the opener window. The endpoint is unauthenticated by design (it reveals nothing the caller didn't provide), so the first check is purely structural: the field must be present and non-empty.","triggerScenarios":"The popup-return handler is invoked without `opener_state` in the body; the frontend posted an empty object; the postMessage bridge between the popup and opener dropped the token.","commonSituations":"A popup login flow where the callback page fails to extract the state from the URL before posting; a cross-origin postMessage where the payload was serialized incorrectly; a manual test call that omits the field.","solutions":["Ensure the popup callback extracts `opener_state` from the OIDC redirect and includes it in the POST body.","Verify the postMessage bridge passes the full token, not a truncated value.","Set `Content-Type: application/json` on the request.","Log the popup URL on the client to confirm the state token is present before posting."],"exampleFix":"// before\nwindow.opener.postMessage({ type: 'oidc-return' }, '*');\n\n// after\nconst params = new URLSearchParams(location.search);\nwindow.opener.postMessage({\n  type: 'oidc-return',\n  opener_state: params.get('state'),\n}, openerOrigin);","handlingStrategy":"validation","validationCode":"// Extract opener_state from the OIDC redirect before posting\nconst params = new URLSearchParams(window.location.search);\nconst openerState = params.get('state');\nif (typeof openerState !== 'string' || !openerState) {\n  console.error('Missing state in OIDC redirect URL');\n  return;\n}\nawait fetch('/api/auth/oidc/verify-popup-return', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({ opener_state: openerState }),\n});","typeGuard":"/** @param {unknown} v @returns {v is string} */\nfunction isNonEmptyString(v) {\n  return typeof v === 'string' && v.length > 0;\n}","tryCatchPattern":null,"preventionTips":["Extract the state token from the OIDC redirect URL before posting to verify-popup-return.","Ensure the postMessage bridge between popup and opener carries the full token.","Log the popup URL client-side to confirm the state param is present."],"tags":["oidc","authentication","validation","popup","http-400","bad-request"],"backgroundTag":null,"analyzedSha":"908ec23eda38526170322c3edf71ba45ecb1ca95","analyzedAt":"2026-08-12T20:53:15.911Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}