Documentation
¶
Overview ¶
Package credential provides credential management for principals. Credentials can be passwords, API keys, keypairs, WebAuthn credentials, TOTP secrets, client secrets, or login tokens.
Index ¶
- Constants
- Variables
- func LoginTokenIssuedBy(c *Credential) (uuid.UUID, error)
- func LoginTokenSingleUse(c *Credential) bool
- type CreateAPIKeyInput
- type CreateClientSecretInput
- type CreateKeypairInput
- type CreatePasswordInput
- type Credential
- type DefaultService
- func (s *DefaultService) CreateAPIKey(ctx context.Context, input CreateAPIKeyInput) (*GeneratedAPIKey, error)
- func (s *DefaultService) CreateClientSecret(ctx context.Context, input CreateClientSecretInput) (*GeneratedClientSecret, error)
- func (s *DefaultService) CreateKeypair(ctx context.Context, input CreateKeypairInput) (*KeypairCredential, error)
- func (s *DefaultService) CreatePassword(ctx context.Context, input CreatePasswordInput) error
- func (s *DefaultService) GetByID(ctx context.Context, id uuid.UUID) (*Credential, error)
- func (s *DefaultService) GetKeypairByKeyID(ctx context.Context, keyID string) (*KeypairCredential, error)
- func (s *DefaultService) IssueLoginToken(ctx context.Context, input IssueLoginTokenInput) (*GeneratedLoginToken, error)
- func (s *DefaultService) ListAPIKeys(ctx context.Context, principalID uuid.UUID) ([]*Credential, error)
- func (s *DefaultService) ListKeypairs(ctx context.Context, principalID uuid.UUID) ([]*KeypairCredential, error)
- func (s *DefaultService) Revoke(ctx context.Context, id uuid.UUID, reason string) error
- func (s *DefaultService) UpdateLastUsed(ctx context.Context, id uuid.UUID, ip string) error
- func (s *DefaultService) UpdatePassword(ctx context.Context, principalID uuid.UUID, newPassword string) error
- func (s *DefaultService) ValidateAPIKey(ctx context.Context, plainKey string) (*Credential, error)
- func (s *DefaultService) ValidateClientSecret(ctx context.Context, clientID, plainSecret string) (*Credential, error)
- func (s *DefaultService) VerifyLoginToken(ctx context.Context, plainToken, ip string) (*Credential, error)
- func (s *DefaultService) VerifyPassword(ctx context.Context, principalID uuid.UUID, password string) (bool, error)
- type GeneratedAPIKey
- type GeneratedClientSecret
- type GeneratedLoginToken
- type IssueLoginTokenInput
- type KeypairCredential
- type LoginTokenPolicy
- type Service
- type Type
Constants ¶
const ( // LoginTokenPrefix starts every login token. Tokens use only uppercase // letters and digits so a QR code can encode them in alphanumeric mode. LoginTokenPrefix = "SFLT" // LoginTokenBytes is the number of random bytes in a login token. LoginTokenBytes = 32 // DefaultLoginTokenTTL is the platform default lifetime of a reusable // login token. DefaultLoginTokenTTL = 30 * 24 * time.Hour // DefaultLoginTokenMaxTTL is the platform ceiling on any login token's // lifetime: one school year plus slack. DefaultLoginTokenMaxTTL = 400 * 24 * time.Hour // DefaultSingleUseLoginTokenTTL is the default lifetime of a // single-use login token. DefaultSingleUseLoginTokenTTL = 20 * time.Minute )
const ( // APIKeyPrefix is the prefix for generated API keys. APIKeyPrefix = "sf_" // APIKeyBytes is the number of random bytes for API key generation. APIKeyBytes = 32 )
Variables ¶
var ( // ErrInvalidLoginToken is returned for every login token that cannot // be used: unknown, malformed, expired, revoked, inactive, or already // used. Callers must not distinguish these cases to the presenter. ErrInvalidLoginToken = errors.New("credential: invalid login token") // ErrInvalidExpiry is returned when a requested expiry is not in the // future. ErrInvalidExpiry = errors.New("credential: login token expiry must be in the future") )
var ErrCorruptCredential = errors.New("credential: stored credential is corrupt")
ErrCorruptCredential is returned when a stored credential cannot be read, such as a secret hash in an unknown format.
var ErrInvalidClientCredentials = errors.New("credential: invalid client credentials")
ErrInvalidClientCredentials is returned by ValidateClientSecret for an unknown client, a client with no usable secret, and a wrong secret alike, so a caller cannot learn which client IDs exist.
Functions ¶
func LoginTokenIssuedBy ¶ added in v0.14.0
func LoginTokenIssuedBy(c *Credential) (uuid.UUID, error)
LoginTokenIssuedBy returns the manager that issued a login token, or uuid.Nil when none is recorded.
func LoginTokenSingleUse ¶ added in v0.14.0
func LoginTokenSingleUse(c *Credential) bool
LoginTokenSingleUse reports whether a login token is single-use.
Types ¶
type CreateAPIKeyInput ¶
type CreateAPIKeyInput struct {
PrincipalID uuid.UUID
Name string
Scopes []string
ExpiresAt *time.Time
Metadata map[string]any
}
CreateAPIKeyInput contains fields for creating an API key credential.
type CreateClientSecretInput ¶
type CreateClientSecretInput struct {
PrincipalID uuid.UUID
Name string
ExpiresAt *time.Time
Metadata map[string]any
}
CreateClientSecretInput contains fields for creating a client secret.
type CreateKeypairInput ¶
type CreateKeypairInput struct {
PrincipalID uuid.UUID
Name string
KeyAlgorithm string // RS256, ES256, EdDSA
PublicKeyPEM string
Scopes []string
ExpiresAt *time.Time
Metadata map[string]any
}
CreateKeypairInput contains fields for creating a keypair credential.
type CreatePasswordInput ¶
type CreatePasswordInput struct {
PrincipalID uuid.UUID
Password string `json:"-"` //nolint:gosec // G117: field holds user-provided value, not a hardcoded secret
}
CreatePasswordInput contains fields for creating a password credential.
type Credential ¶
type Credential struct {
ID uuid.UUID `json:"id"`
PrincipalID uuid.UUID `json:"principal_id"`
Type Type `json:"type"`
Identifier string `json:"identifier,omitempty"` // e.g., key prefix for API keys
Name string `json:"name,omitempty"`
Scopes []string `json:"scopes,omitempty"`
Active bool `json:"active"`
ExpiresAt *time.Time `json:"expires_at,omitempty"`
Revoked bool `json:"revoked"`
RevokedAt *time.Time `json:"revoked_at,omitempty"`
RevokedReason string `json:"revoked_reason,omitempty"`
LastUsedAt *time.Time `json:"last_used_at,omitempty"`
LastUsedIP string `json:"last_used_ip,omitempty"`
Metadata map[string]any `json:"metadata,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Credential represents a credential record.
type DefaultService ¶
type DefaultService struct {
// contains filtered or unexported fields
}
DefaultService implements the Service interface.
func (*DefaultService) CreateAPIKey ¶
func (s *DefaultService) CreateAPIKey(ctx context.Context, input CreateAPIKeyInput) (*GeneratedAPIKey, error)
CreateAPIKey generates a new API key for a principal.
func (*DefaultService) CreateClientSecret ¶
func (s *DefaultService) CreateClientSecret(ctx context.Context, input CreateClientSecretInput) (*GeneratedClientSecret, error)
CreateClientSecret generates a new client secret for an application principal.
func (*DefaultService) CreateKeypair ¶
func (s *DefaultService) CreateKeypair(ctx context.Context, input CreateKeypairInput) (*KeypairCredential, error)
CreateKeypair creates a keypair credential for a principal.
func (*DefaultService) CreatePassword ¶
func (s *DefaultService) CreatePassword(ctx context.Context, input CreatePasswordInput) error
CreatePassword creates a password credential for a principal.
func (*DefaultService) GetByID ¶
func (s *DefaultService) GetByID(ctx context.Context, id uuid.UUID) (*Credential, error)
GetByID retrieves a credential by ID.
func (*DefaultService) GetKeypairByKeyID ¶
func (s *DefaultService) GetKeypairByKeyID(ctx context.Context, keyID string) (*KeypairCredential, error)
GetKeypairByKeyID retrieves a keypair credential by key ID.
func (*DefaultService) IssueLoginToken ¶ added in v0.14.0
func (s *DefaultService) IssueLoginToken(ctx context.Context, input IssueLoginTokenInput) (*GeneratedLoginToken, error)
IssueLoginToken creates a login token credential and returns its plain value once.
func (*DefaultService) ListAPIKeys ¶
func (s *DefaultService) ListAPIKeys(ctx context.Context, principalID uuid.UUID) ([]*Credential, error)
ListAPIKeys lists all API keys for a principal.
func (*DefaultService) ListKeypairs ¶
func (s *DefaultService) ListKeypairs(ctx context.Context, principalID uuid.UUID) ([]*KeypairCredential, error)
ListKeypairs lists all keypairs for a principal.
func (*DefaultService) UpdateLastUsed ¶
UpdateLastUsed updates the last used timestamp and IP.
func (*DefaultService) UpdatePassword ¶
func (s *DefaultService) UpdatePassword(ctx context.Context, principalID uuid.UUID, newPassword string) error
UpdatePassword updates the password for a principal.
func (*DefaultService) ValidateAPIKey ¶
func (s *DefaultService) ValidateAPIKey(ctx context.Context, plainKey string) (*Credential, error)
ValidateAPIKey validates an API key and returns the associated credential.
func (*DefaultService) ValidateClientSecret ¶
func (s *DefaultService) ValidateClientSecret(ctx context.Context, clientID, plainSecret string) (*Credential, error)
ValidateClientSecret validates a client secret.
func (*DefaultService) VerifyLoginToken ¶ added in v0.14.0
func (s *DefaultService) VerifyLoginToken(ctx context.Context, plainToken, ip string) (*Credential, error)
VerifyLoginToken returns the credential for a usable login token and records its use. A single-use token is revoked atomically, so it succeeds at most once even under concurrent presentation. Every unusable token yields ErrInvalidLoginToken.
func (*DefaultService) VerifyPassword ¶
func (s *DefaultService) VerifyPassword(ctx context.Context, principalID uuid.UUID, password string) (bool, error)
VerifyPassword verifies a password against the stored hash.
type GeneratedAPIKey ¶
type GeneratedAPIKey struct {
Credential *Credential `json:"credential"`
PlainKey string `json:"plain_key"` // Only available at creation time
Prefix string `json:"prefix"` // First 8 chars for identification
}
GeneratedAPIKey contains a newly generated API key. The PlainKey is only available at creation time.
type GeneratedClientSecret ¶
type GeneratedClientSecret struct {
Credential *Credential `json:"credential"`
PlainSecret string `json:"plain_secret"` // Only available at creation time
Prefix string `json:"prefix"` // First 8 chars for identification
}
GeneratedClientSecret contains a newly generated client secret.
type GeneratedLoginToken ¶ added in v0.14.0
type GeneratedLoginToken struct {
Credential *Credential `json:"credential"`
PlainToken string `json:"plain_token"`
Prefix string `json:"prefix"`
}
GeneratedLoginToken is a newly issued login token. PlainToken is available only here; the database stores its hash.
type IssueLoginTokenInput ¶ added in v0.14.0
type IssueLoginTokenInput struct {
// PrincipalID is the managed principal the token signs in.
PrincipalID uuid.UUID
// IssuedBy is the manager issuing the token.
IssuedBy uuid.UUID
// ExpiresAt is required; resolve it with LoginTokenPolicy.ExpiresAt.
ExpiresAt time.Time
// SingleUse revokes the token on its first successful use.
SingleUse bool
// Name is an optional label, such as the device or card it is for.
Name string
}
IssueLoginTokenInput describes a login token to issue.
type KeypairCredential ¶
type KeypairCredential struct {
Credential *Credential `json:"credential"`
KeyID string `json:"key_id"` // Used in JWT kid header
KeyAlgorithm string `json:"key_algorithm"` // RS256, ES256, etc.
PublicKeyPEM string `json:"public_key_pem"`
}
KeypairCredential contains keypair-specific data.
type LoginTokenPolicy ¶ added in v0.14.0
type LoginTokenPolicy struct {
// DefaultTTL applies to reusable tokens issued without an explicit
// expiry. A client may set its own, for example a school year.
DefaultTTL time.Duration
// MaxTTL caps every token's lifetime.
MaxTTL time.Duration
// SingleUseTTL applies to single-use tokens issued without an
// explicit expiry.
SingleUseTTL time.Duration
}
LoginTokenPolicy bounds login token lifetimes. Zero fields use the platform defaults.
func (LoginTokenPolicy) ExpiresAt ¶ added in v0.14.0
func (p LoginTokenPolicy) ExpiresAt(now time.Time, requested *time.Time, singleUse bool) (exp time.Time, clamped bool, err error)
ExpiresAt resolves a token's expiry: the requested time if given, else the single-use or default lifetime, never beyond MaxTTL. clamped reports that the result was cut to MaxTTL, which callers should log.
type Service ¶
type Service interface {
// Password operations
CreatePassword(ctx context.Context, input CreatePasswordInput) error
VerifyPassword(ctx context.Context, principalID uuid.UUID, password string) (bool, error)
UpdatePassword(ctx context.Context, principalID uuid.UUID, newPassword string) error
// API key operations
CreateAPIKey(ctx context.Context, input CreateAPIKeyInput) (*GeneratedAPIKey, error)
ValidateAPIKey(ctx context.Context, plainKey string) (*Credential, error)
ListAPIKeys(ctx context.Context, principalID uuid.UUID) ([]*Credential, error)
// Client secret operations
CreateClientSecret(ctx context.Context, input CreateClientSecretInput) (*GeneratedClientSecret, error)
ValidateClientSecret(ctx context.Context, clientID, plainSecret string) (*Credential, error)
// Keypair operations
CreateKeypair(ctx context.Context, input CreateKeypairInput) (*KeypairCredential, error)
GetKeypairByKeyID(ctx context.Context, keyID string) (*KeypairCredential, error)
ListKeypairs(ctx context.Context, principalID uuid.UUID) ([]*KeypairCredential, error)
// Login token operations
IssueLoginToken(ctx context.Context, input IssueLoginTokenInput) (*GeneratedLoginToken, error)
VerifyLoginToken(ctx context.Context, plainToken, ip string) (*Credential, error)
// Common operations
GetByID(ctx context.Context, id uuid.UUID) (*Credential, error)
Revoke(ctx context.Context, id uuid.UUID, reason string) error
UpdateLastUsed(ctx context.Context, id uuid.UUID, ip string) error
}
Service defines the business logic interface for credentials.
func NewService ¶
NewService creates a new CredentialService.
type Type ¶
type Type string
Type represents the type of credential.
const ( // TypePassword represents a password credential. TypePassword Type = "password" // TypeAPIKey represents an API key credential. TypeAPIKey Type = "api_key" // TypeKeypair represents a public/private keypair credential. TypeKeypair Type = "keypair" // TypeWebAuthn represents a WebAuthn credential. TypeWebAuthn Type = "webauthn" // TypeTOTP represents a TOTP credential. TypeTOTP Type = "totp" // TypeClientSecret represents an OAuth client secret. TypeClientSecret Type = "client_secret" // TypeLoginToken represents a manager-issued sign-in token for a // managed principal. TypeLoginToken Type = "login_token" )