Documentation
¶
Overview ¶
Package apikey provides optional PostgreSQL/SQLite scoped API keys: an authentication mode distinct from browser sessions, for non-interactive callers of the host's own API.
Index ¶
- Constants
- func Contract() plugin.Definition
- func PruneExpired(ctx context.Context, store storage.Store, before time.Time, limit int) (int64, error)
- type Config
- type IssuedKey
- type Key
- type Plugin
- func (p *Plugin) AdminList(ctx context.Context, userID string) ([]Key, error)
- func (p *Plugin) AdminRevoke(ctx context.Context, userID, id string) error
- func (p *Plugin) AdminRevokeAll(ctx context.Context, userID string) (int64, error)
- func (p *Plugin) Authorize(ctx context.Context, token sigil.Secret, req Requirement) (context.Context, Principal, error)
- func (p *Plugin) Bind(a *sigil.Auth) error
- func (p *Plugin) Definition() plugin.Definition
- func (p *Plugin) Issue(ctx context.Context, req Request) (IssuedKey, error)
- func (p *Plugin) List(ctx context.Context) ([]Key, error)
- func (p *Plugin) Require(req Requirement) (func(http.Handler) http.Handler, error)
- func (p *Plugin) Revoke(ctx context.Context, id string) error
- func (p *Plugin) WithKey(ctx context.Context, req Requirement, op plugin.Operation, ...) error
- type Principal
- type Request
- type Requirement
Constants ¶
const ( IssueOperation plugin.Operation = "api_key.issue" RevokeOperation plugin.Operation = "api_key.revoke" // RevokeAllOperation records an administrator revoking every key an // account holds. RevokeAllOperation plugin.Operation = "api_key.revoke_all" // UseOperation records every successful Authorize and every rejected key. UseOperation plugin.Operation = "api_key.use" // Method is the audit method of rejections that did not prove a key. // Events about a known key carry Method + "." + key ID. Method sigil.AuditMethod = "api_key" )
const ( ReasonRevoked sigil.AuditReason = "revoked" ReasonExpired sigil.AuditReason = "expired" ReasonInsufficientScope sigil.AuditReason = "insufficient_scope" ReasonAudience sigil.AuditReason = "audience_mismatch" ReasonTenant sigil.AuditReason = "tenant_mismatch" ReasonMembership sigil.AuditReason = "membership" )
Rejection reasons recorded on api_key.use failure events.
const ( MaxScopes = 32 MaxAudiences = 8 )
Limits on configuration and issuance.
const ID = "apikey"
Variables ¶
This section is empty.
Functions ¶
func Contract ¶
func Contract() plugin.Definition
Contract is the offline declaration for tooling: routes, schema names and migrations are independent of configuration.
func PruneExpired ¶
func PruneExpired(ctx context.Context, store storage.Store, before time.Time, limit int) (int64, error)
PruneExpired removes at most limit (1..1000) keys that expired or were revoked before the cutoff, clamped to database time. Revoked and expired keys stay listed until pruned; the audit trail keeps their history.
Types ¶
type Config ¶
type Config struct {
// Scopes and Audiences are the only values a key can hold or a
// Requirement can name. Removing one from configuration withdraws it from
// every existing key, because Requirement validation rejects it.
Scopes, Audiences []string
// Organizations enables organization-bound keys. It adds a dependency on
// the organization plugin; issuance and every use then require the owner's
// live organization.PermissionManageAPIKeys.
Organizations bool
// DefaultLifetime (30 days) applies when a request names none; MaxLifetime
// (365 days) caps every key. Lifetimes are at least one minute.
DefaultLifetime, MaxLifetime time.Duration
// RateLimit (600) is the default and maximum number of successful
// authorizations per RateWindow (one minute) for each key.
RateLimit int
RateWindow time.Duration
// MaxKeys (50, at most 1000) caps an account's unrevoked, unexpired keys.
MaxKeys int
// IssueAssurance raises the session assurance issuance needs above
// sigil.Config.SensitiveAssurance; it never lowers it.
IssueAssurance sigil.AssuranceLevel
}
Config declares the closed catalogs keys may be granted from and the bounds of issuance. Zero values select the documented defaults.
type IssuedKey ¶
IssuedKey.Secret is the only copy of the credential. Return it to the caller once, after commit, and never log or persist it.
type Key ¶
type Key struct {
ID string `json:"id"`
// UserID owns the key: it acts with at most this account's authority.
UserID string `json:"userId"`
// OrganizationID is the tenant binding, empty for a personal key.
OrganizationID string `json:"organizationId"`
Name string `json:"name"`
Scopes []string `json:"scopes"`
Audiences []string `json:"audiences"`
RateLimit int `json:"rateLimit"`
RateWindowSeconds int `json:"rateWindowSeconds"`
CreatedAt time.Time `json:"createdAt"`
ExpiresAt time.Time `json:"expiresAt"`
RevokedAt *time.Time `json:"revokedAt"`
LastUsedAt *time.Time `json:"lastUsedAt"`
}
Key is public metadata. It never contains the secret or its digest.
type Plugin ¶
type Plugin struct {
// contains filtered or unexported fields
}
Plugin binds to exactly one Auth. Methods are safe for concurrent use.
func (*Plugin) AdminList ¶
AdminList lists every key userID holds, including revoked and expired keys until pruned, for an administrator. It records no event. Attribution and the optional operator check follow sigil.Auth.WithAdministration.
func (*Plugin) AdminRevoke ¶
AdminRevoke revokes one of userID's keys as an administrator: the host, or the administrator of a sigil.AdminService.Operator context. It records api_key.revoke attributed to that actor, with the key as method and the account as subject. It works while the account is suspended, which is how a suspension for compromise is made permanent for the keys. A key the account does not hold is forbidden; revoking a revoked key succeeds.
func (*Plugin) AdminRevokeAll ¶
AdminRevokeAll revokes every key userID holds that is not already revoked, in one transaction, and returns how many it revoked. It records api_key.revoke_all attributed like AdminRevoke, even when none remained.
func (*Plugin) Authorize ¶
func (p *Plugin) Authorize(ctx context.Context, token sigil.Secret, req Requirement) (context.Context, Principal, error)
Authorize verifies a presented key against req, charges its rate limit and records api_key.use, all in one transaction under the owner's lock, so a committed revocation or expiry is observed by every instance. It returns a context carrying the key for WithKey and FromContext. It never establishes a session: session services reject the returned context. Unknown, revoked and expired keys are unauthenticated; tenant, audience, scope and membership mismatches are forbidden; an exhausted window is rate limited with a retry hint and consumes nothing.
func (*Plugin) Definition ¶
func (p *Plugin) Definition() plugin.Definition
func (*Plugin) Issue ¶
Issue creates a key owned by the recently authenticated caller. An organization-bound key additionally requires the caller's live organization.PermissionManageAPIKeys. The secret is returned only here.
func (*Plugin) List ¶
List returns the caller's keys, including revoked and expired ones until they are pruned, oldest first.
func (*Plugin) Require ¶
Require returns middleware for the host's own API routes. It accepts only an Authorization: Bearer header (never cookies, query strings or bodies), runs Authorize against req and passes a context carrying the key. Use WithKey inside the handler for mutations. The host owns request-rate, body, origin and CSRF policy for these routes, as with RequireSession; bearer-only authentication is not ambient, so it needs no CSRF proof.
func (*Plugin) Revoke ¶
Revoke ends a key the caller owns, effective for every instance as soon as it commits. Revoking an already revoked key succeeds; another account's or an unknown key is forbidden.
func (*Plugin) WithKey ¶
func (p *Plugin) WithKey(ctx context.Context, req Requirement, op plugin.Operation, fn func(storage.Tx, Principal) error) error
WithKey reverifies the key Authorize bound to ctx against req and runs trusted work in the same transaction, holding the owner's lock and the key row through commit. A non-empty op records a success event attributed to the key's owner and naming the key. It does not charge the rate limit. Always scope resource queries to the returned Principal's owner and tenant.
type Principal ¶
type Principal struct {
KeyID, UserID, OrganizationID string
Scopes, Audiences []string
ExpiresAt time.Time
}
Principal is a credential-free snapshot of an authorized key. It is display and routing data, not a grant: mutations use WithKey.
type Request ¶
type Request struct {
Name string
Scopes, Audiences []string
OrganizationID string
Lifetime time.Duration
RateLimit int
}
Request describes a key to issue. Zero Lifetime and RateLimit select the configured defaults. Scopes and audiences must come from the catalogs.
type Requirement ¶
Requirement is what one call of the host's API needs. Audience names the resource server being called; every scope must be held; OrganizationID must equal the key's tenant binding exactly (empty only for personal keys).