Delegation
Delegation is a forward-looking feature and is off by default. Contact Defakto support to enable it for your organization. This capability is still evolving, and is not yet recommended for production use.
Delegation lets a workload exchange a user's identity token for a JWT-SVID, a JWT form of a SPIFFE Verifiable Identity Document (SVID), that represents the workload acting on behalf of that user (a delegated JWT-SVID). The issued credential carries both identities, the acting workload and the user, in a single token, so a downstream service can see who is calling and on whose behalf.
When to use it
Use delegation when a workload makes a request to a downstream service with the permissions of a user. A common case is an agent or application that takes an action a user initiated and needs the downstream service to apply that user's permissions, while still recording which workload performed the call.
How it works
Delegation uses an RFC 8693 token exchange on the Trust Domain Server's /oauth/token endpoint, the same endpoint the Defakto Agent connects to and that serverless workloads use for issuance. No new endpoint or infrastructure is required. Delegation shares that endpoint with the other token-exchange capabilities described in this section.
The workload calls this endpoint directly rather than going through the Agent. If your network does not currently allow workloads to reach the Trust Domain Server's agent endpoint, enable that traffic before using delegation.
The calling workload makes the exchange request with two inputs. The workload authenticates as itself with its own SVID (see Authenticating the calling workload for the two ways it can do this), and it presents the user's identity token (an OIDC ID token from the user's identity provider) as the subject token. The Trust Domain Server validates the subject token against the trust domain's allowlist (described under Configuration below) and, when the request is allowed, issues a delegated JWT-SVID for the requested audience.
Authenticating the calling workload
Every exchange authenticates the calling workload as itself, so the Trust Domain Server knows which workload is acting and records it on the issued credential and in the audit log. The workload authenticates with an SVID of this trust domain, presented in one of two ways. A request uses a single method.
JWT-SVID client assertion
The workload presents its JWT-SVID as an OAuth client_assertion, with client_assertion_type set to urn:ietf:params:oauth:client-assertion-type:jwt-spiffe. The assertion's audience is the Trust Domain Server's own issuer URL, which binds the assertion to the server it is presented to. The client-assertion method works over any TLS connection to the endpoint and does not require mutual TLS. It is most suitable when the connection terminates somewhere other than the server itself, for example a load balancer in front of a cloud-hosted deployment.
X.509 SVID over mutual TLS
The workload presents its X.509 SVID as the TLS client certificate on the connection. The Trust Domain Server verifies the certificate chain against the trust domain's X.509 bundle, checks its validity, and takes the workload identity from the certificate's SPIFFE ID. The X.509 method requires the TLS session to terminate at the Trust Domain Server itself, which is how self-hosted deployments serve the agent endpoint when TLS is enabled, and is not available where TLS terminates earlier, such as behind a load balancer that does not forward the client certificate.
Choosing a method
When a client assertion is present, it is the credential that counts, and any certificate on the connection is ignored. The X.509 method applies only when no client assertion is present, so a workload whose HTTP client always presents a certificate can still authenticate with an assertion. Either way, the presented SVID must belong to this trust domain, and a credential from another trust domain is rejected. The method used is recorded on the token_exchange audit event, so you can review how each caller authenticated.
The delegated credential
A delegated credential is an ordinary JWT-SVID with the addition of a nested obo claim (the protocol identifier for delegation) that names the user and the acting workload. The outer token is signed with the trust domain's existing JWT-SVID signing key, so it validates with the same SPIFFE tooling and JWKS as any other JWT-SVID. The delegated credential's lifetime is bounded by the user token it was minted from, so it never outlives the user identity evidence behind it.
The obo claim carries the user (obo.sub), a sub_profile marking the subject as a user, the user's identity provider (obo.iss), and the acting workload (obo.act, whose sub is the workload's SPIFFE ID). The act object names the workload currently acting for the user. When a credential is re-exchanged (described below), the new credential's act names the new caller. The token stamps only this single acting workload, not a nested chain of every workload that has carried the credential across re-exchanges, and the full sequence of exchanges stays reconstructable from the audit log.
A JWT-SVID from the trust domain is one of two shapes. When it has an obo claim it is a delegated token, and the sub of the outer envelope is the acting workload. When it has no obo claim it is an autonomous JWT-SVID that represents the workload's own identity, exactly as before.
Because the acting workload is always named in the credential, this is delegation in the RFC 8693 sense, never impersonation, because a downstream service can always see both who initiated the request and which workload is carrying it.
Configuration
Delegation denies every request until you configure an allowlist of trusted user identity providers. The allowlist is a TokenExchangePolicy section on the trust domain configuration. It lists each accepted issuer and the audiences its tokens may carry.
section: TokenExchangePolicy
schema: v1
spec:
allowlist:
- issuer: https://idp.example.com
audiences:
- my-app
Apply the policy to the trust domain:
spirlctl config set trust-domain --id <trust-domain-id> token-exchange-policy.yaml
A subject token is accepted only when its issuer and one of its audiences match an allowlist entry, both by exact string comparison. A token from an issuer that is not listed is rejected before its keys are fetched or its signature validated.
Re-exchanging a delegated credential
A workload that received a delegated credential can exchange it for a new delegated credential targeting a different audience, so a call chain can carry the user across services that each expect their own audience. The workload presents the credential it holds as the subject token, using urn:defakto:security:token-type:jwt-svid-obo as the subject_token_type (the same token type the exchange issues), and authenticates as itself the same way as for a first exchange.
The Trust Domain Server validates a re-exchanged subject token against the trust domain's own signing keys rather than the issuer allowlist. The allowlist governs which external identity providers may assert users, a question that does not arise for credentials the trust domain issued, so no allowlist entry is needed for re-exchange. Re-exchange is available whenever a TokenExchangePolicy is configured, the same switch that enables first exchanges.
The new credential carries the user through unchanged (obo.sub and obo.iss are copied from the input), names the new caller as the actor, and never outlives the input credential, so every credential in a chain of re-exchanges expires by the time the original user-identity evidence does. A delegated credential is bearer evidence within its lifetime. Any workload in the trust domain that holds it can re-exchange it, just as any workload can present a user's ID token for a first exchange. Every re-exchange is recorded in the audit log with the caller, the user, and both audiences, so the chain is reviewable end to end.
An autonomous JWT-SVID (one without an obo claim) cannot be re-exchanged; there is no user to carry through, and a workload that needs a credential for itself uses ordinary issuance.
Recipient processing
A service that receives a delegated token validates it the same way it validates any JWT-SVID. The service verifies the signature against the trust domain's JWKS, checks the standard sub, aud, and exp claims, and then reads the obo claim to learn who the call is on behalf of.
When the obo claim is absent the token is the workload's own identity, and the receiver treats it as a normal JWT-SVID. When the claim is present the receiver reads obo.sub and obo.iss as the user and the user's identity provider, and obo.act as the acting workload.
The act entry is the current presenter, so its sub equals the outer envelope's sub. A receiver can rely on this to confirm the token was presented by the workload it names. As defense in depth, a receiver that knows which user identity providers it expects can also check obo.iss against that set.