Method C — Kubernetes / SPIFFE
The strongest method: no stored credential at all. The SPIRE agent on the node attests your pod and issues it a short-lived (~5 min) JWT-SVID; AuthSec verifies it against your registered trust domain. Nothing to leak, nothing to rotate.
your pod ──(unix socket)──▶ SPIRE agent ──▶ JWT-SVID (5 min)
│ │
└── SDK sends SVID as client assertion ──────┘
│
▼
AuthSec verifies against your trust domain's
OIDC discovery (the "workload identity provider")
│
▼
scoped access token
Setup is three parts: your cluster (once), the trust registration (once), and the workload connection (per app).
Cluster side — SPIRE (once per cluster)
You need a running SPIRE deployment with:
- SPIRE server + agents (the standard SPIRE quickstart)
- A registration entry mapping your pod's selectors to a SPIFFE ID:
spire-server entry create \
-spiffeID spiffe://your-trust-domain/your-workload \
-parentID spiffe://your-trust-domain/spire-agent \
-selector k8s:ns:default -selector k8s:sa:your-service-account - The OIDC discovery endpoint exposed (spire-oidc-discovery-provider) — a public URL where AuthSec can fetch your trust domain's keys. This URL becomes the Issuer URL in the next step.
- The agent socket mounted into your workload pod — the SDK reads
SVIDs from the SPIRE agent's unix socket, so your pod spec needs:
volumes:
- name: spire-agent-socket
hostPath: { path: /run/spire/sockets, type: Directory }
containers:
- name: your-app
volumeMounts:
- name: spire-agent-socket
mountPath: /run/spire/sockets
readOnly: true
Setting up SPIRE itself (server, agents, node attestation) is standard SPIFFE infrastructure — follow the official quickstart above. This guide only covers the parts specific to AuthSec.
Register the trust domain (dashboard, once)
Sidebar → Trusted Issuers → Workload identity providers. As the page says: "Issuers your workloads authenticate with — SPIRE trust domains (any cluster) and OIDC federation (e.g. GitHub Actions). No secrets." Click + Add provider:

Fill the form:

- Name — a label, e.g.
prod-spire - Kind —
SPIRE (SPIFFE) - Issuer URL — your SPIRE OIDC discovery URL from C1.3
- Trust domain — your SPIFFE trust domain (e.g.
authsec.local) - Allowed audiences — leave empty: defaults to this token endpoint, which is exactly what SVIDs must be minted for
Connect the workload to your MCP app (per app)
Open your application → Access tab → Add access → pick Kubernetes workload, no secret:

A four-step wizard opens ("Use SPIFFE/SPIRE so this pod can mint short-lived access tokens without a client secret"):
Step 1 — Workload. Choose your SPIRE setup — AuthSec-managed (they mint the SPIFFE ID for you) or Bring your own SPIRE (federate the trust domain you registered in C2) — and name the workload:

Step 2 — Access. Pick the role this workload gets on the MCP server (the roles from guide 1):

Step 3 — Trust. Select your registered provider (e.g.
prod-spire · authsec.local) and paste the exact SPIFFE ID from your
registration entry (format: spiffe://your-trust-domain/ns/prod/sa/api).
As the form warns: it must match the SVID's sub exactly, and its trust
domain must match the selected provider. Click Register workload:

Step 4 — Install. Confirmation: "Workload registered — no client secret was created. Configure SPIRE to issue this pod a JWT-SVID." Read this screen closely:

- SPIFFE ID — the workload's identity; the pod presents a short-lived JWT-SVID for this ID instead of storing any secret
- SVID AUDIENCE (TOKEN ENDPOINT) — the dashboard states the rule
explicitly: "Fetch the SVID with
-audience https://mcpauthz.com/oauth/token— it must match exactly or the exchange is rejected." - INSTALL SNIPPET — a ready-made
spire-server entry createcommand for your cluster (fill in your namespace/service-account selectors)
Click Done — the workload appears in the app's Who has access list as an active Machine identity with its role and effective scopes.
Code
Inside the pod, the SDK fetches and renews SVIDs automatically:
from authsec_sdk import SpiffeWorkloadIdentity, SpiffeConfig
spiffe = SpiffeWorkloadIdentity(SpiffeConfig(
mcp_server_url="https://your-mcp-server.example.com/mcp",
client_id="YOUR_SPIFFE_CLIENT_ID", # from step 4 (Install)
spiffe_id="spiffe://your-domain/your-workload",
scopes=["test_mcp:read"],
# agent_socket_path="/run/spire/sockets/agent.sock",
))
async with spiffe:
token = await spiffe.access_for()
Already hold an SVID (e.g. minted manually for testing outside a pod)? Use the low-level class — but mind two hard-won rules:
agent = AgentIdentity(ISSUER, SPIFFE_CLIENT_ID, auth=SpiffeSvidAuth(svid))
- The SVID's audience must be the token endpoint
(
https://mcpauthz.com/oauth/token) — an SVID minted with just the issuer as audience is rejected with "token aud must include this token endpoint". (This is why C2's "Allowed audiences" default is right.) - SVIDs live ~5 minutes — mint immediately before use;
SpiffeSvidAuthdoes not refresh (useSpiffeWorkloadIdentityfor that).
Shared steps: grant access · verify · troubleshooting