apikeyissue

package
v1.132.0 Latest Latest
Warning

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

Go to latest
Published: Sep 16, 2026 License: Apache-2.0 Imports: 8 Imported by: 0

Documentation

Overview

Package apikeyissue is the one place a database-managed API key comes into being (#1759).

Two surfaces issue keys: the admin route, where an operator names a key and may bind it to somebody's account, and the portal route, where a person issues one for themselves. What they share is everything that decides whether the key will work -- that a bound account resolves to a person the platform can speak for, that the name is free, that the key exists in the store before it authenticates anywhere, and that peer replicas are told. Forking that would mean one surface could hand out a key the other's rules would have refused.

What the surfaces keep for themselves is what they present: their own request shapes, their own refusal text and status codes, and their own naming (the portal composes a name scoped to its owner). This package raises typed refusals and never writes a response.

Index

Constants

View Source
const SelfIssuedPrefix = "user:"

SelfIssuedPrefix opens the name every key a person issued for themselves is stored under. Key names are unique across a deployment, so a person's chosen name is scoped to them; this is the one spelling of that scope, so the surface that composes such a name and the surface that must refuse to impersonate one cannot disagree about what it looks like.

Variables

View Source
var (
	// ErrNameTaken means a key already carries the name asked for.
	ErrNameTaken = errors.New("a key with that name already exists")
	// ErrUnknownPerson means the account a key was to be bound to is not one
	// the platform can speak for: no directory row, or nobody it has seen sign
	// in, so there is no subject to authenticate as and no roles to carry.
	ErrUnknownPerson = errors.New("the platform has no record of that person signing in")
	// ErrNoStore means the deployment has nowhere to put a key, so none can be
	// issued. A database-less deployment reaches this rather than minting a
	// value that would authenticate nowhere.
	ErrNoStore = errors.New("this deployment stores no api keys")
	// ErrReservedName means the name asked for is one the platform composes
	// for a key somebody issued for themselves.
	ErrReservedName = errors.New("that name belongs to the platform")
)

Refusals an issuer raises. Each surface maps these to its own status code and wording, so the rule lives here and the phrasing lives with the reader.

Functions

This section is empty.

Types

type Issued

type Issued struct {
	Key   string
	Email string
	Roles []string
	// Person is the account the key resolved to, or nil for a service key. Its
	// roles are what a bound key with no roles of its own will carry.
	Person *auth.BoundPrincipal
}

Issued is the key handed back. Key is the only time the value exists outside the holder's keeping: the store has its hash and nothing else.

type Issuer

type Issuer struct {
	// Manager mints the value and holds the copy in memory.
	Manager Manager
	// Store is where a key comes into being: it authenticates once it is here.
	Store Store
	// Principals resolves the account a bound key names. Nil means this
	// deployment cannot bind a key to anybody, and every bound request is
	// refused with ErrUnknownPerson rather than issued unresolvable.
	Principals auth.PrincipalSource
	// Announce tells peer replicas a key was written, so their in-memory copy
	// is refreshed. Optional: a key is live the moment it is stored, so this
	// only shortens how long a peer's listing is stale.
	Announce func()
}

Issuer issues keys against one deployment's key store.

func (*Issuer) Issue

func (i *Issuer) Issue(ctx context.Context, req Request) (*Issued, error)

Issue mints, stores and announces a key, or refuses it.

The binding is resolved before the value is minted, through the same resolver the authenticator will use, so a key is never handed to somebody for an account that will not resolve when they present it.

type Manager

type Manager interface {
	GenerateKey(def auth.APIKey) (string, error)
	SyncHashedKeys(ctx context.Context) error
}

Manager mints key values and refreshes the keys held in memory. Implemented by *auth.APIKeyAuthenticator.

type Request

type Request struct {
	// Name is the key's identity in the store, unique across the deployment.
	Name string
	// Description is what the key is for, as its holder wrote it.
	Description string
	// Email is the address a service key carries, already defaulted by the
	// caller. It is ignored for a bound key, which always carries its
	// person's address.
	Email string
	// UserEmail binds the key to a person's account. Empty issues the
	// standalone service key a key has always been.
	UserEmail string
	// Roles is the key's own role set. A service key must carry one. A bound
	// key with none follows the person it is bound to, on every request; a
	// bound key with one carries that set and nothing else, which is not
	// checked against what that person holds.
	Roles []string
	// ExpiresAt ends the key, or nil for a key that never expires.
	ExpiresAt *time.Time
	// CreatedBy records who issued it.
	CreatedBy string
}

Request is a key to issue.

type Store

type Store interface {
	Create(ctx context.Context, def apikeystore.Definition) error
}

Store persists the key. Implemented by the platform's API key store.

Jump to

Keyboard shortcuts

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