Documentation
¶
Overview ¶
Package kmsaws implements a jwt.Signer that delegates the signature operation to AWS KMS. Private key material never leaves KMS; rotation is performed by switching the active key ARN in the signer's configuration (typically: create a new asymmetric KMS key, add it to the active set with the new kid as primary, wait for the access-token TTL, drop the old key).
AWS KMS is the in-tree KMS reference for this server. We picked AWS over GCP KMS because (a) it has the smaller transitive dependency footprint in `go list -deps` and the fewer required IAM auxiliary services for a single-key deployment, and (b) the SDK's RSASSA_PKCS1_V1_5_SHA_256 + DIGEST signing flow maps 1:1 onto RS256 without an extra envelope. Adding a GCP-KMS or Vault backend is a matter of implementing jwt.Signer in a sibling package — the verifier, JWKS handler, and every caller in the identity service already speak only to the interface.
CI: the KMS backend is exercised against an in-process fake KMS client because real AWS credentials are not available in identity's CI matrix. A nightly / pre-release smoke against a real KMS key must run out-of-band before deploying this backend in production; see docs/key-rotation.md.
Index ¶
- type API
- type Config
- type KeyRef
- type Signer
- func (s *Signer) ActiveKID() string
- func (s *Signer) Get(kid string) (*rsa.PublicKey, bool)
- func (s *Signer) Keys() []jwt.PublicKey
- func (s *Signer) SignAccessToken(ctx context.Context, claims jwt.Claims, expiry time.Duration) (string, error)
- func (s *Signer) SignClaims(ctx context.Context, claims map[string]any) (string, error)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type API ¶
type API interface {
Sign(ctx context.Context, in *kms.SignInput, optFns ...func(*kms.Options)) (*kms.SignOutput, error)
GetPublicKey(ctx context.Context, in *kms.GetPublicKeyInput, optFns ...func(*kms.Options)) (*kms.GetPublicKeyOutput, error)
}
API is the subset of the AWS KMS client surface this signer uses. Exposed so tests can plug a fake; the production wiring passes a real *kms.Client.
type Config ¶
type Config struct {
// Keys is the list of KMS-managed signing keys. Must contain at
// least one in-force entry at New time.
Keys []KeyRef
// API is the AWS KMS client. Production callers pass a real
// *kms.Client (constructed from aws-sdk-go-v2/config); tests pass
// a stub.
API API
// Now overrides time.Now() for tests.
Now func() time.Time
}
Config wires up a KMS-backed signer.
type KeyRef ¶
type KeyRef struct {
// KID is the JWS "kid" header value advertised in the JWKS
// document. Stable across rotations of the same logical key; new
// KMS key versions get fresh kids so verifiers can distinguish.
KID string
// KeyARN is the KMS key identifier (ARN, key ID, alias name, or
// alias ARN — anything KMS accepts as KeyId).
KeyARN string
// NotBefore / ExpiresAt control rotation windows on the signer
// side. Same semantics as [jwt.PublicKey].
NotBefore time.Time
ExpiresAt time.Time
}
KeyRef identifies one KMS key entry in the signer's configuration.
func ARNFromConfig ¶
ARNFromConfig is a small helper for cmd/identity: parse the CSV-format key configuration into a []KeyRef.
Format: "kid=ARN[,kid=ARN,...]". The single-key form "kid=ARN" or just "ARN" (with kid defaulting to a fingerprint of the ARN) is also accepted. NotBefore / ExpiresAt are not exposed via this helper — deployers wanting per-key rotation windows wire the config struct directly.
type Signer ¶
type Signer struct {
// contains filtered or unexported fields
}
Signer implements jwt.Signer against AWS KMS.
func New ¶
New constructs a Signer. It fetches every key's public half via GetPublicKey so the signer can publish JWKS without round-tripping to KMS per JWKS request. Returns an error when no in-force key is present or when any GetPublicKey call fails.