auth

package
v0.12.0 Latest Latest
Warning

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

Go to latest
Published: Aug 3, 2026 License: AGPL-3.0 Imports: 13 Imported by: 0

Documentation

Index

Constants

View Source
const MaxClockSkew = 5 * time.Minute

MaxClockSkew is the maximum allowed difference between a request's Signature-Timestamp and the server's clock, in either direction.

Variables

This section is empty.

Functions

func HasSignatureHeaders added in v0.12.0

func HasSignatureHeaders(r *http.Request) bool

HasSignatureHeaders reports whether r carries request-signature headers at all -- i.e. whether the caller was *trying* to authenticate. Lets a handler tell "anonymous" apart from "authentication failed" without inspecting the error.

func WithIdentity

func WithIdentity(ctx context.Context, id Identity) context.Context

WithIdentity returns a context carrying id.

Types

type Identity

type Identity struct {
	AccountID string
	DeviceID  string
	Role      store.Role
}

Identity is the authenticated caller of a signed request, injected into the request context by Middleware.Require.

func IdentityFromContext

func IdentityFromContext(ctx context.Context) (Identity, bool)

IdentityFromContext retrieves the Identity injected by Middleware.Require. ok is false if the request was never authenticated.

type Middleware

type Middleware struct {
	DB     store.DBTX
	Logger *slog.Logger
	// Now returns the current time; overridable in tests.
	Now func() time.Time

	// StreamedBodyPaths are routes whose bodies are too large to buffer for
	// authentication (the blob upload), written as "METHOD /path/prefix".
	// On these, and ONLY these, the canonical string's body hash is taken
	// from the client's Blob-Digest header instead of from the body itself,
	// so the signature can be checked before reading anything -- see
	// authenticate.
	//
	// Deliberately an explicit allowlist rather than "honour the header
	// whenever it appears": on a normal route, trusting a claimed digest
	// would let a caller sign one body and send another, breaking the
	// body-to-signature binding every other endpoint relies on. A route
	// listed here MUST verify the body against the signed digest as it
	// reads it (blobstore.Put does).
	//
	// The method is part of the entry because it is specifically the upload
	// that streams: a GET or DELETE under the same path prefix has no body
	// and must keep authenticating the ordinary way.
	StreamedBodyPaths []string
	// contains filtered or unexported fields
}

Middleware authenticates incoming requests using per-request Ed25519 signatures (see signature.go) against devices registered in db.

func NewMiddleware

func NewMiddleware(db store.DBTX, logger *slog.Logger) *Middleware

NewMiddleware builds a Middleware backed by db, logging authentication failures (at Warn level, with detail) to logger. The nonce replay cache is created internally, so callers (and tests) need not manage it.

func (*Middleware) PurgeExpiredNonces

func (m *Middleware) PurgeExpiredNonces(now time.Time) int

PurgeExpiredNonces drops replay-cache entries whose skew window has passed, returning the number removed. Intended to be called periodically from the server's cleanup ticker to keep the cache bounded during idle periods.

func (*Middleware) Require

func (m *Middleware) Require(next http.Handler) http.Handler

Require wraps next so it only runs for requests with a valid signature, injecting the resulting Identity into the request context. Every failure mode (unknown key, bad signature, expired timestamp, replayed nonce, revoked device) produces the same generic 401 response, so as not to give an attacker an oracle; specifics go only to the log.

func (*Middleware) TryAuthenticate added in v0.12.0

func (m *Middleware) TryAuthenticate(r *http.Request) (Identity, error)

TryAuthenticate runs the same check [Require] does but returns the outcome instead of writing a response, for a handler that must decide for itself what an unauthenticated caller gets.

Only for routes where "no credentials" is a legitimate case with its own answer -- the prekey-bundle claim (SRV-04), which serves anyone but hands a one-time prekey only to a caller it can identify. Everything else belongs behind [Require], so the generic 401 stays the single answer to every failure mode and no handler can accidentally invent a weaker one.

Note the asymmetry a caller must preserve: an *absent* signature is a legitimate anonymous request, but a *present but invalid* one must still be refused. Silently treating a bad signature as anonymous would turn every client bug into a quiet downgrade.

Jump to

Keyboard shortcuts

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