Skip to content

v0.37.0 — delegation grants

Choose a tag to compare

@rustyconover rustyconover released this 20 Sep 22:30
· 170 commits to main since this release

A worker can mint delegation grants

vgi_rpc.Identity.v1 has always had two methods. Only one was reachable:
_build_identity passed resolve_token= and nothing else, and mint_grant
existed nowhere in this package, so offered_methods() could never include
issue_grant however a worker was written. The protocol was designed; the
wiring was missing.

Worker.mint_grant is the hook, detected by override exactly as resolve_token
is — identity comparison against the base, not a "supports grants" flag, because
the method must be absent unless a worker actually wrote the minting and a flag
can be set without one. A worker may now resolve credentials, mint grants, or
both, and the protocol it hosts describes what it does rather than what the
version supports.

class MyWorker(Worker):
    @classmethod
    def mint_grant(cls, principal, purpose, scopes, ttl_seconds) -> IssuedGrant:
        ...

What a grant is for. OAuth cannot express durable delegation: it fuses the
grant, the credential and the session into one refresh token, so an IdP
shortening session lifetime shortens the grant. This mints the durable record —
created while the user is present, presented later by unattended automation as
an ordinary bearer.

Two guards, both load-bearing

  • No subject on the wire. issue_grant has no subject field; the framework
    passes the caller's own authenticated principal. Cross-subject minting is
    closed by construction rather than by a check, which is why this needs no
    allowlist while introspect_token has one. The introspector allowlist is now
    passed only when a resolver is supplied — requiring one from a worker that
    mints but resolves nothing would be a startup error for a worker that is not
    an oracle, the opposite of the guard's purpose.

  • No auth_time, no minting. A credential that cannot prove a recent human
    login is refused. That single rule stops a grant minting another grant — a
    grant is not IdP-issued, so it carries no auth_time — and makes a static
    bearer token, which proves a machine holds a secret rather than that anyone
    authenticated, fail closed.

On revocation

IssuedGrant.token is opaque to the framework: a sealed envelope, a database
row, or an IdP-brokered credential are equally valid and equally invisible. Be
clear-eyed about what a self-contained sealed token costs — with no server-side
record there is nothing to delete, so individual grants cannot be revoked and
rotating the sealing key invalidates all of them at once. A deployment that
needs per-grant revocation has to put something lookupable in the token.

Also

vgi.auth re-exports IssuedGrant and a GrantMinter alias, so a worker
author never names a private vgi_rpc module.

Rowfence forwards vgi_rpc.Identity.v1/issue_grant from this release onward
(its sibling introspect_token stays unforwarded — the proxy holds an
introspector credential, so relaying a client's call would make it an oracle).