Skip to content

Token and claim reference ​

GenAuth's delegation model uses two kinds of token. Their roles are different and so are their field shapes — confusing the two is the single most common integration mistake:

Delegate tokenAccess token (after exchange)
What it isAn authorization record: who delegated what to which agent, and for how longAn access credential: what the agent actually presents to a resource
Who consumes itGenAuth (for token exchange and validation)Your target resource service
Where it comes fromIssued after consent completes (see Quickstart)Exchanged from a delegate token (RFC 8693)
Has act?NoYes (a GenAuth extension — see below)

Both are JWTs (RFC 7519) and are verified the standard way.

Table 1: complete delegate token fields ​

A JWT whose token_type is eak_delegation_token. Thirteen top-level fields:

FieldTypeMeaningContract level
token_typestringAlways eak_delegation_tokenImplementation detail — fine for recognizing the token kind, but the value may change across versions; do not hard-code an equality check as your only test
issstringIssuerStable contract
substringThe delegating user's ID — the subject of a delegate token is always a personStable contract
azpstringThe access key ID that started the delegationStable contract
agent_idstringThe agent the authority was delegated toStable contract
scopestring[]Granted scopes (formatted service.capability:action, e.g. webagent.web_search:manage)Stable contract
audstringAlways genauth:token-exchange — a delegate token is only accepted by the token exchange endpointStable contract
eak_tenant_idstringThe owning workspace IDStable contract
genauth_userpool_idstringThe user pool the user belongs toImplementation detail — this field is known to be subject to a rename; resource-side code must not depend on it. Treat the version noted on this page as authoritative
resource_bindingsobjectProduct resources bound to the workspace (e.g. { gumem: { project_id }, webagent: { tenant_id } })Implementation detail
jtistringUnique token IDStable contract
grant_idstringThe grant record ID (spans the authorization lifecycle)Stable contract
audit_idstringAudit chain ID (see Audit and accountability chain)Stable contract

A delegate token has no act claim

act only appears in the exchanged access token. If you are looking for act in a delegate token, you are holding the wrong token.

Table 2: extra fields on the exchanged access token ​

The access token returned by token exchange (POST /api/v3/eak/token-exchange) carries these GenAuth extension fields in addition to the target resource's standard claims:

FieldTypeMeaning
actobjectThe actor: { "type": "eak_delegation", "agent_id": "...", "grant_id": "...", "access_key_id": "..." }
exchange_idstringUnique ID for this exchange (enters the audit chain)
product_resourceobjectThe bound target resource, shaped { "type": "gumem_project" | "webagent_tenant", "id": "..." }. This is an object, not a string — do not compare it directly against "gumem"

act is a GenAuth extension, not the RFC 8693 shape

RFC 8693 defines act as a nested sub ("act": { "sub": "..." }). GenAuth's act is an extension object with a type discriminator — resource-side code must explicitly check act.type === "eak_delegation" rather than parsing the standard nested shape. The full resource-side checklist is in Protect your APIs.

The sub of the access token is still the user — "on whose behalf" always points to a person, and "who is acting" is expressed by act. That is where delegation semantics land inside the token.

Migration and the compatibility window ​

The current @qoniai/qoni v0.4.1 package retains two deprecated shapes for compatibility. New code should use the right-hand form only in silent mode; interactive mode does not pass a user field:

OldNew
delegateAgent({ ... })delegateToken({ ... })
top-level userId: "usr_x"user: { id: "usr_x" }

A two-track compatibility window

The server-side API still accepts the top-level userId during the compatibility window. That means: even after your own code has fully migrated to user: { id }, anyone holding your access key can still start a delegation with the old parameter. Treat access key custody and rotation as your first line of defense — threat analysis in Security considerations.

Validating a token ​

  • Online validation (recommended): POST /api/v3/eak/delegations/introspect with { "token": "..." }. A valid token returns { "active": true, ...all claims }; anything invalid or expired returns { "active": false }. The SDK equivalent is qoni.genauth.introspectDelegationToken({ token }).
  • Local verification: after standard JWT signature verification, always check aud (always genauth:token-exchange for a delegate token) and expiry. For access tokens, the resource side must also check the scope intersection (see Protect your APIs).

Next steps ​