diff --git a/sentry_sdk/integrations/starlette.py b/sentry_sdk/integrations/starlette.py index ad9ba52413..3f9fbf4d8e 100644 --- a/sentry_sdk/integrations/starlette.py +++ b/sentry_sdk/integrations/starlette.py @@ -408,8 +408,8 @@ async def _sentry_authenticationmiddleware_call( receive: "Callable[[], Awaitable[Dict[str, Any]]]", send: "Callable[[Dict[str, Any]], Awaitable[None]]", ) -> None: - await old_call(self, scope, receive, send) _add_user_to_sentry_scope(scope) + await old_call(self, scope, receive, send) middleware_class.__call__ = _sentry_authenticationmiddleware_call @@ -422,7 +422,6 @@ def patch_middlewares() -> None: old_middleware_init = Middleware.__init__ not_yet_patched = "_sentry_middleware_init" not in str(old_middleware_init) - if not_yet_patched: def _sentry_middleware_init( diff --git a/tests/integrations/starlette/test_starlette.py b/tests/integrations/starlette/test_starlette.py index fb458a84d1..4ea57153c2 100644 --- a/tests/integrations/starlette/test_starlette.py +++ b/tests/integrations/starlette/test_starlette.py @@ -705,6 +705,48 @@ def test_user_information_transaction_no_pii(sentry_init, capture_events): assert "user" not in transaction_event +def test_user_information_does_not_clobber_app_set_user(sentry_init, capture_events): + """ + Regression test for PY-2596/GH-6756: the AuthenticationMiddleware patch must add the + request-derived user *before* the request handler runs, so that a user the app + sets during the request (via ``sentry_sdk.set_user``) is preserved rather than + overwritten. Starlette's ``SimpleUser`` only carries a username, so an app-set + ``id``/``email`` would otherwise be lost. + """ + sentry_init( + traces_sample_rate=1.0, + send_default_pii=True, + integrations=[StarletteIntegration()], + ) + + async def _set_user(request): + sentry_sdk.set_user( + { + "id": "user_42", + "email": "ada@beans.com", + "username": "Ada", + } + ) + return starlette.responses.JSONResponse({"status": "ok"}) + + app = starlette.applications.Starlette( + routes=[starlette.routing.Route("/set_user", _set_user)], + middleware=[Middleware(AuthenticationMiddleware, backend=BasicAuthBackend())], + ) + + events = capture_events() + + client = TestClient(app, raise_server_exceptions=False) + client.get("/set_user", auth=("Ada", "hello123")) + + (transaction_event,) = events + user = transaction_event.get("user", None) + assert user + assert user["username"] == "Ada" + assert user["id"] == "user_42" + assert user["email"] == "ada@beans.com" + + @pytest.mark.parametrize("span_streaming", [True, False]) def test_middleware_spans(sentry_init, capture_events, capture_items, span_streaming): sentry_init(