Skip to content

Passports

Shared secrets and long-lived API keys leak, and token introspection puts a network call in every request path. A TrustPlane Passport solves both problems: it is a short-lived, audience-scoped, proof-bound authorization artifact. Issuing one turns a workload identity into something a verifier can check; verifying one confirms its signature and claims against local public trust material — no shared secret, no mandatory network call.

  • Issuing is done by trustplane issue (CLI), the trustplane-issuer binary, or the broker.
  • Verification lives in pkg/authcore: it resolves the issuer key, checks the signature, and validates iss, sub, aud, iat, exp, jti, trust_domain, and cnf.
  • Keys are resolved from a TrustPlane bundle document, so verification is offline-capable.

The verification steps (from authcore):

flowchart LR
  P["Passport"] --> K["Resolve issuer key from bundle"]
  K --> S["Verify signature"]
  S --> CL["Validate iss/sub/aud/iat/exp/jti/trust_domain/cnf"]
  CL --> AUD["Check expected audience + trust domain"]
  AUD --> CNF["Check cnf (request proof binding)"]
  CNF --> RP["Replay/jti check (if enabled)"]
  RP --> R["allow / deny + reason"]

Issue a passport and write a trust bundle containing its public key:

Terminal window
./bin/trustplane issue \
--subject spiffe://example.local/ns/default/sa/demo \
--audience example-api \
--trust-domain example.local \
--bundle-out trustplane-bundle.json

Verify with an explicit public key:

Terminal window
./bin/trustplane verify \
--token "$TOKEN" \
--public-key "$PUBLIC_KEY" \
--kid "$KEY_ID" \
--issuer "$ISSUER" \
--audience example-api \
--trust-domain example.local

…or verify against the bundle (the offline-friendly path):

Terminal window
./bin/trustplane verify \
--token "$TOKEN" \
--bundle trustplane-bundle.json \
--kid "$KEY_ID" \
--issuer "$ISSUER" \
--audience example-api \
--trust-domain example.local

Run the bundled demo (issues + verifies + runs the protected-service example):

Terminal window
make demo
  • The verifier consumed only public material (the bundle). The private key never left the issuer.
  • Change --audience on verify and it fails — the passport is audience-scoped, not a general-purpose token.
  • A passport alone is not enough to call a protected route; it must be paired with request binding so the verifier can confirm the caller holds the cnf key for this request.