Documentation
¶
Overview ¶
Package jwt defines the domain models and types used by the jwt plugin.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type AccessToken ¶
type AccessToken struct {
// AccessToken is the JWT access token (use for API requests)
AccessToken string `json:"access_token"`
// AccessExpiry is when the access token expires (UTC)
AccessExpiry time.Time `json:"access_expiry"`
// TokenType is the auth scheme name returned to clients.
// Aegis returns the constant "Bearer".
TokenType string `json:"token_type"`
}
AccessToken represents a single access token response.
This is returned by the /getAccessToken endpoint when only an access token is needed (without a refresh token).
Example response:
{
"access_token": "eyJhbGc...",
"access_expiry": "2024-01-01T12:15:00Z"
}
type Claims ¶
type Claims struct {
// UserID is the authenticated user's ID
// Used for quick user lookup without database query
UserID string `json:"user_id"`
// TokenType indicates if this is an "access" or "refresh" token
// Access tokens can be used for API requests
// Refresh tokens can only be used to get new access tokens
TokenType string `json:"token_type"`
}
Claims defines the structure of JWT token claims.
JWT claims are the payload embedded in the token. These are NOT encrypted, only signed - anyone can decode and read them. Don't include sensitive data.
Standard claims (JWT spec):
- iss: Issuer (from Config.Issuer)
- sub: Subject (UserID)
- exp: Expiration time
- iat: Issued at time
- jti: JWT ID (for revocation tracking)
Custom claims (Aegis):
- user_id: User ID for quick lookup
- token_type: "access" or "refresh"
Example token payload:
{
"iss": "aegis",
"sub": "01ARZ3NDEKTSV4RRFFQ69G5FAV",
"exp": 1704114900,
"iat": 1704114000,
"user_id": "01ARZ3NDEKTSV4RRFFQ69G5FAV",
"token_type": "access"
}
type JWK ¶
type JWK struct {
// Kid is the Key ID (unique identifier for this key)
// Used in JWT header to identify which key was used for signing
// Format: timestamp-based or UUID
Kid string `json:"kid"`
// KeyData is the JSON-encoded JWK (includes public/private key material)
// Stored as BYTEA/BLOB in database
// For RSA: Contains n (modulus), e (exponent), d (private exponent)
KeyData []byte `json:"keyData"`
// Algorithm is the cryptographic algorithm (e.g., "RS256", "ES256")
// Currently only RS256 (RSA with SHA-256) is supported
Algorithm string `json:"algorithm"`
// Use indicates the key purpose:
// - "sig": Signature/verification (most common)
// - "enc": Encryption/decryption (future feature)
Use string `json:"use"`
// CreatedAt is when the key was generated
CreatedAt time.Time `json:"createdAt"`
// ExpiresAt is when the key should be deleted from storage
// Set to CreatedAt + KeyRetention duration
// nil means the key never expires (not recommended)
ExpiresAt *time.Time `json:"expiresAt,omitempty"`
}
JWK represents a JSON Web Key stored in the database.
JWKs are cryptographic keys used for signing and verifying JWT tokens. They are stored in the database to support:
- Key rotation: Generate new keys periodically
- Key retention: Keep old keys for verifying existing tokens
- Multi-server: Share keys across multiple application instances
Key Lifecycle:
- Generate: Create RSA key pair (private + public)
- Store: Save to database with expiry time
- Use: Sign tokens with private key, verify with public key
- Rotate: Generate new key, keep old key for verification
- Expire: Delete keys after retention period
Database Schema:
CREATE TABLE jwk_keys ( kid VARCHAR(255) PRIMARY KEY, key_data BYTEA NOT NULL, algorithm VARCHAR(50) NOT NULL, use VARCHAR(50) NOT NULL, created_at TIMESTAMP NOT NULL, expires_at TIMESTAMP );
type JWKS ¶
JWKS represents a JSON Web Key Set response.
This is returned by the /.well-known/jwks.json endpoint and contains the public keys used to verify JWT signatures.
Example response:
{
"keys": [
{
"kty": "RSA",
"use": "sig",
"kid": "access-1234567890",
"n": "...",
"e": "AQAB"
}
]
}
type LogoutResponse ¶
type LogoutResponse struct {
UserID string `json:"user_id"`
}
LogoutResponse is the data payload returned by POST /logout.
type Provider ¶
type Provider interface {
// GenerateTokenPair creates an access token + refresh token for a user.
// Returns token strings and expiry times.
GenerateTokenPair(userID string) (*TokenPair, error)
// ValidateToken validates a JWT token and returns the claims.
// Checks signature, expiry, issuer, and token type.
ValidateToken(token string) (*Claims, error)
// RefreshTokens validates a refresh token and generates a new token pair.
// Old refresh token is invalidated (single-use refresh tokens).
RefreshTokens(refreshToken string) (*TokenPair, error)
// BlacklistToken adds a token to the blacklist (for logout/revocation).
// Requires Redis for distributed blacklist.
BlacklistToken(token string) error
}
Provider defines an interface for custom JWT token generation and validation.
Implement this interface to customize JWT token handling:
- Custom claims structure
- Custom signing algorithms
- Custom token validation logic
- Integration with external JWT services
The default implementation (Plugin) uses RSA signing with database-backed keys. Most applications don't need a custom provider.
type Store ¶
type Store interface {
// GetCurrentJWK retrieves the most recent active key for signing.
//
// This is used during token generation to get the private key for signing.
// Returns the newest non-expired key matching the algorithm and use.
//
// Parameters:
// - ctx: Context for cancellation and timeouts
// - algorithm: Key algorithm (e.g., "RS256")
// - use: Key use ("sig" for signing, "enc" for encryption)
//
// Returns:
// - jwk.Key: The most recent active key
// - error: Database errors or "key not found"
//
// Example:
//
// privateKey, err := store.GetCurrentJWK(ctx, "RS256", "sig")
// if err != nil {
// // No active key - generate new one
// }
GetCurrentJWK(ctx context.Context, algorithm, use string) (jwk.Key, error)
// StoreJWK persists a JWK to the database with expiry.
//
// This is used during:
// - Initial key generation (plugin initialization)
// - Key rotation (periodic background job)
//
// The key is stored with an expiry time for automatic cleanup.
// Expired keys are kept to verify old tokens until cleanup.
//
// Parameters:
// - ctx: Context for cancellation and timeouts
// - key: JWK to store (includes private/public key material)
// - algorithm: Key algorithm (e.g., "RS256")
// - use: Key use ("sig" or "enc")
// - expiresAt: When to delete this key (nil = never expires)
//
// Returns:
// - error: Database errors or duplicate key ID
//
// Example:
//
// expiresAt := time.Now().Add(30 * 24 * time.Hour)
// err := store.StoreJWK(ctx, privateKey, "RS256", "sig", &expiresAt)
StoreJWK(ctx context.Context, key jwk.Key, algorithm, use string, expiresAt *time.Time) error
// DeleteExpiredJWKS removes all expired keys from storage.
//
// This is called periodically to clean up old keys that are no longer needed.
// Only deletes keys where:
// - expires_at IS NOT NULL (keys with expiry set)
// - expires_at < NOW() (expiry time has passed)
//
// Note: Keys are kept past their "active" period to verify old tokens.
// The expiry should be set to created_at + KeyRetention duration.
//
// Parameters:
// - ctx: Context for cancellation and timeouts
//
// Returns:
// - error: Database errors
//
// Example:
//
// if err := store.DeleteExpiredJWKS(ctx); err != nil {
// log.Printf("Failed to cleanup expired keys: %v", err)
// }
DeleteExpiredJWKS(ctx context.Context) error
// ListJWKS returns all active (non-expired) keys.
//
// This is used by:
// - JWKS endpoint: Expose public keys for token verification
// - Token validation: Find key by kid (key ID) for signature verification
//
// Only returns keys where:
// - expires_at IS NULL (never expires), OR
// - expires_at > NOW() (not yet expired)
//
// Parameters:
// - ctx: Context for cancellation and timeouts
//
// Returns:
// - []JWK: List of active keys (includes both public and private keys)
// - error: Database errors
//
// Example:
//
// keys, err := store.ListJWKS(ctx)
// for _, key := range keys {
// fmt.Printf("Key: %s, Algorithm: %s\n", key.Kid, key.Algorithm)
// }
ListJWKS(ctx context.Context) ([]JWK, error)
}
Store defines the interface for JWT token and key storage operations.
This interface abstracts database operations for JWK (JSON Web Key) storage, allowing different storage backends:
- SQL databases: PostgreSQL, MySQL, SQLite (default)
- NoSQL databases: MongoDB, DynamoDB (custom implementation)
- Cloud key vaults: AWS KMS, Google Cloud KMS (custom implementation)
The default implementation (DefaultStore) uses SQL with sqlc-generated queries.
Key Storage Requirements:
- Persistence: Keys must survive application restarts
- Multi-server: Keys must be shared across application instances
- Key rotation: Support storing multiple active keys
- Key expiry: Automatic cleanup of expired keys
Thread Safety:
Implementations must be thread-safe for concurrent access from:
- Token generation requests (read current key)
- Key rotation background job (write new keys)
- Token validation requests (read all active keys)
- JWKS endpoint requests (list public keys)
type TokenPair ¶
type TokenPair struct {
// AccessToken is the JWT access token (use for API requests)
AccessToken string `json:"access_token"`
// AccessExpiry is when the access token expires (UTC)
AccessExpiry time.Time `json:"access_expiry"`
// RefreshToken is the JWT refresh token (use to get new access token)
RefreshToken string `json:"refresh_token"`
// RefreshExpiry is when the refresh token expires (UTC)
RefreshExpiry time.Time `json:"refresh_expiry"`
}
TokenPair represents a complete set of access and refresh tokens.
This is returned by token generation and refresh endpoints. Clients should:
- Store access token for API requests (short-lived)
- Store refresh token securely for obtaining new access tokens (long-lived)
- Use refresh token before access token expires
Example response:
{
"access_token": "eyJhbGc...",
"access_expiry": "2024-01-01T12:15:00Z",
"refresh_token": "eyJhbGc...",
"refresh_expiry": "2024-01-08T12:00:00Z"
}