apikeys

package
v1.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 23, 2026 License: MIT, MIT Imports: 19 Imported by: 0

README

GoModularAuth API Keys Plugin (gomodularauth-api-key)

Production-grade API key authentication, lifecycle management, and identity resolution plugin for GoModularAuth.

Installation

go get github.com/BladiCreator/go-modular-auth/plugins/api-keys

Quick Start


Detailed guide: doc/plugins/api-keys.md.

Documentation

Overview

Package apikeys provides API key authentication, lifecycle management, and resolution into GoModularAuth Core Principal semantics.

Package apikeys implements cryptographically secure API key issuance, scoping, and validation.

Index

Constants

View Source
const (
	// DefaultKeyPrefix is the default prefix prepended to issued API keys.
	DefaultKeyPrefix = "gma_"

	// EntropyBytes is the number of cryptographically secure random bytes generated for each key (256 bits).
	EntropyBytes = 32

	// PublicPrefixLength defines how many characters from the start of rawKey are retained for human display.
	PublicPrefixLength = 12
)
View Source
const (
	EventAPIKeyCreated       auth.EventName = "apikeys.created"
	EventAPIKeyRevoked       auth.EventName = "apikeys.revoked"
	EventAPIKeyRotated       auth.EventName = "apikeys.rotated"
	EventAPIKeyAuthenticated auth.EventName = "apikeys.authenticated"
	EventAPIKeyDeleted       auth.EventName = "apikeys.deleted"
)

Event names emitted by the apikeys module.

View Source
const (
	ModelAPIKeys = "api_keys"
)
View Source
const PluginID = "apikeys"

PluginID is the unique identifier for this plugin module in GoModularAuth.

Variables

View Source
var (
	// ErrInvalidKey is returned when the provided API key token is malformed, unrecognized, or fails verification.
	ErrInvalidKey = errors.New("apikeys: invalid API key")

	// ErrKeyNotFound is returned when the requested API key ID does not exist.
	ErrKeyNotFound = errors.New("apikeys: API key not found")

	// ErrKeyRevoked is returned when attempting to authenticate or use an API key that has been revoked.
	ErrKeyRevoked = errors.New("apikeys: API key has been revoked")

	// ErrKeyExpired is returned when an API key has passed its expiration timestamp.
	ErrKeyExpired = errors.New("apikeys: API key has expired")

	// ErrInsufficientScope is returned when an authenticated API key lacks one or more required scopes.
	ErrInsufficientScope = errors.New("apikeys: insufficient scope")

	// ErrInsufficientPermission is returned when an authenticated API key lacks one or more required permissions.
	ErrInsufficientPermission = errors.New("apikeys: insufficient permission")

	// ErrRateLimited is returned when API key verification attempts exceed rate limits (brute-force defense).
	ErrRateLimited = errors.New("apikeys: rate limit exceeded, authentication throttled")

	// ErrInvalidInput is returned when creation, rotation, or update arguments fail domain validation.
	ErrInvalidInput = errors.New("apikeys: invalid input")

	// ErrConflict is returned when an API key constraint is violated (e.g. duplicate key ID).
	ErrConflict = errors.New("apikeys: API key conflict")

	// ErrStoreRequired is returned during plugin initialization when no Store adapter is configured.
	ErrStoreRequired = errors.New("apikeys: store adapter is required")
)

Functions

func ConstantTimeEqual

func ConstantTimeEqual(a, b string) bool

ConstantTimeEqual performs constant-time comparison between two string values.

func DummyHashVerification

func DummyHashVerification(rawKey string) bool

DummyHashVerification simulates key verification timing against a fixed dummy hash to equalize response timing when a requested key does not exist.

func GenerateID

func GenerateID(prefix string) string

GenerateID produces a random hexadecimal identifier with an optional prefix (e.g. "ak_...").

func GenerateRawKey

func GenerateRawKey(prefix string) (rawKey string, publicPrefix string, keyHash string, err error)

