Documentation
¶
Overview ¶
Package auth provides database-independent API key workflows for users and groups.
The service generates raw API keys, stores only HMAC-SHA-256 lookup hashes, verifies credentials, checks required scopes, revokes keys, lists keys with cursor pagination, and records structured audit events. Applications provide storage through small interfaces, or use an included native store adapter.
Key generation and service setup ¶
Generate the API key lookup key once, store it in secret management, and load it at process startup. Do not generate a new lookup key on every boot unless you intentionally want existing API keys to stop verifying.
lookupKey, err := keys.GenerateHMACKey()
if err != nil {
return err
}
service, err := auth.New(auth.Config{
Issuer: "example-api",
KeyPrefix: "ak",
APIKeyLookupKey: lookupKey,
Principals: principalStore,
APIKeys: apiKeyStore,
Audit: auditStore,
})
if err != nil {
return err
}
Create and verify API keys ¶
API keys can be owned by users or groups. RawKey is returned once and must not be logged or stored.
created, err := service.CreateAPIKey(ctx, auth.CreateAPIKeyRequest{
OwnerType: auth.PrincipalTypeUser,
OwnerID: "user_123",
Name: "production client",
Scopes: []string{"widgets:read", "widgets:write"},
})
if err != nil {
return err
}
rawKey := created.RawKey
verified, err := service.VerifyAPIKey(ctx, auth.VerifyAPIKeyRequest{
RawKey: rawKey,
RequiredScopes: []string{"widgets:read"},
})
if err != nil {
return err
}
_ = verified.Principal
Revoke and list API keys
err := service.RevokeAPIKey(ctx, auth.RevokeAPIKeyRequest{
APIKeyID: verified.APIKey.ID,
})
if err != nil {
return err
}
page, err := service.ListAPIKeys(ctx, auth.ListAPIKeysRequest{
OwnerType: auth.PrincipalTypeUser,
OwnerID: "user_123",
Page: auth.PageRequest{
Limit: 50,
},
})
if err != nil {
return err
}
if page.HasMore() {
nextPage, err := service.ListAPIKeys(ctx, auth.ListAPIKeysRequest{
OwnerType: auth.PrincipalTypeUser,
OwnerID: "user_123",
Page: auth.PageRequest{
Limit: 50,
Cursor: page.NextCursor,
},
})
_ = nextPage
_ = err
}
SQLite setup ¶
SQLite is the complete built-in store adapter. It implements PrincipalStore, APIKeyStore, AuditStore, and AtomicAPIKeyAuditStore.
db, err := sqlite.Open(ctx, "auth.db")
if err != nil {
return err
}
defer db.Close()
if err := sqlite.Migrate(ctx, db); err != nil {
return err
}
store := sqlite.NewStore(db)
service, err := auth.New(auth.Config{
Issuer: "example-api",
APIKeyLookupKey: lookupKey,
Principals: store,
APIKeys: store,
Audit: store,
})
MySQL and MariaDB setup ¶
MySQL/MariaDB is a complete built-in store adapter. It implements PrincipalStore, APIKeyStore, AuditStore, and AtomicAPIKeyAuditStore.
db, err := mysql.Open(ctx, dsn)
if err != nil {
return err
}
defer db.Close()
if err := mysql.Migrate(ctx, db); err != nil {
return err
}
if err := mysql.ValidateSchema(ctx, db); err != nil {
return err
}
store := mysql.NewStore(db)
service, err := auth.New(auth.Config{
Issuer: "example-api",
APIKeyLookupKey: lookupKey,
Principals: store,
APIKeys: store,
Audit: store,
})
PostgreSQL setup ¶
PostgreSQL is a complete built-in store adapter. It implements PrincipalStore, APIKeyStore, AuditStore, and AtomicAPIKeyAuditStore.
db, err := postgres.Open(ctx, dsn)
if err != nil {
return err
}
defer db.Close()
if err := postgres.Migrate(ctx, db); err != nil {
return err
}
if err := postgres.ValidateSchema(ctx, db); err != nil {
return err
}
store := postgres.NewStore(db)
service, err := auth.New(auth.Config{
Issuer: "example-api",
APIKeyLookupKey: lookupKey,
Principals: store,
APIKeys: store,
Audit: store,
})
Redis setup ¶
Redis support currently covers namespaced migration markers and explicit namespace deletion helpers.
client, err := redis.Open(ctx, &goredis.Options{
Addr: "127.0.0.1:6379",
})
if err != nil {
return err
}
defer client.Close()
if err := redis.MigrateNamespace(ctx, client, "prod"); err != nil {
return err
}
// For reset workflows, quiesce writers before draining.
err = redis.DrainNamespaceData(ctx, client, "prod", redis.DrainOptions{
EmptyPasses: 2,
MaxPasses: 10,
})
MongoDB setup ¶
MongoDB is a complete built-in store adapter. The default Store implements PrincipalStore, APIKeyStore, and AuditStore. Use TransactionalStore for atomic key/audit operations on deployments that support MongoDB transactions.
conn, err := mongodb.Open(ctx, uri, "auth")
if err != nil {
return err
}
defer conn.Close(ctx)
if err := conn.Migrate(ctx); err != nil {
return err
}
store := conn.Store()
service, err := auth.New(auth.Config{
Issuer: "example-api",
APIKeyLookupKey: lookupKey,
Principals: store,
APIKeys: store,
Audit: store,
})
For transaction-backed audit:
store := conn.TransactionalStore()
If your application already owns the MongoDB client, pass its database handle directly:
db := client.Database("auth")
if err := mongodb.Migrate(ctx, db); err != nil {
return err
}
store := mongodb.NewStore(db)
transactionalStore := mongodb.NewTransactionalStore(db)
_ = transactionalStore
Custom stores ¶
To use another database, implement PrincipalStore, APIKeyStore, and optionally AuditStore. Stores must never persist raw API keys. Store APIKey.Hash exactly as provided, index by APIKey.Prefix, return ErrNotFound for missing records, and return ErrAlreadyExists for uniqueness conflicts. If key metadata and audit events live in the same database, implement AtomicAPIKeyAuditStore so create and revoke operations can commit their audit event atomically.
Index ¶
- Constants
- Variables
- type APIKey
- type APIKeyStore
- type AtomicAPIKeyAuditStore
- type AuditEvent
- type AuditEventType
- type AuditStore
- type Clock
- type Config
- type CreateAPIKeyRequest
- type CreateAPIKeyResult
- type ListAPIKeysRequest
- type Page
- type PageRequest
- type Principal
- type PrincipalStore
- type PrincipalType
- type RevokeAPIKeyRequest
- type Service
- func (s *Service) Config() Config
- func (s *Service) CreateAPIKey(ctx context.Context, req CreateAPIKeyRequest) (CreateAPIKeyResult, error)
- func (s *Service) ListAPIKeys(ctx context.Context, req ListAPIKeysRequest) (Page[APIKey], error)
- func (s *Service) RevokeAPIKey(ctx context.Context, req RevokeAPIKeyRequest) error
- func (s *Service) VerifyAPIKey(ctx context.Context, req VerifyAPIKeyRequest) (VerifyAPIKeyResult, error)
- type VerifyAPIKeyRequest
- type VerifyAPIKeyResult
Constants ¶
const ( // DefaultPageLimit is used when callers do not provide a limit. DefaultPageLimit = 50 // MaxPageLimit is the largest page size accepted by core workflows. MaxPageLimit = 200 )
Variables ¶
var ( // ErrNotFound reports that a requested record does not exist. ErrNotFound = errors.New("auth: not found") // ErrAlreadyExists reports that creating a record would violate uniqueness. ErrAlreadyExists = errors.New("auth: already exists") // ErrConflict reports that a write could not be applied because the stored // state changed or conflicts with the requested state. ErrConflict = errors.New("auth: conflict") // ErrInvalidState reports that a requested transition is not allowed for the // current resource state, such as revoking an already-revoked token. ErrInvalidState = errors.New("auth: invalid state") // ErrInvalidRequest reports malformed or incomplete caller input. ErrInvalidRequest = errors.New("auth: invalid request") // ErrInvalidCredentials reports failed authentication without revealing // whether an API key prefix, hash, owner, or state was the failing factor. ErrInvalidCredentials = errors.New("auth: invalid credentials") // ErrDisabledPrincipal reports that an otherwise valid principal is disabled. ErrDisabledPrincipal = errors.New("auth: disabled principal") // ErrPermissionDenied reports that a valid API key lacks required scope. ErrPermissionDenied = errors.New("auth: permission denied") // ErrMissingStore reports that a workflow was called without the required // storage dependency configured. ErrMissingStore = errors.New("auth: missing store") )
var ( // ErrInvalidConfig is returned when a service configuration is unsafe or // incomplete. ErrInvalidConfig = errors.New("auth: invalid config") )
Functions ¶
This section is empty.
Types ¶
type APIKey ¶
type APIKey struct {
ID string
Issuer string
Prefix string
Name string
OwnerType PrincipalType
OwnerID string
Hash []byte
Scopes []string
CreatedAt time.Time
ExpiresAt *time.Time
RevokedAt *time.Time
LastUsedAt *time.Time
}
APIKey represents stored API key metadata.
Raw key material must never be stored in this type. Store adapters persist Hash for lookup verification and Prefix for efficient key lookup.
type APIKeyStore ¶
type APIKeyStore interface {
// CreateAPIKey stores key metadata. It returns ErrAlreadyExists when the key
// ID or prefix is already present.
CreateAPIKey(ctx context.Context, key APIKey) error
// GetAPIKeyByID returns ErrNotFound when keyID does not exist.
GetAPIKeyByID(ctx context.Context, keyID string) (APIKey, error)
// GetAPIKeyByPrefix returns ErrNotFound when prefix does not exist.
GetAPIKeyByPrefix(ctx context.Context, prefix string) (APIKey, error)
// ListAPIKeys returns keys for a principal in a stable deterministic order.
// It returns an empty page when the principal has no keys. Cursor values are
// opaque and store-defined.
ListAPIKeys(ctx context.Context, ownerType PrincipalType, ownerID string, page PageRequest) (Page[APIKey], error)
// RevokeAPIKey returns ErrNotFound when keyID does not exist and
// ErrInvalidState when the key cannot be revoked.
RevokeAPIKey(ctx context.Context, keyID string, revokedAt time.Time) error
// TouchAPIKey records successful use. The core service treats this as
// best-effort metadata and does not fail verification when it returns an
// error.
TouchAPIKey(ctx context.Context, keyID string, usedAt time.Time) error
}
APIKeyStore persists API key metadata and hashed key lookups.
Raw API key values must never be persisted. Store adapters should index by Prefix and store only Hash for verification.
type AtomicAPIKeyAuditStore ¶
type AtomicAPIKeyAuditStore interface {
// CreateAPIKeyWithAudit stores key metadata and its creation audit event in
// one atomic operation.
CreateAPIKeyWithAudit(ctx context.Context, key APIKey, event AuditEvent) error
// RevokeAPIKeyWithAudit reads and revokes an API key, stores its revocation
// audit event, and returns the revoked key metadata in one atomic operation.
//
// Implementations should populate event API key and principal fields from
// the key read inside the atomic operation.
RevokeAPIKeyWithAudit(ctx context.Context, keyID string, revokedAt time.Time, event AuditEvent) (APIKey, error)
}
AtomicAPIKeyAuditStore optionally persists API key mutations and their audit event in one store-owned atomic operation.
Store adapters that can provide transactions should implement this interface when their APIKeyStore and AuditStore data live in the same database.
type AuditEvent ¶
type AuditEvent struct {
ID string
Type AuditEventType
ActorID string
PrincipalType PrincipalType
PrincipalID string
APIKeyID string
Occurred time.Time
Metadata map[string]string
}
AuditEvent is a structured security event.
Metadata must not contain secrets, raw API keys, key hashes, private keys, or sensitive personal data.
type AuditEventType ¶
type AuditEventType string
AuditEventType identifies a security-relevant action or state transition.
const ( AuditEventAPIKeyCreated AuditEventType = "api_key.created" AuditEventAPIKeyVerified AuditEventType = "api_key.verified" AuditEventAPIKeyVerificationFailed AuditEventType = "api_key.verification_failed" AuditEventAPIKeyRevoked AuditEventType = "api_key.revoked" )
type AuditStore ¶
type AuditStore interface {
// RecordAuditEvent stores an audit event. It returns ErrAlreadyExists when
// the event ID is already present.
RecordAuditEvent(ctx context.Context, event AuditEvent) error
}
AuditStore records security-relevant events.
type Clock ¶
Clock supplies time for auth workflows.
It is injectable so tests can avoid sleeps and production code can use the system clock by default.
type Config ¶
type Config struct {
// Issuer identifies this auth service in generated credentials and audit
// records. It must be stable within a deployment.
Issuer string
// Clock supplies time for token and session lifecycles. The system clock is
// used when this is nil.
Clock Clock
// KeyPrefix is the public prefix used in generated API keys. It may contain
// ASCII letters, digits, and hyphens.
KeyPrefix string
// APIKeyTTL is the default lifetime for generated API keys when callers do
// not provide an explicit expiration.
APIKeyTTL time.Duration
// APIKeyLookupKey is the application-controlled HMAC key used to hash API
// keys before storage. It is required when APIKeys is configured and must
// come from secret management, not source code.
APIKeyLookupKey []byte
// Principals stores users and groups that can own API keys.
Principals PrincipalStore
// APIKeys stores API key metadata and lookup hashes.
APIKeys APIKeyStore
// Audit records security-relevant events. Audit writes and last-used updates
// are best-effort for completed workflows so metadata storage failures do
// not orphan newly created API keys or deny otherwise valid keys.
Audit AuditStore
}
Config controls the core authentication service.
type CreateAPIKeyRequest ¶
type CreateAPIKeyRequest struct {
OwnerType PrincipalType
OwnerID string
Name string
Scopes []string
ExpiresAt *time.Time
}
CreateAPIKeyRequest contains metadata for a new API key.
type CreateAPIKeyResult ¶
CreateAPIKeyResult returns the stored key metadata and the raw API key.
RawKey is shown once. It must not be logged or stored.
type ListAPIKeysRequest ¶
type ListAPIKeysRequest struct {
OwnerType PrincipalType
OwnerID string
Page PageRequest
}
ListAPIKeysRequest identifies the principal whose keys should be listed.
type Page ¶
Page contains one page of items and the cursor for the next page.
NextCursor is empty when there are no more results.
type PageRequest ¶
PageRequest requests a bounded page of results after Cursor.
Cursor is an opaque value returned by a previous page. Stores define the cursor encoding, but callers must pass it through unchanged.
type Principal ¶
type Principal struct {
ID string
Type PrincipalType
Name string
CreatedAt time.Time
UpdatedAt time.Time
DisabledAt *time.Time
}
Principal is a user or group that can own API keys.
func (Principal) IsDisabled ¶
IsDisabled reports whether the principal is disabled.
type PrincipalStore ¶
type PrincipalStore interface {
// GetPrincipal returns ErrNotFound when the principal does not exist.
GetPrincipal(ctx context.Context, principalType PrincipalType, principalID string) (Principal, error)
}
PrincipalStore persists users and groups that can own API keys.
type PrincipalType ¶
type PrincipalType string
PrincipalType identifies the kind of entity that can own an API key.
const ( PrincipalTypeUser PrincipalType = "user" PrincipalTypeGroup PrincipalType = "group" )
type RevokeAPIKeyRequest ¶
type RevokeAPIKeyRequest struct {
APIKeyID string
}
RevokeAPIKeyRequest identifies an API key to revoke.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service coordinates authentication workflows.
The service is deliberately database-independent. Future steps will add storage interfaces to Config and implement workflows against those interfaces.
func (*Service) CreateAPIKey ¶
func (s *Service) CreateAPIKey(ctx context.Context, req CreateAPIKeyRequest) (CreateAPIKeyResult, error)
CreateAPIKey creates a new API key for a user or group.
func (*Service) ListAPIKeys ¶
ListAPIKeys lists API keys for a user or group.
func (*Service) RevokeAPIKey ¶
func (s *Service) RevokeAPIKey(ctx context.Context, req RevokeAPIKeyRequest) error
RevokeAPIKey revokes an API key by ID.
func (*Service) VerifyAPIKey ¶
func (s *Service) VerifyAPIKey(ctx context.Context, req VerifyAPIKeyRequest) (VerifyAPIKeyResult, error)
VerifyAPIKey verifies a raw API key and checks required scopes.
type VerifyAPIKeyRequest ¶
VerifyAPIKeyRequest contains a raw API key and optional required scopes.
type VerifyAPIKeyResult ¶
VerifyAPIKeyResult contains the verified key and owning principal.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
testbench
command
|
|
|
Package keys provides secure signing and symmetric key generation.
|
Package keys provides secure signing and symmetric key generation. |
|
Package migrate provides explicit migration planning and execution.
|
Package migrate provides explicit migration planning and execution. |
|
Package mongodb provides a native MongoDB adapter for auth stores.
|
Package mongodb provides a native MongoDB adapter for auth stores. |
|
Package mysql provides a native MySQL/MariaDB adapter for auth stores.
|
Package mysql provides a native MySQL/MariaDB adapter for auth stores. |
|
Package postgres provides a native PostgreSQL adapter for auth stores.
|
Package postgres provides a native PostgreSQL adapter for auth stores. |
|
Package redis provides Redis migration helpers for auth stores.
|
Package redis provides Redis migration helpers for auth stores. |
|
Package sqlite provides a SQLite adapter for auth stores.
|
Package sqlite provides a SQLite adapter for auth stores. |
|
Package token provides secure opaque token generation and lookup hashing.
|
Package token provides secure opaque token generation and lookup hashing. |