Human identity and machine binding โ draft
Status: Draft for joint review; not ratified or a published extension. This document proposes a custom resource-request profile. Do not advertise this path as an extension URI or infer production readiness from it.
The Workbench owner approved the custom signed-header direction for the Roblox first release on 2026-10-21. This selects the implementation direction, not ratification of the full profile or approval to provision production. The actual issuers, client claim and trust configuration remain unresolved.
Proposed request profile
The Workbench sends a machine OAuth access token as Authorization: Bearer
and a separately signed, short-lived human JWT as
X-Upperhand-Identity-Grant. Both travel server-to-server over HTTPS;
neither belongs in browser storage, URLs, transcripts or logs.
The specialist verifies both signatures and operator-pinned trust before
using claims. Proposed human claims are iss, sub, aud, iat, exp
and azp:
subidentifies the authenticated human, not caller-supplied metadata.audnames the receiving specialist. A grant for the coordinator is not automatically valid for its children.iatandexpbound the lifetime to at most 600 seconds. Verification rejects expired grants and future issuance outside an explicitly agreed clock tolerance.azpbinds the grant to the authenticated OAuth client presenting it. The comparison uses an issuer-specific, documented client identity claim from the verified machine token. Do not assumesub,client_idandazpare interchangeable.
Issuer, JWKS location, allowed algorithms, audience, scope and client-claim
mapping are operator configuration, not trust learned from a token or card.
The grant issuer signs only from an authenticated human session and an
authorized client/target relationship. It does not sign arbitrary sub
values supplied by the caller. The specialist checks machine and human
entitlements on every protected operation, including task lookup and
feedback. Missing identity fails closed; there is no production test-token
or anonymous-user fallback.
This remains a bearer profile: a stolen token/grant pair can be replayed within its validity window. Client binding prevents using a grant with a different authenticated machine; it is not proof of possession or a single-use nonce scheme.
Not ID-JAG
The IETF Identity Assertion JWT Authorization Grant draft uses token exchange with an identity provider and resource authorization server to obtain an access token. A custom second JWT header on a resource request is not that flow. Adopting ID-JAG would be a separate design with its own issuer support and tests; this draft makes no ID-JAG conformance claim.
Roblox implementation evidence, 2026-10-21
The Roblox owner
reports that src/auth.ts verifies pinned issuer/JWKS/audience, RS256 or
ES256, machine sub/exp, bls:invoke, and human sub/iat/exp with a
10-minute maximum age and lifetime. It already accepts the proposed header.
It does not read azp or client_id from either token. Entitlements
use (machine_sub, human_sub); persisted owner is
SHA256(JSON.stringify([machineSub, humanSub])). Another entitled machine
for the same human can present the same valid human grant today. Signature
verification alone therefore does not satisfy the proposed client binding.
Client binding need not change ownership. The proposed implementation
compares human azp against one operator-selected, issuer-documented
machine client claim, rejecting absent, non-string, empty or mismatched
values without fallback between claims. It retains machine sub, human
sub, entitlement lookup and the existing owner hash unchanged. If the
issuer establishes that sub identifies the client, that can be the
selected claim; otherwise use its documented client claim separately.
Changing issuer or subjects can still require entitlement/history
migration. Do not strand persisted tasks or silently broaden access.
The owner confirmed the orb runs AUTH_MODE=local; machine issuer/JWKS,
OAuth token URL, agent audience and human issuer/JWKS are unset. Local
test-user authentication does not establish a production client mapping.
Workbench implementation evidence, 2026-10-21
The local Workbench implementation signs five-minute RS256 grants from
authenticated Clerk sessions and operator-configured audience/client policy.
It passes the human subject internally to the coordinator, outside request
metadata. Direct and routed specialist calls use the same signing path.
The configured RSA key's public half is served at /.well-known/jwks.json.
Missing configuration fails closed; the private key never reaches the browser.
Signature, claim and public-key tests pass with disposable keys. Neither production trust nor Roblox's client-binding check is certified. The pilot publishes one key; overlapping-key rotation remains unimplemented.
Decisions and acceptance still required
- Provision the selected header profile's human grant issuer, specialist audiences, machine issuer and client registration after operator approval. Confirm the exact machine client claim and clock tolerance first.
- Validate the implemented session-to-target signing path with the real issuers and entitled humans. The local implementation is not evidence that production identity registration is complete.
- Agree migration of existing entitlements/history if identity keys change, and document revocation behavior within the bearer lifetime.
- Test missing/expired/wrong-audience grants, invalid signatures, unapproved issuers, absent scopes, and cross-human ownership. Critically, test two machines both entitled for one human: a grant bound to one must be rejected with the other's valid machine token.
- Exercise direct and coordinator-routed calls with the actual issuers before enabling the registry entry. Record evidence without credentials.