auth

package module
v0.3.4 Latest Latest
Warning

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

Go to latest
Published: Sep 23, 2026 License: Apache-2.0 Imports: 34 Imported by: 6

Documentation

Overview

Package auth provides the building blocks of email and password authentication: argon2id password hashing, session tokens and one-time codes stored only as hashes, email normalisation, session cookies, the request middleware, a permission catalog and plain authentication emails (ADR-0024, ADR-0038).

Apps own their authentication flows, tables and SQL in internal/modules/auth (generated by orb new): its use cases call these helpers for the security-sensitive steps, so fixes to them arrive with go get, while every flow stays readable and editable in the app.

Stability: stable (ADR-0015, ADR-0054).

Index

Constants

View Source
const (
	// APIKeyPrefix starts every API key.
	APIKeyPrefix = "gbk_"
	// APIKeyLength is the length of every API key.
	APIKeyLength = len(APIKeyPrefix) + apiKeyLookupLength + 1 + apiKeySecretLength
)

API keys (ADR-0058) look like gbk_<lookup ID>_<secret>: the prefix lets middleware, secret scanners and people recognise them; the lookup ID (128 random bits, 26 characters) finds the stored key and may be logged; the secret (256 random bits, 52 characters) is never stored or logged. Both parts use lowercase base32, so a key has no characters that need escaping and selects with a double click.

View Source
const (
	// DefaultAPIKeyMaxTTL is the longest lifetime a new API key may have by
	// default; apps read it from a runtime setting clamped to APIKeyTTLLimits.
	DefaultAPIKeyMaxTTL = 90 * 24 * time.Hour
	// APIKeyTouchInterval is the shortest time between two last-used updates
	// of an API key, so busy clients don't write on every request.
	APIKeyTouchInterval = time.Minute
	// APIKeyRetention is how long expired and revoked API keys stay listed
	// before cleanup removes them.
	APIKeyRetention = 30 * 24 * time.Hour
)

Defaults and limits for API keys.

View Source
const (
	DefaultSessionIdleTTL          = 14 * 24 * time.Hour
	DefaultSessionAbsoluteTTL      = 90 * 24 * time.Hour
	DefaultVerificationCodeTTL     = 15 * time.Minute
	DefaultResetCodeTTL            = 30 * time.Minute
	DefaultDeletedAccountRetention = 30 * 24 * time.Hour
	// DefaultUnverifiedAccountTTL is how long an account whose address was
	// never verified stays before cleanup deletes it.
	DefaultUnverifiedAccountTTL = 7 * 24 * time.Hour

	// DefaultLoginAttempts per email address and client network within
	// DefaultLoginWindow.
	DefaultLoginAttempts = 10
	DefaultLoginWindow   = 15 * time.Minute
	// DefaultLoginAddressAttempts per email address from any network within
	// DefaultLoginWindow: looser than DefaultLoginAttempts, so someone else
	// can't cheaply lock the owner out, while credential stuffing from many
	// networks stays bounded.
	DefaultLoginAddressAttempts = 50

	// DefaultCodeAttempts is how many verification or reset code checks one
	// address allows within DefaultCodeWindow, across every code sent to it.
	DefaultCodeAttempts = 20
	DefaultCodeWindow   = 24 * time.Hour

	// DefaultMinResponseTime is the shortest time registration, resending a
	// verification code and requesting a password reset take, so their
	// timing doesn't reveal whether an address has an account.
	DefaultMinResponseTime = 300 * time.Millisecond

	MinPasswordLength = 12
	MaxPasswordLength = 128

	// CodeMaxAttempts is how many guesses one code allows.
	CodeMaxAttempts = 5
	// CodeResendInterval is the shortest time between two codes of the same
	// purpose for one account.
	CodeResendInterval = time.Minute
	// SessionTouchInterval is the shortest time between two last-seen updates
	// of a session, so busy clients don't write on every request.
	SessionTouchInterval = time.Minute
	// EndedSessionRetention is how long ended sessions stay for
	// investigations before cleanup removes them.
	EndedSessionRetention = 7 * 24 * time.Hour
	// CodeRetention is how long codes stay before cleanup removes them.
	CodeRetention = 24 * time.Hour

	DefaultCookieName = "__Host-session"

	// MFAChallengeTTL is how long a sign-in waits for its second factor.
	MFAChallengeTTL = 5 * time.Minute
	// MFAChallengeMaxAttempts is how many wrong second factors one sign-in
	// allows.
	MFAChallengeMaxAttempts = 5
)

