POST /api/session/delegated
Mint a session acting as an externally-authenticated principal. Guarded.
WARNING: despite the path, this is not a login and it is not open. It is guarded, and
the caller must already hold a certificate-login session belonging to a registered
service permitted for the session:delegated operation (ADR-0031). It was open through
nine earlier revisions of this surface, for the structural reason a login must be; that
reason lapsed once a service had a credential of its own to present, and left open it
would let any unauthenticated caller impersonate any XAuth principal this Gateway's
delegator covers.
You do not send the target identity. You send localUser, and the Gateway constructs
the XAuth id by prefixing it with the xauthPrefix configured for your registered
service. A body that tries to name the target itself is refused with
CALLER_SUPPLIED_IDENTITY, because a caller-settable identity would defeat the whole
construction.
Keys created on the resulting session are owned by the target, not by the delegator, which is what keeps per-user grant, revoke and audit intact.
The session inherits the minting service's sender key and is therefore sender-constrained: its holder is the same program holding a second credential, and that program has already proved possession of that key. Binding to the same key closes what would otherwise be the one credential on this surface that a constrained request could mint and an unconstrained one could then spend.
WARNING: the key server's delegation guard is operation-subset only. It has no tenant or namespace scoping, so this Gateway can impersonate any XAuth principal whose operation set its delegator covers. That is a property of the engine, not of this route.
Authentication
Send the session id as Authorization: Bearer <session id>, and an RFC 9449 DPoP proof in the DPoP header if your session is bound to a key. The two are required together, not as alternatives — see Authenticating and Proving you hold your key.
There is no session cookie on any response and none is read on any request. Do not set credentials: "include" on a browser fetch.
Request
| Field | Type | Required | Description |
|---|---|---|---|
localUser | string | yes | Your application's own user id. The Gateway prefixes it to build the XAuth identity; you cannot name that identity directly. |
Example
{
"localUser": "..."
}
Responses
200
A delegated session, acting as the constructed target.
| Field | Type | Required | Description |
|---|---|---|---|
actingAs | string | yes | The XAuth id this session acts as. |
localUser | string | no | |
service | string | no | |
delegator | string | no | The subject of this Gateway's own delegating certificate. |
session | string | yes | The bearer credential. |
ks | KsAddress | yes | |
system | SystemBlock | no | The cached boot snapshot of the key server this Gateway fronts. It carries no credential: the boot call that produces it cannot return one. |
detail | string | yes |
KsAddress
| Field | Type | Required | Description |
|---|---|---|---|
host | string | no | |
port | integer | no |
Example
{
"actingAs": "...",
"localUser": "...",
"service": "...",
"delegator": "...",
"session": "...",
"ks": {
"host": "...",
"port": 0
},
"system": {},
"detail": "..."
}
400
CALLER_SUPPLIED_IDENTITY -- the body named an identity field the caller may not set. Or BAD_REQUEST -- localUser is missing or malformed, or the id the Gateway built from its own configured prefix is not a usable one, which is a fault in this Gateway's configuration rather than in your request.
401
No live session, or a DPoP proof that did not verify.
SESSION_NOT_FOUND -- the credential names no session this Gateway holds.
SESSION_EXPIRED -- it aged out. Both carry reauth: true; obtain a new credential
rather than retrying.
PROOF_* -- the sender constraint refused. See the DPoP section of this document for the
full table; PROOF_MALFORMED is a 400 and PROOF_REPLAY_CACHE_FULL a 503, and the
rest are here.
SCOPED_CREDENTIAL_EXPIRED -- a scoped browser credential passed its fixed lifetime. Use
does not extend it: an idle timeout would be refreshed by exactly the traffic a stolen
handle produces.
403
NOT_A_SERVICE_SESSION -- the caller's session is not a certificate login.
SERVICE_NOT_REGISTERED -- it is, and no registration matches its principal.
SERVICE_OPERATION_NOT_PERMITTED -- it is registered and this operation is not on
its permitted list.
DELEGATOR_NOT_PERMITTED -- the key server refused this Gateway's own certificate
principal as a delegator: either no client certificate was bound for the connection,
or the enrolled principal does not carry the DELEGATION flag (0x400).
DELEGATOR_TOO_NARROW -- the TARGET holds at least one operation the DELEGATOR does
not. The guard is a superset test with no administrator carve-out, so the fix is to
widen the delegator, never to narrow the check.
404
TARGET_NOT_FOUND -- no XAuth principal with that id exists, so there is nobody to become. This is the lazy-provisioning signal: create the target with POST /api/principals/xauth, which is idempotent, and retry.
500
A fault rather than a refusal.
OP_FAILED is the catch-all: the key server refused in words that match no classified
pattern, so the Gateway cannot say more than that the operation did not happen. Some key
server refusals that are conceptually a caller error arrive here rather than as a 4xx,
because the wire carries no distinguishing code -- where a route can recognise one from
its message it maps it, and each such mapping is documented on the operation.
503
MAJORDOMO_NOT_CONFIGURED or DELEGATION_NOT_CONFIGURED -- this Gateway holds no delegator identity, so it cannot impersonate anybody; nothing was sent to the key server. SERVICE_REGISTRY_NOT_CONFIGURED -- no service registry is loaded. SENDER_BINDING_UNAVAILABLE -- the Gateway is holding its maximum of sender-key bindings and could not record another; the session was not handed out, because a delegated credential this Gateway cannot bind would be spendable by anyone holding it. Retryable.
Error handling
Every failure answers JSON carrying at least code and detail. Match on code — detail is written for a human debugging the call and its wording is not part of the contract. See the error model.
| Status | Meaning |
|---|---|
400 | CALLER_SUPPLIED_IDENTITY -- the body named an identity field the caller may not set. Or BAD_REQUEST -- localUser is missing or malformed, or the id the Gateway built from its own configured prefix is not a usable one, which is a fault in this Gateway's configuration rather than in your request. |
401 | No live session, or a DPoP proof that did not verify. SESSION_NOT_FOUND -- the credential names no session this Gateway holds. SESSION_EXPIRED -- it aged out. Both carry reauth: true; obtain a new credential rather than retrying. PROOF_* -- the sender constraint refused. See the DPoP section of this document for the full table; PROOF_MALFORMED is a 400 and PROOF_REPLAY_CACHE_FULL a 503, and the rest are here. SCOPED_CREDENTIAL_EXPIRED -- a scoped browser credential passed its fixed lifetime. Use does not extend it: an idle timeout would be refreshed by exactly the traffic a stolen handle produces. |
403 | NOT_A_SERVICE_SESSION -- the caller's session is not a certificate login. SERVICE_NOT_REGISTERED -- it is, and no registration matches its principal. SERVICE_OPERATION_NOT_PERMITTED -- it is registered and this operation is not on its permitted list. DELEGATOR_NOT_PERMITTED -- the key server refused this Gateway's own certificate principal as a delegator: either no client certificate was bound for the connection, or the enrolled principal does not carry the DELEGATION flag (0x400). DELEGATOR_TOO_NARROW -- the TARGET holds at least one operation the DELEGATOR does not. The guard is a superset test with no administrator carve-out, so the fix is to widen the delegator, never to narrow the check. |
404 | TARGET_NOT_FOUND -- no XAuth principal with that id exists, so there is nobody to become. This is the lazy-provisioning signal: create the target with POST /api/principals/xauth, which is idempotent, and retry. |
500 | A fault rather than a refusal. OP_FAILED is the catch-all: the key server refused in words that match no classified pattern, so the Gateway cannot say more than that the operation did not happen. Some key server refusals that are conceptually a caller error arrive here rather than as a 4xx, because the wire carries no distinguishing code -- where a route can recognise one from its message it maps it, and each such mapping is documented on the operation. |
503 | MAJORDOMO_NOT_CONFIGURED or DELEGATION_NOT_CONFIGURED -- this Gateway holds no delegator identity, so it cannot impersonate anybody; nothing was sent to the key server. SERVICE_REGISTRY_NOT_CONFIGURED -- no service registry is loaded. SENDER_BINDING_UNAVAILABLE -- the Gateway is holding its maximum of sender-key bindings and could not record another; the session was not handed out, because a delegated credential this Gateway cannot bind would be spendable by anyone holding it. Retryable. |