GenerateRawKey produces a new cryptographically secure random API key, its public prefix, and its SHA-256 hash representation. The raw key must be returned to the caller immediately and NEVER stored in persistence.

func HashKey

func HashKey(rawKey string) string

HashKey computes the SHA-256 hex digest of a raw API key token.

func RegisterSchema

func RegisterSchema(s *schema.Schema) error

RegisterSchema registers the api_keys model into the compiled schema.

func VerifyKey

func VerifyKey(rawKey string, expectedHash string) bool

VerifyKey performs a constant-time comparison between a candidate raw key's SHA-256 hash and the stored expected hash.

func WithAPIKey

func WithAPIKey(ctx context.Context, key *APIKey) context.Context

WithAPIKey stores an authenticated APIKey in the context. Transport middleware should attach this after a successful Authenticate call.

Types

type APIKey

type APIKey struct {
	ID             string            `json:"id"`
	Name           string            `json:"name"`
	Prefix         string            `json:"prefix"`
	KeyHash        string            `json:"key_hash"`
	UserID         string            `json:"user_id,omitempty"`
	OrganizationID string            `json:"organization_id,omitempty"`
	ServiceID      string            `json:"service_id,omitempty"`
	Scopes         []string          `json:"scopes,omitempty"`
	Permissions    []string          `json:"permissions,omitempty"`
	Metadata       map[string]string `json:"metadata,omitempty"`
	ExpiresAt      *time.Time        `json:"expires_at,omitempty"`
	RevokedAt      *time.Time        `json:"revoked_at,omitempty"`
	LastUsedAt     *time.Time        `json:"last_used_at,omitempty"`
	LastUsedIP     string            `json:"last_used_ip,omitempty"`
	LastUsedUA     string            `json:"last_used_ua,omitempty"`
	CreatedAt      time.Time         `json:"created_at"`
	UpdatedAt      time.Time         `json:"updated_at"`
}

APIKey represents the persistent record of an API key. Secret tokens are NEVER stored; only KeyHash (SHA-256) and Prefix are kept.

func APIKeyFrom

func APIKeyFrom(ctx context.Context) (*APIKey, bool)

APIKeyFrom retrieves the APIKey stored in ctx, if present.

func (*APIKey) HasAllPermissions

func (k *APIKey) HasAllPermissions(permissions ...string) bool

HasAllPermissions checks if the key contains all specified permissions.

func (*APIKey) HasAllScopes

func (k *APIKey) HasAllScopes(scopes ...string) bool

HasAllScopes checks if the key contains all specified scopes.

func (*APIKey) HasAnyPermission

func (k *APIKey) HasAnyPermission(permissions ...string) bool

HasAnyPermission checks if the key contains at least one of the specified permissions.

func (*APIKey) HasAnyScope

func (k *APIKey) HasAnyScope(scopes ...string) bool

HasAnyScope checks if the key contains at least one of the specified scopes.

func (*APIKey) HasPermission

func (k *APIKey) HasPermission(permission string) bool

HasPermission checks if the key contains the specified permission.

func (*APIKey) HasScope

func (k *APIKey) HasScope(scope string) bool

HasScope checks if the key contains the specified scope.

func (*APIKey) IsActive

func (k *APIKey) IsActive(now time.Time) bool

IsActive reports whether the key is active (neither revoked nor expired).

func (*APIKey) IsExpired

func (k *APIKey) IsExpired(now time.Time) bool

IsExpired reports whether the key has expired.

func (*APIKey) IsRevoked

func (k *APIKey) IsRevoked() bool

IsRevoked reports whether the key has been revoked.

type APIKeys

