microsoft/semantic-kernel · warning · HTTPException

Missing state parameter

Error message

Missing state parameter

What it means

Identical guard to the auth_server variant, raised as HTTPException(400) by the legacy_as_server login_page_handler (/login GET) when no 'state' query parameter is present. This is the legacy Authorization Server variant of the same flow.

Solutions

  1. Route clients through the legacy AS authorize endpoint so state is generated and attached to /login.
  2. Update legacy clients to include the state parameter when calling /login.
  3. Avoid direct deep-links to /login.
  4. Verify proxies preserve the state query parameter.

Example fix

// before
# legacy client opens /login with no query string

// after
# legacy client starts at /authorize; server redirects with state attached
Defensive patterns

Strategy: validation

Validate before calling

state = request.query_params.get('state')
if not state:
    return PlainTextResponse('Missing state. Start the flow at /authorize.', status_code=400)
await oauth_provider.get_login_page(state)

Type guard

def has_state_param(request) -> bool:
    return bool(request.query_params.get('state'))

Prevention

When it happens

Trigger: A GET /login on the legacy AS server without ?state=...; the legacy client built the login URL without state; direct navigation to /login.

Common situations: Migrating from the legacy AS and reusing stale bookmarks/URLs; a legacy client not emitting state; proxy stripping the query string.

Related errors


AI-assisted analysis of microsoft/semantic-kernel@c028a0c7dc (2026-08-13). Data as JSON: /api/errors/df34ceb2033277d2. Report an issue: GitHub.

Appendix: source

Thrown at python/samples/demos/mcp_with_oauth/server/mcp_simple_auth/legacy_as_server.py:81

        resource_server_url=None,
    )

    app = FastMCP(
        name="Simple Auth MCP Server",
        instructions="A simple MCP server with simple credential authentication",
        auth_server_provider=oauth_provider,
        host=server_settings.host,
        port=server_settings.port,
        debug=True,
        auth=mcp_auth_settings,
    )

    @app.custom_route("/login", methods=["GET"])
    async def login_page_handler(request: Request) -> Response:
        """Show login form."""
        state = request.query_params.get("state")
        if not state:
            raise HTTPException(400, "Missing state parameter")
        return await oauth_provider.get_login_page(state)

    @app.custom_route("/login/callback", methods=["POST"])
    async def login_callback_handler(request: Request) -> Response:
        """Handle simple authentication callback."""
        return await oauth_provider.handle_login_callback(request)

    @app.tool()
    async def get_time() -> dict[str, Any]:
        """
        Get the current server time.

        This tool demonstrates that system information can be protected
        by OAuth authentication. User must be authenticated to access it.
        """

        now = datetime.datetime.now()

View on GitHub (pinned to c028a0c7dc)