auth

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Aug 10, 2026 License: Apache-2.0 Imports: 9 Imported by: 0

Documentation

Overview

Package auth provides tenant-aware API key authentication for cleat.

It implements Bearer token and header-based auth, tenant ID extraction from API keys via PostgreSQL lookup, and context propagation of tenant IDs.

Key types:

  • TenantStore — PostgreSQL-backed API key to tenant ID resolution
  • AuthMiddleware — HTTP middleware that validates and propagates tenant context

Package auth provides tenant-aware API key authentication for cleat's cleat execution framework.

It implements Bearer token and header-based auth, tenant ID extraction from API keys via PostgreSQL lookup, and context propagation of tenant IDs.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func GenerateAPIKey

func GenerateAPIKey() string

GenerateAPIKey generates a random API key string.

func Middleware

func Middleware(store engine.WorkflowStore, requireAuth bool, publicPatterns ...string) func(http.Handler) http.Handler

Middleware authenticates requests using a cleat API key. Supports: Authorization: Bearer cleat_sk_<key> Also supports: X-Cleat-API-Key: <key> When requireAuth is true, requests without a valid API key are rejected with 401, except for public paths (/healthz, /metrics, and any additional patterns passed via publicPatterns).

publicPatterns is a hand-maintained allowlist, not a generic plugin-declared mechanism. It exists for endpoints that are meant to be called by parties who cannot present a cleat API key -- an inbound webhook receiver with its own HMAC check (plugins/webhookingest), a third-party IdP's OAuth redirect target (plugins/oauthprovider) -- and would otherwise 401 before that endpoint's own verification ever runs. Each entry is a Go 1.22+ http.ServeMux pattern ("POST /ingest/{source_id}"), matched with the exact same method+wildcard semantics the real mux uses, via a throwaway ServeMux built only for matching (see buildPublicMatcher) -- so "POST /ingest/{source_id}" does not also make "GET /ingest/sources" public.

A plugin-declared version of this (a PublicRoutes() method plugins implement themselves) would need changes to plugin/plugin.go and to each plugin, which are outside this package's ownership; wiring the list by hand in cmd/cleat-worker/main.go is the option available without those changes. Anyone adding a new externally-triggered plugin endpoint must add it here too -- nothing enforces that the two stay in sync.

func TenantFromAPIKey

func TenantFromAPIKey(ctx context.Context, store engine.WorkflowStore, keyHash []byte) (uuid.UUID, error)

TenantFromAPIKey looks up a tenant by API key hash.

func TenantIDFromContext

func TenantIDFromContext(ctx context.Context) (uuid.UUID, bool)

TenantIDFromContext extracts the tenant ID from the request context.

func WithTenantID

func WithTenantID(ctx context.Context, tenantID uuid.UUID) context.Context

WithTenantID sets the tenant ID in the context. Primarily for testing.

Types

type TenantStore

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

TenantStore provides CRUD operations for tenants and their API keys.

func NewTenantStore

func NewTenantStore(db *sql.DB) *TenantStore

NewTenantStore creates a new TenantStore.

func (*TenantStore) CreateAPIKey

func (s *TenantStore) CreateAPIKey(ctx context.Context, tenantID uuid.UUID, description, rawKey string) error

CreateAPIKey creates an API key for a tenant. Returns the plaintext key (only returned once — caller must store it). The database stores sha256(key).

rawKey must be a high-entropy, randomly generated value — in practice always the output of GenerateAPIKey below (32 bytes from crypto/rand). SHA-256 is used, not a slow password KDF, because the only two production callers (cmd/cleat-worker/main.go) always pass a GenerateAPIKey() value: a 256-bit random token has no meaningful offline brute-force surface even hashed with a fast function, unlike a low-entropy user-chosen password. The hash exists so the plaintext key is never persisted and so lookups can use a DB equality index (ResolveTenantFromAPIKey does `WHERE key_hash = $1`); it is not a password-verification barrier. CodeQL go/weak-sensitive-data-hashing alert #12 flags this call; dismissed with that reasoning. If a caller is ever added that lets a tenant supply their own key text, that precondition breaks and this needs to move to bcrypt/scrypt/argon2 (golang.org/x/crypto is already a dependency) with a versioned-hash migration path for existing rows.

func (*TenantStore) CreateTenant

func (s *TenantStore) CreateTenant(ctx context.Context, name, displayName string) (uuid.UUID, error)

CreateTenant creates a new tenant. Returns the tenant ID.

func (*TenantStore) RevokeAPIKey

func (s *TenantStore) RevokeAPIKey(ctx context.Context, keyID uuid.UUID) error

RevokeAPIKey revokes an API key.

Jump to

Keyboard shortcuts

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