Skip to content

fix(observability): register ObservabilityMiddleware inside auth so user_email is captured - #6478

Open
0717lee wants to merge 1 commit into
IBM:mainfrom
0717lee:fix/observability-user-email-middleware-order
Open

0717lee wants to merge 1 commit into
IBM:mainfrom
0717lee:fix/observability-user-email-middleware-order

Conversation

@0717lee

@0717lee 0717lee commented Aug 31, 2026

Copy link
Copy Markdown

Fixes #6473

Problem

observability_traces.user_email was NULL on 100% of rows regardless of auth mode. ObservabilityMiddleware was registered after AuthContextMiddleware in main.py, and Starlette's add_middleware() inserts at position 0 — the later-registered middleware is outermost and executes first. So start_trace() ran before any authentication had set request.state.user, and end_trace() has no backfill. The comment in main.py claimed the opposite execution order, which is what let this slip through.

Fix

  • Register ObservabilityMiddleware before AuthContextMiddleware so it ends up inside auth: AuthContextMiddleware executes first and populates request.state.user / request.state.user_email, then start_trace() can read the identity. The ordering comment now describes the real add_middleware semantics (last registered = outermost = first) so the order doesn't silently regress.
  • In observability_middleware.py, read the identity defensively: prefer request.state.user.email, falling back to request.state.user_email, which is what the JWT and API-token paths set.
  • Dropped an unused AsyncMock import in the touched test file.

Tests

  • New test_main_observability_order.py: pins on the assembled app that AuthContextMiddleware sits closer to the front of user_middleware than ObservabilityMiddleware (i.e. executes first).
  • Two dispatch tests in test_observability_middleware.py: request.state.user.email and the request.state.user_email fallback both reach start_trace(user_email=...).

pytest tests/unit/mcpgateway/middleware/test_observability_middleware.py tests/unit/mcpgateway/test_main_observability_order.py — 17 passed; related test_main*.py / observability-migration suites (444 tests) pass; ruff check / ruff format clean.

@0717lee
0717lee force-pushed the fix/observability-user-email-middleware-order branch 2 times, most recently from f58ca0f to 2db1231 Compare September 12, 2026 00:19
…ser_email is captured

ObservabilityMiddleware was registered after AuthContextMiddleware, and
Starlette's add_middleware() inserts at position 0, so the later-registered
middleware is outermost and executes first. start_trace() therefore ran
before any authentication and read request.state.user before
AuthContextMiddleware had set it, leaving observability_traces.user_email
NULL on every row (IBM#6473).

- Register ObservabilityMiddleware before AuthContextMiddleware so it ends
  up inside auth (auth runs first), and replace the ordering comment that
  described the opposite of the actual execution semantics.
- Read the identity defensively in the middleware: prefer
  request.state.user.email, falling back to request.state.user_email, which
  is what the JWT and API-token paths set.
- Regression tests: a registration-order test pinning auth before
  observability on the assembled app, and dispatch tests covering both
  identity shapes reaching start_trace(user_email=...).

Signed-off-by: Li Fengmin <2080291162@qq.com>
@0717lee
0717lee force-pushed the fix/observability-user-email-middleware-order branch from 2db1231 to 5b3625d Compare October 24, 2026 04:55
@0717lee

0717lee commented Oct 24, 2026

Copy link
Copy Markdown
Author

Replayed onto the rewritten main (the 1.1.0 history rewrite broke the branch's merge base, which is why the PR showed as conflicting). 5b3625d4 applies the same two changes to today's code: register_auth_context_middleware / TokenUsageMiddleware now register after ObservabilityMiddleware (placing observability inside auth), and start_trace also reads request.state.user_email for the JWT/API-token paths that only set that attribute.

Re-verified locally: the registration-order regression test fails without the change and passes with it; the observability middleware suite (17 tests) passes; ruff is clean on the touched files.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

observability_traces.user_email is always NULL: ObservabilityMiddleware registered after AuthContextMiddleware runs before auth

1 participant