symfonic.agent.fastapi.identity¶
identity ¶
Who is calling, whichever auth path answered.
Two auth paths reach a handler and they publish identity differently. The
legacy verifier owns the Request and stashes user_id / is_admin on
request.state; the injected resolver never sees a request and publishes an
:class:~symfonic.platform.values.AuthenticatedPrincipal instead. Eight call
sites across five modules read the legacy attributes directly, so before this
module an app that migrated to the resolver lost its identity in every one of
them — with no error, because every read was a getattr with a default.
Kept out of dependencies deliberately: that module is the transport
seam (headers in, scope out) and already carries the resolver installer, the
production gate and the legacy global. "Who is the caller" is a different
question with different consumers, and it was the growth of dependencies
past its budget that made the split obvious.
request_is_admin ¶
Whether the caller is a platform admin.
hasattr rather than a truthy check, because is_admin = False is a
decision the legacy verifier made and absence is not. Reading it with a
falsy default would let an unauthenticated request and a request explicitly
refused admin look identical, and then the principal would silently
override a denial the verifier had already issued.
Source code in src/symfonic/agent/fastapi/identity.py
request_principal ¶
The principal the injected resolver derived for this request, if any.
Source code in src/symfonic/agent/fastapi/identity.py
request_user_id ¶
The authenticated caller's id, or None when nothing authenticated.
The legacy stash wins when present, and the precedence is deliberate rather than incidental: a verifier that saw the request can enrich what it stashes — the scaffold promotes an org owner to admin after its membership query — so between two answers the more informed one is the one that had the request in hand.
None rather than a raise. This is read in audit emission and the budget
breaker, and a deployment with no verifier is explicitly supported for dev;
turning that into a 500 would punish the supported case.