Documentation
¶
Overview ¶
Package assertion mints the identity assertions the proxy passes to apps, and publishes the keys that verify them.
An assertion is the only thing an app can trust about who is calling. The convenience headers alongside it are explicitly unverified (R-053), so the signature here is what separates "Pando says this is Alice" from "a client typed Alice into a header".
Package assertion mints the identity assertions the proxy passes to apps, and publishes the JWKS that verifies them.
Ed25519, aud bound to the app ID to prevent cross-app replay, 120s lifetime, minted per request (R-051, R-055). The sub claim is users.id, stable across email change and independent of the identity adapter — apps key their data on it (R-054).
Index ¶
- Constants
- func KeyID(pub ed25519.PublicKey) string
- type Claims
- type Key
- type Minter
- func (m *Minter) JWKS(ctx context.Context) map[string]any
- func (m *Minter) Mint(c Claims) (string, error)
- func (m *Minter) Rotate() error
- func (m *Minter) SigningKey() PublicKey
- func (m *Minter) SigningKeyID() string
- func (m *Minter) Verify(token string) (Claims, error)
- func (m *Minter) WithPeers(p PeerKeys) *Minter
- type PeerKeys
- type PublicKey
Constants ¶
const AnonymousSubject = "anonymous"
AnonymousSubject is the constant `sub` for an unauthenticated request (R-056).
A constant rather than an empty claim, so an app can tell "Pando says nobody is signed in" from "this request did not come through Pando" — the latter being the absence of the assertion header entirely.
const Header = "X-Pando-Assertion"
Header is where the assertion travels.
const Lifetime = 120 * time.Second
Lifetime is how long an assertion is valid (R-055).
It is also the interval at which long-lived connections are re-authorized (O-13) and the width of the revocation window (design 06 §3.1). One constant, referenced from all three places: two clocks measuring the same thing drift apart the first time someone tunes one of them.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type Claims ¶
type Claims struct {
// Sub is users.id, stable across email change and independent of the
// identity adapter (R-054). Apps key their data on it, which makes it the
// single most important stability guarantee in the system.
Sub string `json:"sub"`
Email string `json:"email,omitempty"`
Name string `json:"name,omitempty"`
Groups []string `json:"groups,omitempty"`
// Aud is the app ID. It is what stops an assertion minted for one app being
// replayed against another.
Aud string `json:"aud"`
Iat int64 `json:"iat"`
Exp int64 `json:"exp"`
Iss string `json:"iss"`
}
Claims is the assertion body.
type Key ¶
type Key struct {
ID string
Public ed25519.PublicKey
// NotAfter is when this key stops being used for signing. It stays in the
// JWKS past that point so assertions already issued still verify — which is
// what makes rotation an overlap rather than a cliff (R-057).
NotAfter time.Time
// contains filtered or unexported fields
}
Key is an Ed25519 signing key with an identifier.
type Minter ¶
type Minter struct {
// contains filtered or unexported fields
}
Minter signs assertions and publishes the verifying keys.
func (*Minter) JWKS ¶
JWKS returns the public keys, for publication at /.well-known/jwks.json: this process's own, and every peer's.
Read from the peers per request rather than cached. An app that meets an unknown kid fetches the JWKS again, and a cached list would answer that fetch without the key it came for. If the peers cannot be read the process's own keys are still published: the alternative is publishing nothing, which fails every app rather than some.
func (*Minter) Mint ¶
Mint signs an assertion for one request.
Minted per request rather than cached: the lifetime is short enough that caching buys little, and a cached assertion would outlive a revocation by however long the cache held it.
func (*Minter) Rotate ¶
Rotate generates a new signing key and retires the current one.
The retired key keeps verifying until every assertion it signed has expired, which is at most Lifetime. Publishing both and switching the signer is the propagation window R-057 asks for.
func (*Minter) SigningKey ¶
SigningKey is the public half of the key this process signs with, for publishing to the other replicas.
func (*Minter) SigningKeyID ¶
SigningKeyID returns the key currently used to sign.
type PeerKeys ¶
PeerKeys lists the verifying keys of the other Pando processes serving this install (issue #72).
Each replica signs with a key of its own that never leaves its memory, and publishes the public half where the others can read it. An app may fetch the JWKS from one replica and receive an assertion signed by another, so every replica's JWKS has to carry every key that may have signed something still valid — otherwise the app sees a kid nobody publishes and refuses a caller Pando vouched for (R-051).