Defaults and policy constants shared by generated apps.

View Source
const (
	TOTPDigits = 6
	TOTPPeriod = 30 * time.Second
	// TOTPSkew is how many steps before and after the current one are
	// accepted, for clocks that drift.
	TOTPSkew = 1
)

TOTP parameters every authenticator app supports (RFC 6238).

View Source
const DefaultHashMaxWait = 5 * time.Second

DefaultHashMaxWait is how long Hasher waits for a free slot by default.

View Source
const RecentVerification = 10 * time.Minute

RecentVerification is how long after verifying a second factor a session can change the account's sign-in methods without the password (ADR-0044), and how long after signing in an account without a password can confirm sensitive changes (ADR-0046).

View Source
const RecoveryCodeCount = 10

RecoveryCodeCount is how many recovery codes a user gets at a time.

Variables

View Source
var (
	SessionIdleLimits       = Limits{Min: 5 * time.Minute, Max: 90 * 24 * time.Hour}
	SessionAbsoluteLimits   = Limits{Min: time.Hour, Max: 365 * 24 * time.Hour}
	VerificationCodeLimits  = Limits{Min: time.Minute, Max: time.Hour}
	ResetCodeLimits         = Limits{Min: time.Minute, Max: 2 * time.Hour}
	DeletedRetentionLimits  = Limits{Min: 0, Max: 365 * 24 * time.Hour}
	UnverifiedAccountLimits = Limits{Min: time.Hour, Max: 90 * 24 * time.Hour}
)

Hard limits for authentication durations.

View Source
var (
	// ErrInvalidEmail reports an email address that isn't valid.
	ErrInvalidEmail = errors.New("auth: invalid email address")

	// ErrWeakPassword reports a password that fails the password policy. The
	// error is a [*PasswordError].
	ErrWeakPassword = errors.New("auth: password does not meet the policy")

	// ErrUnauthenticated reports a missing, invalid, expired or revoked
	// session.
	ErrUnauthenticated = errors.New("auth: authentication required")
)

Errors returned by the helpers. Check them with errors.Is.

View Source
var (
	// ErrInvalidKeyring reports an AUTH_ENCRYPTION_KEYS value that can't be
	// parsed.
	ErrInvalidKeyring = errors.New("auth: invalid encryption keys")
	// ErrUnknownKey reports a ciphertext encrypted with a key the keyring
	// doesn't hold.
	ErrUnknownKey = errors.New("auth: ciphertext was encrypted with an unknown key")
	// ErrDecrypt reports a ciphertext that was changed, or bound to other
	// additional data.
	ErrDecrypt = errors.New("auth: ciphertext can't be decrypted")
)

Errors returned by Keyring.

View Source
var APIKeyTTLLimits = Limits{Min: time.Hour, Max: 365 * 24 * time.Hour}

APIKeyTTLLimits bounds the longest lifetime of a new API key: however an operator sets it, a key lives at least an hour and at most a year.

View Source
var ErrHasherBusy = errors.New("auth: password hashing is busy")

ErrHasherBusy reports that no password hashing slot became free before the request's context ended or the hasher's maximum wait passed. Answer 503: the server is overloaded, not the request wrong.

View Source
var ErrInvalidTOTPSecret = errors.New("auth: invalid TOTP secret")

ErrInvalidTOTPSecret reports a secret that isn't base32.

