Skip to content

PATTERN Cited by 1 source

Third-party-caveat strip for service token

Pattern

Design user tokens with a third-party authentication caveat — but for service-token use (tokens stored alongside running code), expose an authority API that:

  1. Takes the tuple (permissions-token, discharge-token).
  2. Verifies the discharge (proving the caller could have used the token normally).
  3. Returns a new token with the third-party authentication caveat removed, and typically with no expiration.

The recipient can then attenuate that token further — binding it to a specific host, instance, or role — before handing it to the service. Theft of the resulting token is useless unless the attacker also controls the environment it's bound to.

Canonical implementation: Fly.io tkdb

From sources/2025-03-27-flyio-operationalizing-macaroons:

"The solution we came up with for service tokens is simple: tkdb exports an API that uses its access to token secrets to strip off the third-party authentication caveat. To call into that API, you have to present a valid discharging authentication token; that is, you have to prove you could already have done whatever the token said. tkdb returns a new token with all the previous caveats, minus the expiration (you don't usually want service tokens to expire)."

"OK, so we've managed to transform a tuple (unscary-token, scary-token) into the new tuple (scary-token). Not so impressive. But hold on: the recipient of scary-token can attenuate it further: we can lock it to a particular instance of flyd, or to a particular Fly Machine. Which means exfiltrating it doesn't do you any good; to use it, you have to control the environment it's intended to be used in."

Properties

  • Traceability: "every token used in production is traceable in some way to a valid token a user submitted." The strip API requires proof of prior authorization, so the service-token lineage always goes back to a user action.
  • No stored authenticator hazmat: running code never holds the auth-service discharge token ("we don't want running code to store those authenticator tokens, because they're hazardous").
  • No automatic expiration: service tokens typically don't expire — the binding-by-attenuation is the compensating control.
  • Authority-only operation: only tkdb can strip (it holds the token secrets). The operation isn't available to bearers in general.

Companion pattern

The same third-party-caveat mechanism runs in the other direction at Fly.io's Pet Semetary secret service — see patterns/caveat-for-privilege-separation — where a broadly-privileged read-secret Macaroon carries a third-party caveat that can only be discharged by proving the right org permissions through tkdb.

When it applies

Any service-token issuance where the token must live next to running code but the auth discharge would be unsafe to store there. Works only if the token construction supports caveat- stripping by the authority (Macaroons do natively because of the chained-HMAC construction — the authority holds the root key and can re-derive).

Seen in

Last updated · 200 distilled / 1,178 read