type APIKeys interface {
	// Create generates and stores a new API key, returning the plaintext key once.
	Create(ctx context.Context, in CreateInput) (*CreateResult, error)

	// Get retrieves an API key by its unique ID.
	Get(ctx context.Context, id string) (*APIKey, error)

	// List queries API keys based on ownership filters.
	List(ctx context.Context, filter ListFilter) ([]*APIKey, error)

	// Revoke immediately revokes an API key and purges it from any cache.
	Revoke(ctx context.Context, id string) error

	// Rotate replaces an existing key with a newly generated key, optionally preserving
	// the old key during a grace period.
	Rotate(ctx context.Context, id string, opts ...RotateOption) (*RotateResult, error)

	// Authenticate validates a raw API key, checks expiration and scopes,
	// updates last-used telemetry, and resolves to an auth.Principal.
	Authenticate(ctx context.Context, rawKey string, opts ...AuthOption) (*AuthResult, error)

	// Delete permanently deletes an API key record and invalidates cache entries.
	Delete(ctx context.Context, id string) error

	// PurgeExpired deletes expired and revoked keys from persistence.
	PurgeExpired(ctx context.Context) (int, error)

	// Hooks provides access to the API key workflow lifecycle hooks.
	Hooks() *Hooks
}

APIKeys defines the strongly typed capability interface for API key authentication.

type AuthOption

type AuthOption func(*authConfig)

AuthOption configures authentication validation and telemetry.

func WithIPAddress

func WithIPAddress(ip string) AuthOption

WithIPAddress records the client IP address on the API key's last used telemetry.

func WithRequiredPermissions

func WithRequiredPermissions(perms ...string) AuthOption

WithRequiredPermissions enforces that the authenticated key must possess all given permissions.

func WithRequiredScopes

func WithRequiredScopes(scopes ...string) AuthOption

WithRequiredScopes enforces that the authenticated key must possess all given scopes.

func WithUserAgent

func WithUserAgent(ua string) AuthOption

WithUserAgent records the client User-Agent header on the API key's last used telemetry.

type AuthResult

type AuthResult struct {
	Principal auth.Principal
	Key       *APIKey
}

AuthResult is returned when an API key is successfully authenticated. In accordance with GoModularAuth architecture, API key authentication resolves directly to an auth.Principal and does NOT create or return an HTTP User Session.

type AuthenticatedPayload

type AuthenticatedPayload struct {
	ID             string `json:"id"`
	Prefix         string `json:"prefix"`
	UserID         string `json:"user_id,omitempty"`
	OrganizationID string `json:"organization_id,omitempty"`
	ServiceID      string `json:"service_id,omitempty"`
	IPAddress      string `json:"ip_address,omitempty"`
	UserAgent      string `json:"user_agent,omitempty"`
	PrincipalType  string `json:"principal_type"`
	Subject        string `json:"subject"`
}

AuthenticatedPayload is the payload published on EventAPIKeyAuthenticated.

type Config

type Config struct {
	// KeyPrefix is the default prefix prepended to issued API keys (defaults to "gma_").
	KeyPrefix string

	// DefaultExpiresIn sets a default expiration duration if none is specified at creation.
	DefaultExpiresIn time.Duration

	// CacheTTL defines how long active keys are retained in cache (defaults to 5m).
	CacheTTL time.Duration

	// MaxFailedAttempts specifies the maximum failed verification attempts before throttling.
	MaxFailedAttempts int

	// BruteForceWindow is the evaluation window for failed verification attempts.
	BruteForceWindow time.Duration

	// SubjectStrategy controls how auth.Principal.Subject is populated.
	SubjectStrategy SubjectStrategy

	// TouchLastUsedThreshold prevents database write amplification by updating LastUsedAt
	// only if the key was last used longer ago than this threshold (defaults to 1m).
	TouchLastUsedThreshold time.Duration
}

Config configures the API keys plugin module.

type CreateInput

type CreateInput struct {
	Name           string
	UserID         string
	OrganizationID string
	ServiceID      string
	Scopes         []string
	Permissions    []string
	Metadata       map[string]string
	ExpiresAt      *time.Time
	ExpiresIn      time.Duration
	Prefix         string
}

CreateInput contains parameters required to create an API key.