View Source
var ErrRateLimited = errors.New("auth: too many failed authentications")

ErrRateLimited reports a client that failed to authenticate too often. The error is a *RateLimitError.

Functions

func APIKeyMatches

func APIKeyMatches(key string, hash []byte) bool

APIKeyMatches reports, in constant time, whether key is the one hashed as hash. A nil or short hash never matches, but takes the same time.

func ClearSessionCookie

func ClearSessionCookie(name string) *http.Cookie

ClearSessionCookie returns a cookie that removes the session cookie.

func CodeMatches

func CodeMatches(id, code string, hash []byte) bool

CodeMatches reports, in constant time, whether code is the one hashed as hash for row id.

func HashAPIKey

func HashAPIKey(key string) []byte

HashAPIKey returns the SHA-256 of an API key, for storing. The secret has 256 random bits, so a fast hash is enough: nobody can guess it from the hash.

func HashCode

func HashCode(id, code string) []byte

HashCode returns the hash to store for code, bound to its row ID so equal codes hash differently.

func HashRecoveryCode

func HashRecoveryCode(userID, code string) []byte

HashRecoveryCode returns the hash to store for a user's recovery code. Codes carry 80 random bits, so a stolen hash can't be reversed by trying codes; binding it to the user makes equal codes of two users hash differently. Codes made before 2026-09-16 carry 50 bits and still match (security review AUTH-M-4).

func HashToken

func HashToken(token string) []byte

HashToken returns the SHA-256 of a token, for storing and looking it up.

func IsAPIKey

func IsAPIKey(token string) bool

IsAPIKey reports whether token has the API key prefix. A token with the prefix is never a session token: Middleware authenticates it only as an API key, and NewToken never returns one.

func Middleware

func Middleware(a Authenticator, opts ...MiddlewareOption) func(http.Handler) http.Handler

Middleware stores every request's ClientInfo in its context, and authenticates requests carrying a session token in an "Authorization: Bearer" header or the session cookie, putting the principal and its actor in the context. Requests without a valid token continue anonymously: use cases decide what needs authentication. When the authenticator fails for another reason (the database is down) it responds 503, rather than treating a signed-in user as anonymous.

A token starting with APIKeyPrefix is never passed to the session authenticator: in the Authorization header it goes to the API key authenticator of WithAPIKeys, and in a cookie it is ignored. An API key authenticator's *RateLimitError responds 429 too_many_attempts.

Cookie-authenticated requests need cross-origin protection (httpx.CrossOrigin) earlier in the chain.

func NewAPIKey

func NewAPIKey() (key, lookupID string, hash []byte)

NewAPIKey returns a new API key, its lookup ID and the hash to store. Show the key once and store only the lookup ID and hash.

func NewCode

func NewCode() string

NewCode returns a uniformly random 6-digit code.

func NewID

func NewID(prefix string) string

NewID returns a random identifier such as usr_2x7…, with 128 bits of randomness.

func NewKeyringKey

func NewKeyringKey(id string) string

NewKeyringKey returns a new random key entry "id:base64key", for generating AUTH_ENCRYPTION_KEYS.

func NewRecoveryCodes

func NewRecoveryCodes() []string

NewRecoveryCodes returns RecoveryCodeCount single-use recovery codes, each 16 base32 characters (80 random bits) shown as "xxxx-xxxx-xxxx-xxxx". Show them once and store only HashRecoveryCode.

func NewTOTPSecret

func NewTOTPSecret() string

NewTOTPSecret returns a new TOTP secret: 20 random bytes (160 bits, as RFC 4226 recommends) in unpadded base32, the form authenticator apps accept when typed or scanned. Store it only encrypted (Keyring).

func NewToken

func NewToken() (token string, hash []byte)

NewToken returns a session token with 256 bits of randomness and the hash to store. Store only the hash. The token never starts with APIKeyPrefix, which marks API keys.

func NormalizeEmail

func NormalizeEmail(s string) (email, normalized string, err error)

