{"record":{"id":"a5758fa0cc4898b8","repo":"microsoft/semantic-kernel","slug":"invalid-state-parameter","errorCode":null,"errorMessage":"Invalid state parameter","messagePattern":"Invalid state parameter","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"python/samples/demos/mcp_with_oauth/server/mcp_simple_auth/simple_auth_provider.py","lineNumber":160,"sourceCode":"        username = form.get(\"username\")\n        password = form.get(\"password\")\n        state = form.get(\"state\")\n\n        if not username or not password or not state:\n            raise HTTPException(400, \"Missing username, password, or state parameter\")\n\n        # Ensure we have strings, not UploadFile objects\n        if not isinstance(username, str) or not isinstance(password, str) or not isinstance(state, str):\n            raise HTTPException(400, \"Invalid parameter types\")\n\n        redirect_uri = await self.handle_simple_callback(username, password, state)\n        return RedirectResponse(url=redirect_uri, status_code=302)\n\n    async def handle_simple_callback(self, username: str, password: str, state: str) -> str:\n        \"\"\"Handle simple authentication callback and return redirect URI.\"\"\"\n        state_data = self.state_mapping.get(state)\n        if not state_data:\n            raise HTTPException(400, \"Invalid state parameter\")\n\n        redirect_uri = state_data[\"redirect_uri\"]\n        code_challenge = state_data[\"code_challenge\"]\n        redirect_uri_provided_explicitly = state_data[\"redirect_uri_provided_explicitly\"] == \"True\"\n        client_id = state_data[\"client_id\"]\n        resource = state_data.get(\"resource\")  # RFC 8707\n\n        # These are required values from our own state mapping\n        assert redirect_uri is not None\n        assert code_challenge is not None\n        assert client_id is not None\n\n        # Validate demo credentials\n        if username != self.settings.demo_username or password != self.settings.demo_password:\n            raise HTTPException(401, \"Invalid credentials\")\n\n        # Create MCP authorization code\n        new_code = f\"mcp_{secrets.token_hex(16)}\"","sourceCodeStart":142,"sourceCodeEnd":178,"githubUrl":"https://github.com/microsoft/semantic-kernel/blob/c028a0c7dc4f0814cdcbaba9d998f187a41197bf/python/samples/demos/mcp_with_oauth/server/mcp_simple_auth/simple_auth_provider.py#L142-L178","documentation":"Thrown during the MCP OAuth sample's simple-login callback. The server keeps OAuth 'state' tokens in an in-memory dict (self.state_mapping); when the login form is POSTed back, the supplied 'state' value has no matching entry. This is the standard OAuth2 CSRF/state-protection check failing because the state is unknown, already consumed, or lost on a server restart.","triggerScenarios":"POST to /login/callback with a 'state' field that is absent from self.state_mapping — e.g. the user opened the login page, the server restarted (in-memory store wiped), then submitted the form; or the state was already consumed/removed after a prior successful callback (line 198 deletes it); or a crafted/tampered state.","commonSituations":"Restarting the demo MCP OAuth server between starting the auth flow and submitting credentials; double-submitting the login form; running multiple server replicas with no shared state store; clock/session expiry in long-held browser tabs.","solutions":["Restart the entire flow from the client: begin a new authorization request so the server generates and stores a fresh state token, then complete login in one continuous server session.","Do not restart the OAuth server mid-flow — its state_mapping is in-memory only and does not survive a restart.","If running behind multiple processes, use a single server instance for the sample (it is not designed for shared/clustered state).","Verify the login form's hidden 'state' input is being sent unchanged and that no proxy/browser is stripping form fields."],"exampleFix":"// Not a code bug — operational. Ensure no server restart between GET /login and POST /login/callback.\n// If you need persistence, replace the in-memory dict with a shared store in the sample provider.","handlingStrategy":"validation","validationCode":"# Before submitting the login form, ensure the state token is still valid by\n# completing the flow in a single server session. Server-side, optionally check:\nif state not in provider.state_mapping:\n    # prompt the user to restart the authorization flow\n    return RedirectResponse(url='/authorize', status_code=302)","typeGuard":null,"tryCatchPattern":"try:\n    redirect = await provider.handle_simple_callback(username, password, state)\nexcept HTTPException as e:\n    if e.status_code == 400 and 'state' in e.detail.lower():\n        # restart the OAuth flow from the beginning\n        ...\n    raise","preventionTips":["Never restart the in-memory OAuth sample server mid-flow.","Complete authorize -> login -> callback -> token exchange without server restarts.","Run only a single instance of the sample provider.","Treat a 400 'Invalid state' as a signal to restart the flow, not retry the same state."],"tags":["oauth","mcp","authentication","state-csrf","http-400","demo"],"backgroundTag":null,"analyzedSha":"c028a0c7dc4f0814cdcbaba9d998f187a41197bf","analyzedAt":"2026-08-13T13:48:05.040Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}