type CreateResult

type CreateResult struct {
	Key    *APIKey
	RawKey string
}

CreateResult returns the newly persisted API key record along with the single-use plaintext key secret.

type CreatedPayload

type CreatedPayload struct {
	ID             string     `json:"id"`
	Name           string     `json:"name"`
	Prefix         string     `json:"prefix"`
	UserID         string     `json:"user_id,omitempty"`
	OrganizationID string     `json:"organization_id,omitempty"`
	ServiceID      string     `json:"service_id,omitempty"`
	Scopes         []string   `json:"scopes,omitempty"`
	Permissions    []string   `json:"permissions,omitempty"`
	ExpiresAt      *time.Time `json:"expires_at,omitempty"`
}

CreatedPayload is the payload published on EventAPIKeyCreated. Secrets and full key hashes are strictly omitted.

type DeletedPayload

type DeletedPayload struct {
	ID             string `json:"id"`
	Prefix         string `json:"prefix"`
	UserID         string `json:"user_id,omitempty"`
	OrganizationID string `json:"organization_id,omitempty"`
	ServiceID      string `json:"service_id,omitempty"`
}

DeletedPayload is the payload published on EventAPIKeyDeleted.

type Hooks

type Hooks struct {
	// contains filtered or unexported fields
}

Hooks provides lifecycle interception points for the API keys module workflows.

func (*Hooks) AfterAuthenticate

func (h *Hooks) AfterAuthenticate(fn func(ctx context.Context, res *AuthResult))

AfterAuthenticate runs after credentials are successfully verified and telemetry updated.

func (*Hooks) AfterCreate

func (h *Hooks) AfterCreate(fn func(ctx context.Context, key *APIKey))

AfterCreate runs after an API key is persisted and published.

func (*Hooks) AfterRevoke

func (h *Hooks) AfterRevoke(fn func(ctx context.Context, key *APIKey))

AfterRevoke runs after an API key is revoked and cache invalidated.

func (*Hooks) AfterRotate

func (h *Hooks) AfterRotate(fn func(ctx context.Context, res *RotateResult))

AfterRotate runs after rotation finishes, returning both old and new keys.

func (*Hooks) BeforeAuthenticate

func (h *Hooks) BeforeAuthenticate(fn func(ctx context.Context, rawKey *string) error)

BeforeAuthenticate runs before validating candidate credentials.

func (*Hooks) BeforeCreate

func (h *Hooks) BeforeCreate(fn func(ctx context.Context, in *CreateInput) error)

BeforeCreate runs before an API key is validated and persisted.

func (*Hooks) BeforeRevoke

func (h *Hooks) BeforeRevoke(fn func(ctx context.Context, keyID *string) error)

BeforeRevoke runs before an API key revocation is applied in the store.

func (*Hooks) BeforeRotate

func (h *Hooks) BeforeRotate(fn func(ctx context.Context, keyID *string) error)

BeforeRotate runs before initiating rotation on an existing key.

type ListFilter

type ListFilter struct {
	UserID         string
	OrganizationID string
	ServiceID      string
	IncludeRevoked bool
	Limit          int
	Offset         int
}

ListFilter specifies filter and pagination criteria for querying API keys.

type Module

type Module struct {
	// contains filtered or unexported fields
}

Module implements auth.PluginModule and auth.SchemaProvider for API key authentication using plugin-sdk.

func New

func New(opts ...Option) *Module

New constructs an installable API keys plugin module.

func (*Module) ID

func (m *Module) ID() string

ID returns the module's unique identifier.

func (*Module) Init

func (m *Module) Init(h *auth.Host) error

Init initializes the plugin module on the host and registers its capability.

func (*Module) RegisterSchema

func (m *Module) RegisterSchema(s *schema.Schema) error

RegisterSchema contributes the api_keys model to the compiled schema.

type Option

type Option func(*Config)

Option modifies Config.

func WithBruteForceProtection