NormalizeEmail returns the address to send to and the lowercased address accounts are unique by, or ErrInvalidEmail.

An address with a non-ASCII character that lowercasing changes, such as the Kelvin sign (U+212A, lowercased to k) or an uppercase Ä, is refused: otherwise a look-alike address could take the account key of another address and receive its codes. Lowercase non-ASCII characters are accepted.

func NormalizeRecoveryCode

func NormalizeRecoveryCode(code string) string

NormalizeRecoveryCode returns code lowercased without spaces or hyphens, so "ABCDE-FGHIJ", "abcde fghij" and "abcdefghij" match.

func ParseAPIKey

func ParseAPIKey(key string) (lookupID string, err error)

ParseAPIKey returns the lookup ID of a well-formed API key, or ErrUnauthenticated. It checks only the format: look the key up by its lookup ID, then compare with APIKeyMatches.

func SessionCookie

func SessionCookie(name, token string, expires time.Time) *http.Cookie

SessionCookie returns the cookie that carries token for browsers: Secure, HttpOnly, SameSite=Lax, path "/", expiring at expires.

func TOTPCode

func TOTPCode(secret string, t time.Time) (string, error)

TOTPCode returns the code for secret at t.

func TOTPQRCode

func TOTPQRCode(uri string) (string, error)

TOTPQRCode returns a PNG image of uri (from TOTPURI) as a data URL, ready for an <img> tag, so an authenticator app can scan it before a client renders its own (ADR-0043).

func TOTPStep

func TOTPStep(t time.Time) int64

TOTPStep returns the time step containing t.

func TOTPURI

func TOTPURI(issuer, account, secret string) string

TOTPURI returns the otpauth:// URI authenticator apps read from a QR code: issuer is the app's name and account the user's email address.

func TokenFrom

func TokenFrom(r *http.Request, cookieName string) string

TokenFrom returns the request's bearer token, or else its session cookie.

func ValidatePassword

func ValidatePassword(ctx context.Context, password string, checker PasswordChecker) error

ValidatePassword applies the password policy to a new password: 12 to 128 characters, not blank, and checker when not nil. It returns a *PasswordError.

func VerifyTOTP

func VerifyTOTP(secret, code string, now time.Time) (step int64, ok bool)

VerifyTOTP reports whether code is valid for secret at now, within TOTPSkew steps, and returns the matching step. Store the step and accept a later code only for a later step, so a code can't be used twice. Spaces in code are ignored.

func WithClientInfo

func WithClientInfo(ctx context.Context, c ClientInfo) context.Context

WithClientInfo returns a copy of ctx carrying c. It also sets c as the context's actor.Client, so audit events any module records for the request carry the client's IP address and user agent.

func WithPrincipal

func WithPrincipal(ctx context.Context, p Principal) context.Context

WithPrincipal returns a copy of ctx carrying p and its actor: a user, or a service for a service account's API key. An API key's actor gets only the permissions Principal.Restrict leaves it.

Types

type APIKeyAuthenticator

type APIKeyAuthenticator interface {
	AuthenticateAPIKey(ctx context.Context, key string) (Principal, error)
}

An APIKeyAuthenticator resolves an API key, returning ErrUnauthenticated for an unknown, malformed, expired or revoked key, or one whose owner is deleted or disabled, and a *RateLimitError for a client that failed too often. The app's auth use cases implement it.

type Authenticator

type Authenticator interface {
	Authenticate(ctx context.Context, token string) (Principal, error)
}

An Authenticator resolves a session token, returning ErrUnauthenticated for an unknown, ended or expired session. The app's auth use cases implement it.

type Catalog

type Catalog struct {
	// contains filtered or unexported fields
}

A Catalog declares the permissions modules check and the roles that grant them. Access is denied by default: a role grants only the permissions declared for it, and a role name stored for a user but missing from the catalog grants nothing. Declare everything at startup, then call Catalog.Freeze; invalid or late declarations are programming errors and panic.

