Documentation
¶
Index ¶
- Variables
- type AddKidRequest
- type AddKidResponse
- type CleanExpiredRequest
- type CleanExpiredResponse
- type CompositeKeyLookup
- type DeleteKeyRequest
- type DeleteKeyResponse
- type EncryptedKeyStorage
- func (e *EncryptedKeyStorage) DeleteKey(ctx context.Context, req *DeleteKeyRequest) (*DeleteKeyResponse, error)
- func (e *EncryptedKeyStorage) GetKey(ctx context.Context, req *GetKeyRequest) (*GetKeyResponse, error)
- func (e *EncryptedKeyStorage) GetKeyByKid(ctx context.Context, req *GetKeyByKidRequest) (*GetKeyByKidResponse, error)
- func (e *EncryptedKeyStorage) ListKeyIDs(ctx context.Context, req *ListKeyIDsRequest) (*ListKeyIDsResponse, error)
- func (e *EncryptedKeyStorage) PutKey(ctx context.Context, req *PutKeyRequest) (*PutKeyResponse, error)
- type GetKeyByKidRequest
- type GetKeyByKidResponse
- type GetKeyRequest
- type GetKeyResponse
- type InMemoryKeyStore
- func (s *InMemoryKeyStore) DeleteKey(ctx context.Context, req *DeleteKeyRequest) (*DeleteKeyResponse, error)
- func (s *InMemoryKeyStore) GetKey(ctx context.Context, req *GetKeyRequest) (*GetKeyResponse, error)
- func (s *InMemoryKeyStore) GetKeyByKid(ctx context.Context, req *GetKeyByKidRequest) (*GetKeyByKidResponse, error)
- func (s *InMemoryKeyStore) ListKeyIDs(ctx context.Context, req *ListKeyIDsRequest) (*ListKeyIDsResponse, error)
- func (s *InMemoryKeyStore) PutKey(ctx context.Context, req *PutKeyRequest) (*PutKeyResponse, error)
- type JWKSHandler
- type JWKSKeyStore
- type JWKSOption
- type KeyLookup
- type KeyRecord
- type KeyStorage
- type KidStorage
- type KidStore
- func (s *KidStore) Add(ctx context.Context, req *AddKidRequest) (*AddKidResponse, error)
- func (s *KidStore) CleanExpired(ctx context.Context, req *CleanExpiredRequest) (*CleanExpiredResponse, error)
- func (s *KidStore) GetKey(ctx context.Context, req *GetKeyRequest) (*GetKeyResponse, error)
- func (s *KidStore) GetKeyByKid(ctx context.Context, req *GetKeyByKidRequest) (*GetKeyByKidResponse, error)
- func (s *KidStore) Len() int
- func (s *KidStore) Remove(ctx context.Context, req *RemoveKidRequest) (*RemoveKidResponse, error)
- type ListKeyIDsRequest
- type ListKeyIDsResponse
- type PutKeyRequest
- type PutKeyResponse
- type RemoveKidRequest
- type RemoveKidResponse
Constants ¶
This section is empty.
Variables ¶
var ( ErrKeyNotFound = fmt.Errorf("signing key not found") ErrAlgorithmMismatch = fmt.Errorf("algorithm mismatch") ErrKidNotFound = fmt.Errorf("key not found for kid") )
Common errors for key operations
Functions ¶
This section is empty.
Types ¶
type AddKidRequest ¶ added in v0.1.7
type AddKidRequest struct {
Kid string
Key any
Algorithm string
ClientID string
ExpiresAt time.Time
}
AddKidRequest is the input to KidStorage.Add. If ExpiresAt is zero, the kid has no expiry (current key).
type AddKidResponse ¶ added in v0.1.7
type AddKidResponse struct{}
AddKidResponse is the output of KidStorage.Add.
type CleanExpiredRequest ¶ added in v0.1.7
type CleanExpiredRequest struct{}
CleanExpiredRequest is the input to KidStorage.CleanExpired.
type CleanExpiredResponse ¶ added in v0.1.7
type CleanExpiredResponse struct {
// Removed is the number of expired records evicted by this call.
Removed int
}
CleanExpiredResponse is the output of KidStorage.CleanExpired.
type CompositeKeyLookup ¶
type CompositeKeyLookup struct {
Lookups []KeyLookup
}
CompositeKeyLookup tries multiple KeyLookups in order, returning the first successful result. Used to combine a KeyStorage's current-key lookup with a KidStore's grace-period entries.
func (*CompositeKeyLookup) GetKey ¶
func (c *CompositeKeyLookup) GetKey(ctx context.Context, req *GetKeyRequest) (*GetKeyResponse, error)
GetKey tries each lookup in order for a client_id match.
func (*CompositeKeyLookup) GetKeyByKid ¶
func (c *CompositeKeyLookup) GetKeyByKid(ctx context.Context, req *GetKeyByKidRequest) (*GetKeyByKidResponse, error)
GetKeyByKid tries each lookup in order for a kid match.
type DeleteKeyRequest ¶ added in v0.1.7
type DeleteKeyRequest struct {
ClientID string
}
DeleteKeyRequest is the input to KeyStorage.DeleteKey.
type DeleteKeyResponse ¶ added in v0.1.7
type DeleteKeyResponse struct{}
DeleteKeyResponse is the output of KeyStorage.DeleteKey.
type EncryptedKeyStorage ¶
type EncryptedKeyStorage struct {
// contains filtered or unexported fields
}
EncryptedKeyStorage is a KeyStorage decorator that transparently encrypts HMAC (HS256/HS384/HS512) client secrets at rest using AES-256-GCM. Asymmetric keys (RS256, ES256 public keys) pass through unencrypted.
Because it wraps KeyStorage and operates on KeyRecord, it only needs to implement 5 methods — no manual forwarding of individual field accessors.
The kid is computed from plaintext key material in PutKey before encryption, so kid-based lookups work correctly even though the stored bytes are encrypted.
func NewEncryptedKeyStorage ¶
func NewEncryptedKeyStorage(inner KeyStorage, masterKeyHex string) (*EncryptedKeyStorage, error)
NewEncryptedKeyStorage creates an EncryptedKeyStorage wrapping inner. masterKeyHex must be exactly 64 hex characters (32 bytes).
func (*EncryptedKeyStorage) DeleteKey ¶
func (e *EncryptedKeyStorage) DeleteKey(ctx context.Context, req *DeleteKeyRequest) (*DeleteKeyResponse, error)
DeleteKey delegates to the inner store.
func (*EncryptedKeyStorage) GetKey ¶
func (e *EncryptedKeyStorage) GetKey(ctx context.Context, req *GetKeyRequest) (*GetKeyResponse, error)
GetKey retrieves and decrypts the key for the given clientID.
func (*EncryptedKeyStorage) GetKeyByKid ¶
func (e *EncryptedKeyStorage) GetKeyByKid(ctx context.Context, req *GetKeyByKidRequest) (*GetKeyByKidResponse, error)
GetKeyByKid retrieves and decrypts the key matching the given kid.
func (*EncryptedKeyStorage) ListKeyIDs ¶
func (e *EncryptedKeyStorage) ListKeyIDs(ctx context.Context, req *ListKeyIDsRequest) (*ListKeyIDsResponse, error)
ListKeyIDs delegates to the inner store.
func (*EncryptedKeyStorage) PutKey ¶
func (e *EncryptedKeyStorage) PutKey(ctx context.Context, req *PutKeyRequest) (*PutKeyResponse, error)
PutKey computes kid from plaintext, then encrypts HMAC keys before storing.
type GetKeyByKidRequest ¶ added in v0.1.7
type GetKeyByKidRequest struct {
Kid string
}
GetKeyByKidRequest is the input to KeyLookup.GetKeyByKid.
type GetKeyByKidResponse ¶ added in v0.1.7
type GetKeyByKidResponse struct {
Record *KeyRecord
}
GetKeyByKidResponse is the output of KeyLookup.GetKeyByKid.
type GetKeyRequest ¶ added in v0.1.7
type GetKeyRequest struct {
ClientID string
}
GetKeyRequest is the input to KeyLookup.GetKey.
type GetKeyResponse ¶ added in v0.1.7
type GetKeyResponse struct {
Record *KeyRecord
}
GetKeyResponse is the output of KeyLookup.GetKey.
type InMemoryKeyStore ¶
type InMemoryKeyStore struct {
// contains filtered or unexported fields
}
InMemoryKeyStore is a thread-safe in-memory KeyStorage implementation. Suitable for testing and simple single-process deployments.
func NewInMemoryKeyStore ¶
func NewInMemoryKeyStore() *InMemoryKeyStore
NewInMemoryKeyStore creates a new empty InMemoryKeyStore.
func (*InMemoryKeyStore) DeleteKey ¶
func (s *InMemoryKeyStore) DeleteKey(ctx context.Context, req *DeleteKeyRequest) (*DeleteKeyResponse, error)
DeleteKey removes the key for the given clientID.
func (*InMemoryKeyStore) GetKey ¶
func (s *InMemoryKeyStore) GetKey(ctx context.Context, req *GetKeyRequest) (*GetKeyResponse, error)
GetKey returns the key record for the given clientID.
func (*InMemoryKeyStore) GetKeyByKid ¶
func (s *InMemoryKeyStore) GetKeyByKid(ctx context.Context, req *GetKeyByKidRequest) (*GetKeyByKidResponse, error)
GetKeyByKid returns the key record matching the given kid.
func (*InMemoryKeyStore) ListKeyIDs ¶
func (s *InMemoryKeyStore) ListKeyIDs(ctx context.Context, req *ListKeyIDsRequest) (*ListKeyIDsResponse, error)
ListKeyIDs returns all registered client IDs.
func (*InMemoryKeyStore) PutKey ¶
func (s *InMemoryKeyStore) PutKey(ctx context.Context, req *PutKeyRequest) (*PutKeyResponse, error)
PutKey stores a key record. Computes Kid from key material if not set.
type JWKSHandler ¶
type JWKSHandler struct {
KeyStore KeyStorage // needs ListKeyIDs() and GetKey()
KidStore *KidStore // optional: serves previous keys during grace period
CacheMaxAge int // Cache-Control max-age in seconds (default: 3600)
}
JWKSHandler serves a JWKS (JSON Web Key Set) endpoint at /.well-known/jwks.json. Only asymmetric keys (RS256/ES256) are included — HS256 secrets are never exposed.
func (*JWKSHandler) ServeHTTP ¶
func (h *JWKSHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
type JWKSKeyStore ¶
type JWKSKeyStore struct {
JWKSURL string
HTTPClient *http.Client
RefreshInterval time.Duration // default: 1 hour
MinRefreshGap time.Duration // default: 5 seconds
// contains filtered or unexported fields
}
JWKSKeyStore implements KeyLookup (read-only) by fetching public keys from a remote JWKS endpoint. It caches keys locally and refreshes them periodically.
func NewJWKSKeyStore ¶
func NewJWKSKeyStore(jwksURL string, opts ...JWKSOption) *JWKSKeyStore
NewJWKSKeyStore creates a new JWKSKeyStore. Call Start() to begin fetching keys.
func (*JWKSKeyStore) GetKey ¶
func (s *JWKSKeyStore) GetKey(ctx context.Context, req *GetKeyRequest) (*GetKeyResponse, error)
GetKey returns ErrKeyNotFound — JWKSKeyStore only supports kid-based lookup. JWKS doesn't carry clientID→key mappings; use GetKeyByKid instead.
func (*JWKSKeyStore) GetKeyByKid ¶
func (s *JWKSKeyStore) GetKeyByKid(ctx context.Context, req *GetKeyByKidRequest) (*GetKeyByKidResponse, error)
GetKeyByKid returns the key record for the given kid. ClientID is empty since JWKS doesn't carry client_id metadata — the middleware skips the cross-app check in this case.
func (*JWKSKeyStore) Start ¶
func (s *JWKSKeyStore) Start() error
Start performs the initial JWKS fetch and starts background refresh.
func (*JWKSKeyStore) Stop ¶
func (s *JWKSKeyStore) Stop()
Stop stops the background refresh goroutine.
type JWKSOption ¶
type JWKSOption func(*JWKSKeyStore)
JWKSOption configures a JWKSKeyStore.
func WithHTTPClient ¶
func WithHTTPClient(c *http.Client) JWKSOption
WithHTTPClient sets the HTTP client for JWKS fetching.
func WithMinRefreshGap ¶
func WithMinRefreshGap(d time.Duration) JWKSOption
WithMinRefreshGap sets the minimum time between refreshes (prevents stampede).
func WithRefreshInterval ¶
func WithRefreshInterval(d time.Duration) JWKSOption
WithRefreshInterval sets how often keys are refreshed in the background.
type KeyLookup ¶
type KeyLookup interface {
// GetKey returns the key record for the given clientID.
// Returns ErrKeyNotFound if the client has no registered key.
GetKey(ctx context.Context, req *GetKeyRequest) (*GetKeyResponse, error)
// GetKeyByKid returns the key record matching the given kid.
// Returns ErrKidNotFound if no key matches or the key has expired.
GetKeyByKid(ctx context.Context, req *GetKeyByKidRequest) (*GetKeyByKidResponse, error)
}
KeyLookup provides read-only key lookup by clientID or kid. Implemented by all keystores, including read-only ones like JWKSKeyStore.
type KeyRecord ¶
type KeyRecord struct {
ClientID string // owning client/app
Key any // []byte for HMAC, PEM bytes for asymmetric
Algorithm string // "HS256", "RS256", "ES256", etc.
Kid string // key identifier, computed from key material
}
KeyRecord holds all fields for a stored signing key. All key operations work with this type rather than separate accessor methods.
type KeyStorage ¶
type KeyStorage interface {
KeyLookup
// PutKey stores a key record. If req.Record.Kid is empty, it is
// auto-computed from the key material and algorithm. Overwrites any
// existing key for the same ClientID.
PutKey(ctx context.Context, req *PutKeyRequest) (*PutKeyResponse, error)
// DeleteKey removes the key for the given clientID.
DeleteKey(ctx context.Context, req *DeleteKeyRequest) (*DeleteKeyResponse, error)
// ListKeyIDs returns all registered client IDs.
ListKeyIDs(ctx context.Context, req *ListKeyIDsRequest) (*ListKeyIDsResponse, error)
}
KeyStorage extends KeyLookup with write operations. Implemented by persistent backends (InMemory, GORM, FS, GAE).
type KidStorage ¶ added in v0.0.83
type KidStorage interface {
KeyLookup
// Add registers a kid→key mapping. If req.ExpiresAt is zero, the key
// has no expiry. Re-adding an existing kid overwrites it.
Add(ctx context.Context, req *AddKidRequest) (*AddKidResponse, error)
// Remove deletes a kid entry. Removing an absent kid is not an error.
Remove(ctx context.Context, req *RemoveKidRequest) (*RemoveKidResponse, error)
// CleanExpired removes all entries whose expiry has passed.
CleanExpired(ctx context.Context, req *CleanExpiredRequest) (*CleanExpiredResponse, error)
}
KidStorage is the write side of a kid-indexed key store. It extends the read-only KeyLookup with the grace-period operations KidStore provides, so retired keys can be persisted across process restarts by backends in stores/ (FS, GORM, GAE) rather than living only in process memory.
Unlike KeyStorage, KidStorage is keyed by kid (not clientID): GetKey by clientID always returns ErrKeyNotFound — only GetKeyByKid is meaningful.
type KidStore ¶
type KidStore struct {
// contains filtered or unexported fields
}
KidStore is an in-memory KidStorage that tracks kid→key mappings, including grace-period entries retained during key rotation.
Usage during rotation:
- kidStore.Add(ctx, &AddKidRequest{Kid: oldKid, Key: oldKey, Algorithm: alg, ClientID: clientID, ExpiresAt: time.Now().Add(gracePeriod)})
- keyStorage.PutKey(ctx, &PutKeyRequest{Record: newRecord}) // overwrites current
- kidStore holds the old key until grace period expires
func (*KidStore) Add ¶
func (s *KidStore) Add(ctx context.Context, req *AddKidRequest) (*AddKidResponse, error)
Add registers a kid→key mapping. If req.ExpiresAt is zero, the key has no expiry.
func (*KidStore) CleanExpired ¶
func (s *KidStore) CleanExpired(ctx context.Context, req *CleanExpiredRequest) (*CleanExpiredResponse, error)
CleanExpired removes all expired entries.
func (*KidStore) GetKey ¶
func (s *KidStore) GetKey(ctx context.Context, req *GetKeyRequest) (*GetKeyResponse, error)
GetKey always returns ErrKeyNotFound — KidStore only supports kid-based lookup.
func (*KidStore) GetKeyByKid ¶
func (s *KidStore) GetKeyByKid(ctx context.Context, req *GetKeyByKidRequest) (*GetKeyByKidResponse, error)
GetKeyByKid returns the key record for the given kid. Returns ErrKidNotFound if the kid is unknown or expired.
func (*KidStore) Remove ¶
func (s *KidStore) Remove(ctx context.Context, req *RemoveKidRequest) (*RemoveKidResponse, error)
Remove deletes a kid entry. Removing an absent kid is not an error.
type ListKeyIDsRequest ¶ added in v0.1.7
type ListKeyIDsRequest struct{}
ListKeyIDsRequest is the input to KeyStorage.ListKeyIDs.
type ListKeyIDsResponse ¶ added in v0.1.7
type ListKeyIDsResponse struct {
ClientIDs []string
}
ListKeyIDsResponse is the output of KeyStorage.ListKeyIDs.
type PutKeyRequest ¶ added in v0.1.7
type PutKeyRequest struct {
Record *KeyRecord
}
PutKeyRequest is the input to KeyStorage.PutKey.
type PutKeyResponse ¶ added in v0.1.7
type PutKeyResponse struct{}
PutKeyResponse is the output of KeyStorage.PutKey.
type RemoveKidRequest ¶ added in v0.1.7
type RemoveKidRequest struct {
Kid string
}
RemoveKidRequest is the input to KidStorage.Remove.
type RemoveKidResponse ¶ added in v0.1.7
type RemoveKidResponse struct{}
RemoveKidResponse is the output of KidStorage.Remove.