Help us shape the future of Go! Take the 2026 Go Developer Survey

apikey

package
v0.1.0-alpha.9 Latest Latest
Warning

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

Go to latest
Published: Sep 23, 2026 License: MIT Imports: 24 Imported by: 0

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

View Source
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"
)
View Source
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.

View Source
const (
	MaxScopes    = 32
	MaxAudiences = 8
)

Limits on configuration and issuance.

View Source
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

type IssuedKey struct {
	Key    Key          `json:"key"`
	Secret sigil.Secret `json:"-"`
}

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 New

func New(c Config) (*Plugin, error)

New validates and copies configuration.

func (*Plugin) AdminList

func (p *Plugin) AdminList(ctx context.Context, userID string) ([]Key, error)

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

func (p *Plugin) AdminRevoke(ctx context.Context, userID, id string) error

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

func (p *Plugin) AdminRevokeAll(ctx context.Context, userID string) (int64, error)

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) Bind

func (p *Plugin) Bind(a *sigil.Auth) error

func (*Plugin) Definition

func (p *Plugin) Definition() plugin.Definition

func (*Plugin) Issue

func (p *Plugin) Issue(ctx context.Context, req Request) (IssuedKey, error)

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

func (p *Plugin) List(ctx context.Context) ([]Key, error)

List returns the caller's keys, including revoked and expired ones until they are pruned, oldest first.

func (*Plugin) Require

func (p *Plugin) Require(req Requirement) (func(http.Handler) http.Handler, error)

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

func (p *Plugin) Revoke(ctx context.Context, id string) error

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.

func FromContext

func FromContext(ctx context.Context) (Principal, bool)

FromContext returns the Principal Authorize bound to ctx.

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

type Requirement struct {
	Audience       string
	Scopes         []string
	OrganizationID string
}

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).

Jump to

Keyboard shortcuts

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