func NewCatalog

func NewCatalog() *Catalog

NewCatalog returns an empty catalog.

func (*Catalog) AllPermissions

func (c *Catalog) AllPermissions() []Permission

AllPermissions returns every declared permission in declaration order.

func (*Catalog) Freeze

func (c *Catalog) Freeze()

Freeze stops further declarations. Services using the catalog call it.

func (*Catalog) HasRole

func (c *Catalog) HasRole(role string) bool

HasRole reports whether role is declared.

func (*Catalog) Permission

func (c *Catalog) Permission(name, description string)

Permission declares a permission, such as "ops.settings.write".

func (*Catalog) Permissions

func (c *Catalog) Permissions(roles ...string) []string

Permissions returns the sorted permissions granted by roles. Unknown roles grant nothing.

func (*Catalog) PermissionsFor

func (c *Catalog) PermissionsFor(roles []string, mfaVerified bool) (granted, stepUp []string)

PermissionsFor returns the sorted permissions roles grant a session: granted holds those of roles that don't require two-factor authentication, plus those of roles that do when mfaVerified; stepUp holds the permissions only a verified session would add. Unknown roles grant nothing.

func (*Catalog) RequireMFA

func (c *Catalog) RequireMFA(roles ...string)

RequireMFA marks declared roles as requiring two-factor authentication: their permissions are granted only to sessions verified with a second factor (ADR-0043). It is code, not a runtime setting, so operators can't weaken it.

func (*Catalog) RequiresMFA

func (c *Catalog) RequiresMFA(roles ...string) bool

RequiresMFA reports whether any of roles requires two-factor authentication.

func (*Catalog) Role

func (c *Catalog) Role(name, description string, permissions ...string)

Role declares a role granting permissions, each already declared.

func (*Catalog) Roles

func (c *Catalog) Roles() []Role

Roles returns every declared role in declaration order.

type ClientInfo

type ClientInfo struct {
	IP        string
	UserAgent string
}

ClientInfo describes the client making a request, for sessions and audit events.

func ClientInfoFrom

func ClientInfoFrom(r *http.Request) ClientInfo

ClientInfoFrom returns the client's IP address (from RemoteAddr: run trusted-proxy middleware first behind a proxy) and user agent.

func ClientInfoFromContext

func ClientInfoFromContext(ctx context.Context) ClientInfo

ClientInfoFromContext returns the client details Middleware stored for the request, or empty details.

func (ClientInfo) Clean

func (c ClientInfo) Clean() ClientInfo