func WithBruteForceProtection(maxAttempts int, window time.Duration) Option

WithBruteForceProtection configures rate limiting on failed key verification attempts.

func WithCacheTTL

func WithCacheTTL(d time.Duration) Option

WithCacheTTL configures the TTL for cached API keys.

func WithDefaultExpiresIn

func WithDefaultExpiresIn(d time.Duration) Option

WithDefaultExpiresIn sets the default expiration lifetime for keys created without explicit expiration.

func WithKeyPrefix

func WithKeyPrefix(p string) Option

WithKeyPrefix sets the default prefix for newly generated API keys.

func WithSubjectStrategy

func WithSubjectStrategy(s SubjectStrategy) Option

WithSubjectStrategy configures how the principal subject is resolved.

func WithTouchLastUsedThreshold

func WithTouchLastUsedThreshold(d time.Duration) Option

WithTouchLastUsedThreshold configures the minimum interval between LastUsedAt database writes.

type RevokedPayload

type RevokedPayload struct {
	ID             string    `json:"id"`
	Prefix         string    `json:"prefix"`
	UserID         string    `json:"user_id,omitempty"`
	OrganizationID string    `json:"organization_id,omitempty"`
	ServiceID      string    `json:"service_id,omitempty"`
	RevokedAt      time.Time `json:"revoked_at"`
}

RevokedPayload is the payload published on EventAPIKeyRevoked.

type RotateOption

type RotateOption func(*rotateConfig)

RotateOption configures an API key rotation.

func WithGracePeriod

func WithGracePeriod(d time.Duration) RotateOption

WithGracePeriod specifies how long the old API key remains valid before expiring. If zero or not provided, the old key is immediately revoked.

func WithNewExpiresIn

func WithNewExpiresIn(d time.Duration) RotateOption

WithNewExpiresIn sets a new relative expiration duration on the rotated key.

func WithNewMetadata

func WithNewMetadata(meta map[string]string) RotateOption

WithNewMetadata overrides the custom metadata on the rotated key.

func WithNewName

func WithNewName(name string) RotateOption

WithNewName overrides the name of the newly generated key during rotation.

func WithNewPermissions

func WithNewPermissions(perms []string) RotateOption

WithNewPermissions overrides the permissions granted to the rotated key.

func WithNewScopes

func WithNewScopes(scopes []string) RotateOption

WithNewScopes overrides the scopes granted to the rotated key.

type RotateResult

type RotateResult struct {
	NewKey *APIKey
	RawKey string
	OldKey *APIKey
}

RotateResult contains the newly issued key, its plaintext secret, and the updated old key.

type RotatedPayload

type RotatedPayload struct {
	OldKeyID       string     `json:"old_key_id"`
	NewKeyID       string     `json:"new_key_id"`
	Prefix         string     `json:"prefix"`
	UserID         string     `json:"user_id,omitempty"`
	OrganizationID string     `json:"organization_id,omitempty"`
	ServiceID      string     `json:"service_id,omitempty"`
	GracePeriod    *time.Time `json:"grace_period_expires_at,omitempty"`
}

RotatedPayload is the payload published on EventAPIKeyRotated.

type SubjectStrategy

type SubjectStrategy string

SubjectStrategy defines how an API key resolves its auth.Principal.Subject.

const (
	// SubjectStrategyKeyID sets Principal.Subject to the unique API key ID (default).
	SubjectStrategyKeyID SubjectStrategy = "key_id"

	// SubjectStrategyOwner prioritizes UserID, then ServiceID, then KeyID as Principal.Subject.
	SubjectStrategyOwner SubjectStrategy = "owner"
)

Directories

Path Synopsis
Package cache provides caching and brute force prevention abstractions for the API keys module.
Package cache provides caching and brute force prevention abstractions for the API keys module.
Package plugins exposes capability aliases for GoModularAuth plugins.
Package plugins exposes capability aliases for GoModularAuth plugins.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL