Bridge a verified resource-server identity into the Anubis MCP frame.
AttestoMCP.Plug.Authenticate (or AttestoPhoenix.Plug.Authenticate)
verifies the access token and assigns a protocol-shaped context onto the
conn under :attesto_context. The Anubis HTTP transport copies
conn.assigns into frame.assigns, so that context is reachable as
frame.assigns[:attesto_context] inside the server.
Anubis's own authorization machinery - the tools/list visibility filter,
Frame.scopes/1, Frame.has_scope?/2, any per-tool scope gate - reads the
identity from frame.context.auth (an Anubis.Server.Context field), which
is nil until a host populates it. put_auth/1 performs that one
projection: frame.assigns[:attesto_context] -> frame.context.auth, in the
auth-claims map shape Anubis.Server.Context documents.
The bridge does not add application authorization policy such as role, tenant, or tool-visibility rules. It records the first verified issuer, subject, and OAuth client that reaches a host callback and rejects a different identity on a later callback in the same live session.
Host wiring
Call it in init/2 so initialization records the owner, then at the top of
every request callback that uses the identity or session state:
def init(_client_info, frame) do
{:ok, AttestoMCP.Anubis.put_auth(frame)}
end
def handle_request(request, frame) do
frame = AttestoMCP.Anubis.put_auth(frame)
# ... Anubis authorization now sees the verified identity ...
endThe module is compile-guarded on Anubis.Server.Frame: a resource server
that does not depend on anubis_mcp never compiles it (mirroring how
attesto core gates its optional Plug surface on Plug.Conn).
Summary
Functions
Project the verified :attesto_context into frame.context.auth.
Project a verified context using an optional custom assign key.
Functions
@spec put_auth(Anubis.Server.Frame.t()) :: Anubis.Server.Frame.t()
Project the verified :attesto_context into frame.context.auth.
Idempotent and non-destructive:
- if
frame.context.authis already set, that value is never overwritten (a host or an earlier call owns it), while the verified context is still checked against the session owner; - if
frame.assigns[:attesto_context]is absent (unauthenticated request, STDIO transport, or the auth plug did not run), the frame is returned unchanged.
Otherwise frame.context.auth is set to the Anubis.Server.Context
auth-claims map projected from the context.
The first verified context also records a session-owner marker as described by
put_auth/2.
@spec put_auth( Anubis.Server.Frame.t(), keyword() ) :: Anubis.Server.Frame.t()
Project a verified context using an optional custom assign key.
:context_key must be an atom and defaults to :attesto_context, matching
AttestoMCP.Plug.Authenticate. Both the atom key used by live HTTP requests
and its string form produced by a JSON session-store round-trip are read.
The first verified identity seen by a host callback is recorded in the live
frame. A later callback carrying a different issuer, subject, or OAuth client
raises SessionOwnerMismatchError. Anubis reports that exception as a failed
request or session rather than an OAuth 401/403. Anubis handles initialization,
GET/SSE, DELETE, and persisted-session recovery before callbacks, and its 2.0
HTTP recovery path does not restore saved frame assigns. This guard therefore
protects only callback-visible state in the same live session. Hosts requiring
complete session isolation should use attesto_mcp_server.