Clean returns c with the IP address in canonical form (empty when it isn't one) and a bounded, valid user agent, ready to store.

type EmailPreview

type EmailPreview struct {
	// Name is auth.<kind>, such as auth.verification_code.
	Name        string
	Description string
	Category    string
	// Build renders the message for to.
	Build func(ctx context.Context, to string) (mail.Message, error)
}

EmailPreview is one of the emails this module sends, rendered with sample data for the Dev Portal's template preview (ADR-0074).

func BrandedEmailPreviews

func BrandedEmailPreviews(brand mail.Brand) []EmailPreview

BrandedEmailPreviews returns a preview of every email NewBrandedEmails sends, with sample codes and names, in brand's layout. Category is the message's own tag.

func EmailPreviews

func EmailPreviews(appName string) []EmailPreview

EmailPreviews returns the previews of BrandedEmailPreviews with appName as the whole brand.

type Emails

type Emails interface {
	// SendVerificationCode sends the code that verifies to's address.
	SendVerificationCode(ctx context.Context, to, code string, ttl time.Duration) error
	// SendPasswordResetCode sends the code that resets to's password.
	SendPasswordResetCode(ctx context.Context, to, code string, ttl time.Duration) error
	// SendAccountExists tells to that someone tried to register with an
	// address that already has an account.
	SendAccountExists(ctx context.Context, to string) error
	// SendPasswordChanged tells to that their password changed.
	SendPasswordChanged(ctx context.Context, to string) error
	// SendTwoFactorEnabled tells to that two-factor authentication was
	// turned on.
	SendTwoFactorEnabled(ctx context.Context, to string) error
	// SendTwoFactorDisabled tells to that two-factor authentication was
	// turned off.
	SendTwoFactorDisabled(ctx context.Context, to string) error
	// SendRecoveryCodeUsed tells to that a recovery code was used to sign in
	// and how many are left.
	SendRecoveryCodeUsed(ctx context.Context, to string, remaining int) error
	// SendSignInMethodAdded tells to that a sign-in method such as Google was
	// linked to their account.
	SendSignInMethodAdded(ctx context.Context, to, method string) error
	// SendSignInMethodRemoved tells to that a sign-in method was unlinked.
	SendSignInMethodRemoved(ctx context.Context, to, method string) error
	// SendPasskeyAdded tells to that a passkey named name was added.
	SendPasskeyAdded(ctx context.Context, to, name string) error
	// SendPasskeyRemoved tells to that a passkey named name was removed.
	SendPasskeyRemoved(ctx context.Context, to, name string) error
}

Emails sends the emails authentication needs. Apps use NewBrandedEmails or their own templates. Implementations should queue rather than deliver inline (jobs.AsyncSender), so requests don't wait for the provider.

func NewBrandedEmails

func NewBrandedEmails(sender mail.Sender, brand mail.Brand) Emails

NewBrandedEmails returns emails in brand's layout (mail.Brand.Render) sent through sender, which sets the From address (mail.WithDefaults). brand.Name appears in subjects and bodies.

func NewMailEmails

func NewMailEmails(sender mail.Sender, appName string) Emails

NewMailEmails returns the emails of NewBrandedEmails with appName as the whole brand: no link, logo or support address.

type Hasher

type Hasher struct {
	// contains filtered or unexported fields
}

Hasher hashes and verifies passwords with argon2id, bounding how many hashes run at once so a burst of sign-ins can't exhaust memory, and how long a request waits for a turn, so a flood can't hold requests without limit. It is safe for concurrent use.

func NewHasher

func NewHasher() (*Hasher, error)

NewHasher returns a hasher with the current parameters and default options.

func NewHasherWith

func NewHasherWith(opts ...HasherOption) (*Hasher, error)

NewHasherWith returns a hasher with the current parameters and opts.

func (*Hasher) Hash

func (h *Hasher) Hash(password string) (string, error)

Hash returns an encoded hash: $argon2id$v=19$m=…,t=…,p=…$salt$key. It returns ErrHasherBusy when no slot frees up within the maximum wait; prefer Hasher.HashContext in requests.

func (*Hasher) HashContext

func (h *Hasher) HashContext(ctx context.Context, password string) (string, error)

HashContext is Hasher.Hash, waiting for a free slot only until ctx ends (ErrHasherBusy).

func (*Hasher) Verify

func (h *Hasher) Verify(password, encoded string) (ok, rehash bool)

Verify reports whether password matches encoded, and whether the hash should be replaced (Hash again and store it) because its parameters are outdated. A busy hasher reports no match; prefer Hasher.VerifyContext in requests, which tells the two apart.

func (*Hasher) VerifyContext

func (h *Hasher) VerifyContext(ctx context.Context, password, encoded string) (ok, rehash bool, err error)

VerifyContext is Hasher.Verify, waiting for a free slot only until ctx ends. It returns ErrHasherBusy when it couldn't check the password.

func (*Hasher) VerifyDummy

func (h *Hasher) VerifyDummy(password string)

VerifyDummy does the work of a failed Verify. Call it when an account doesn't exist, so the response takes as long as for a wrong password.

func (*Hasher) VerifyDummyContext

func (h *Hasher) VerifyDummyContext(ctx context.Context, password string) error

VerifyDummyContext is Hasher.VerifyDummy with the waiting of Hasher.VerifyContext; it returns only ErrHasherBusy.

type HasherOption

type HasherOption interface {
	// contains filtered or unexported methods
}

A HasherOption configures a Hasher.

func WithHashConcurrency

func WithHashConcurrency(n int) HasherOption

WithHashConcurrency sets how many hashes run at once; each uses 19 MiB. Default: GOMAXPROCS, and at least 4.

func WithHashMaxWait

func WithHashMaxWait(d time.Duration) HasherOption

WithHashMaxWait sets how long hashing waits for a free slot before returning ErrHasherBusy. Default: DefaultHashMaxWait.

type Keyring

type Keyring struct {
	// contains filtered or unexported fields
}

A Keyring encrypts small secrets, such as TOTP secrets, with AES-256-GCM. It holds one or more keys, each with an ID: the first key encrypts, and every key decrypts, so a key can be replaced without a flag day (put the new key first, re-encrypt, then remove the old one).

func ParseKeyring

func ParseKeyring(spec string) (*Keyring, error)

ParseKeyring parses keys written as comma-separated "id:base64key" entries, each key 32 bytes, the first used to encrypt. IDs are lowercase letters, digits, hyphens and underscores (at most 32).

func (*Keyring) CurrentKeyID

func (k *Keyring) CurrentKeyID() string

CurrentKeyID returns the ID of the key that encrypts.

func (*Keyring) Decrypt

func (k *Keyring) Decrypt(keyID string, ciphertext, additionalData []byte) ([]byte, error)

Decrypt opens a ciphertext from Keyring.Encrypt with the key keyID and the same additionalData. It returns ErrUnknownKey or ErrDecrypt.

func (*Keyring) Encrypt

func (k *Keyring) Encrypt(plaintext, additionalData []byte) (keyID string, ciphertext []byte, err error)

Encrypt seals plaintext with the current key, bound to additionalData (for example the owning user's ID and the secret's purpose), and returns the key's ID and the nonce followed by the ciphertext.

type Limits

type Limits struct {
	Min, Max time.Duration
}

Limits bounds a duration. Apps read durations from runtime settings and clamp them, so a setting can't weaken security beyond the limits.

func (Limits) Clamp

func (l Limits) Clamp(d time.Duration) time.Duration

Clamp returns d within the limits.

type MiddlewareOption

type MiddlewareOption interface {
	// contains filtered or unexported methods
}

A MiddlewareOption configures Middleware.

func WithAPIKeys

WithAPIKeys authenticates bearer tokens that start with APIKeyPrefix with a (ADR-0058). Without it, such tokens are ignored and the request continues anonymously.

func WithCookieName

func WithCookieName(name string) MiddlewareOption

WithCookieName sets the session cookie name. Default: DefaultCookieName.

func WithLogger

func WithLogger(logger *slog.Logger) MiddlewareOption

WithLogger sets the logger for authentication failures. Default: discard.

type PasswordChecker

type PasswordChecker func(ctx context.Context, password string) error

A PasswordChecker rejects a new password by returning an error whose message explains why, such as "appears in a list of breached passwords". It must return nil when it can't decide, for example when a remote list is unreachable.

type PasswordError

type PasswordError struct {
	Reason string
}

PasswordError explains why a password was rejected. Reason never contains the password.

func (*PasswordError) Error

func (e *PasswordError) Error() string

Error returns "auth: password " followed by Reason.

func (*PasswordError) Unwrap

func (e *PasswordError) Unwrap() error

Unwrap returns ErrWeakPassword.

type Permission

type Permission struct {
	Name        string
	Description string
}

Permission is a declared permission.

type Principal

type Principal struct {
	UserID    string
	SessionID string
	// Permissions are granted by the user's roles to this session.
	Permissions []string
	// StepUp are permissions of roles requiring two-factor authentication,
	// held back because the session isn't verified with a second factor.
	StepUp []string
	// MFAVerified reports whether the session was verified with a second
	// factor, and MFAVerifiedAt when it last was.
	MFAVerified   bool
	MFAVerifiedAt time.Time
	// SignedInAt is when the session started.
	SignedInAt time.Time

	// APIKeyID is the API key that authenticated the request, empty for a
	// session (ADR-0058). SessionID is empty for an API key.
	APIKeyID string
	// Scopes limit an API key to these permissions; empty means its owner's.
	// See [Principal.Restrict].
	Scopes []string
	// ServiceAccountID is set, instead of UserID, for an API key of a service
	// account: a non-human principal whose actor has kind service.
	ServiceAccountID string
	// OrgID is the organisation an organisation's service account belongs
	// to; it can act in no other.
	OrgID string
}

Principal is the authenticated user of a request.

func PrincipalFrom

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

PrincipalFrom returns the authenticated principal in ctx.

func (Principal) APIKey

func (p Principal) APIKey() bool

APIKey reports whether an API key, rather than a session, authenticated the principal.

func (Principal) RecentlySignedIn

func (p Principal) RecentlySignedIn(now time.Time) bool

RecentlySignedIn reports whether the session started less than RecentVerification before now.

func (Principal) RecentlyVerified

func (p Principal) RecentlyVerified(now time.Time) bool

RecentlyVerified reports whether the session verified a second factor less than RecentVerification before now. A stolen session that verified one long ago doesn't pass.

func (Principal) Restrict

func (p Principal) Restrict(granted, stepUp []string) (permissions, stepUpPermissions []string)

Restrict returns the permissions a principal may use from granted and stepUp, as computed from its roles. A session keeps both. An API key never gets step-up permissions, since it can't sign in with a second factor, and keeps only the granted permissions in its Scopes when it has any (ADR-0058). Every path that turns roles into an API key's permissions passes through it.

type RateLimitError

type RateLimitError struct {
	RetryAfter time.Duration
}

RateLimitError reports how long a client waits before authenticating again.

func (*RateLimitError) Error

func (e *RateLimitError) Error() string

Error says authentication failed too often and how long to wait, rounded to the second.

func (*RateLimitError) Unwrap

func (e *RateLimitError) Unwrap() error

Unwrap returns ErrRateLimited.

type Role

type Role struct {
	Name        string
	Description string
	Permissions []string
}

Role is a named set of permissions.

Directories

Path Synopsis
Package passkey provides passkeys (WebAuthn) for apps: registration and sign-in ceremonies checked against the relying party's ID and allowed origins, credential records to store, and the association files native apps need (ADR-0044).
Package passkey provides passkeys (WebAuthn) for apps: registration and sign-in ceremonies checked against the relying party's ID and allowed origins, credential records to store, and the association files native apps need (ADR-0044).
passkeytest
Package passkeytest is a software passkey authenticator for tests.
Package passkeytest is a software passkey authenticator for tests.
Package social signs people in with Google, Apple (ADR-0046) and GitHub (ADR-0059): the web authorization code flow with state, nonce and PKCE, ID tokens from native apps checked against the app's client IDs, Apple's client secret, token revocation and server-to-server notifications, and GitHub's user and email API for a provider without OpenID Connect.
Package social signs people in with Google, Apple (ADR-0046) and GitHub (ADR-0059): the web authorization code flow with state, nonce and PKCE, ID tokens from native apps checked against the app's client IDs, Apple's client secret, token revocation and server-to-server notifications, and GitHub's user and email API for a provider without OpenID Connect.
socialtest
Package socialtest runs an in-process OpenID Connect provider standing in for Google or Apple in tests: it serves signing keys, a token endpoint and a revocation endpoint, and issues signed ID tokens, authorization codes and Apple notifications.
Package socialtest runs an in-process OpenID Connect provider standing in for Google or Apple in tests: it serves signing keys, a token endpoint and a revocation endpoint, and issues signed ID tokens, authorization codes and Apple notifications.

Jump to

Keyboard shortcuts

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