Documentation
¶
Overview ¶
Package auth implements the OpenVaultDB auth MVP: an OAuth-style connect flow (consent → one-time code → scoped bearer token), capability grants persisted with hashed tokens, and HTTP middleware enforcing them.
The capability model follows the draft taxonomy in the spec hub (openvaultdb/openvaultdb spec/api/auth.md): namespaced capability strings such as "records:read", optionally collection-scoped as "records:read:contacts" (the spec's open question answered pragmatically — scoping is per collection, evaluated at enforcement time). Tokens are opaque bearer strings; only their SHA-256 lands on disk. The full JWT claim structure, refresh tokens, OIDC and passkeys stay with the spec (pending its architecture/security review) — this MVP keeps the wire surface small enough to swap those in without breaking applications.
Index ¶
- Constants
- func BearerToken(r *http.Request) string
- func HashToken(token string) string
- func NewGrantID() (string, error)
- func NewToken() (string, error)
- type Capability
- type Config
- type Grant
- type Principal
- type Store
- func (s *Store) CreateGrant(g *Grant, token string) error
- func (s *Store) ExchangeCode(code, clientID string) (token string, g *Grant, err error)
- func (s *Store) ListGrants() []*Grant
- func (s *Store) Lookup(token string) *Grant
- func (s *Store) PutCode(code, clientID, redirectURI, databaseID string, caps []Capability)
- func (s *Store) RevokeGrant(id string) (*Grant, bool)
Constants ¶
const ( CapRecordsRead = "records:read" // GET/HEAD records, /query, /dtql CapRecordsWrite = "records:write" // PUT/POST/PATCH records, batch set/insert/update CapRecordsDelete = "records:delete" // DELETE records, batch delete CapCollectionsRead = "collections:read" CapSchemaRead = "schema:read" // inferred-schema endpoint CapDatabasesCreate = "databases:create" // POST /v1/databases (server-level) CapPoliciesDiscover = "policies:discover" CapPoliciesList = "policies:list" CapPoliciesRead = "policies:read" CapPoliciesAdmin = "policies:admin" CapAccessExplain = "access:explain" CapAccessSimulate = "access:simulate" CapAccessDiagnostics = "access:diagnostics" CapAccessInspectProtected = "access:inspect-protected" )
Actions an application capability can name (subset of the spec taxonomy that is meaningful against the MVP API surface).
const CodeTTL = 5 * time.Minute
CodeTTL is how long a one-time authorization code stays exchangeable.
const TokenTTL = time.Hour
TokenTTL is how long an issued application token stays valid. The spec drafts 15-minute app tokens with refresh; the MVP has no refresh tokens yet, so it uses the spec's API-key tier (1 hour) — apps re-run the connect flow after expiry.
Variables ¶
This section is empty.
Functions ¶
func BearerToken ¶
BearerToken extracts the bearer token from the Authorization header.
func NewGrantID ¶
NewGrantID generates a short random grant identifier (8 random hex bytes = 16 hex chars).
Types ¶
type Capability ¶
type Capability struct {
Action string `json:"action"`
Collection string `json:"collection,omitempty"`
}
Capability is one parsed grant entry: an action, optionally scoped to a single collection ("" = all collections of the granted database).
func ParseCapabilities ¶
func ParseCapabilities(csv string) ([]Capability, error)
ParseCapabilities parses a comma-separated capability list.
func ParseCapability ¶
func ParseCapability(s string) (Capability, error)
ParseCapability parses "records:read" or "records:read:contacts".
func (Capability) String ¶
func (c Capability) String() string
String renders the capability back to its wire form.
type Config ¶
type Config struct {
// OwnerToken grants administrative capabilities; database policies still apply.
OwnerToken string
// Store holds application grants and pending authorization codes.
Store *Store
}
Config is the server-side auth configuration. A nil *Config means auth is disabled (the local-dev default documented in the threat model).
func (*Config) Middleware ¶
Middleware implements the spec's Layer-1 token validation: it authenticates every request (401 on missing/invalid tokens except on public paths) and attaches the Principal for the handlers' Layer-2 capability enforcement.
type Grant ¶
type Grant struct {
// Subject and Actor are provisioned by owner administration. Membership is
// resolved per request, never accepted from the token consumer.
Subject *access.PrincipalRef `json:"subject,omitempty"`
Actor *access.PrincipalRef `json:"actor,omitempty"`
ID string `json:"id"` // short random identifier (8 hex bytes)
Label string `json:"label,omitempty"` // human display name
TokenHash string `json:"tokenHash"`
PrincipalType string `json:"principalType"` // "application" (MVP)
PrincipalID string `json:"principalId"` // client_id
DatabaseID string `json:"databaseId,omitempty"` // "" = server-level grant (e.g. databases:create)
Capabilities []Capability `json:"capabilities"`
IssuedAt time.Time `json:"issuedAt"`
ExpiresAt time.Time `json:"expiresAt,omitempty"` // zero = never expires
RevokedAt *time.Time `json:"revokedAt,omitempty"`
}
Grant is one issued application token: the token itself is NOT stored — only its SHA-256 hex — so the grants file never holds usable credentials.
func (*Grant) Expired ¶
Expired reports whether the grant has reached its expiry. A zero ExpiresAt never expires.
func (*Grant) ValidateIdentity ¶ added in v0.4.0
ValidateIdentity rejects partial or malformed delegation bindings. Legacy client-only grants remain application credentials and never imply a user.
type Principal ¶
type Principal struct {
Owner bool // owner token: administrative capability authority; data ACL still applies
Grant *Grant // application grant when Owner is false
}
Principal is the authenticated caller attached to a request context.
func FromRequest ¶
FromRequest returns the authenticated principal attached by Middleware, or nil when the request is unauthenticated (public path or auth disabled).
func (*Principal) Allows ¶
Allows reports whether the principal may perform action on the collection of the database. Owner allows everything. An empty collection means the action targets the database as a whole (listing collections, queries whose target collection cannot be determined) — that requires an UNSCOPED grant entry, so collection-scoped grants never leak beyond their collection.
Database matching is on the GRANT side only: a grant with an empty DatabaseID is SERVER-LEVEL and matches any requested database (including the empty requested id used for server-level checks such as databases:create). A grant with a concrete DatabaseID matches only that exact database — it never matches a server-level check, because the requested "" differs from its concrete id.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store holds issued grants (persisted as JSON with hashed tokens) and pending one-time codes (in-memory). Safe for concurrent use.
func OpenStore ¶
OpenStore loads (or initializes) the grants file. Truly expired grants (non-zero ExpiresAt in the past) are dropped on load; revoked grants are kept for the audit trail.
func (*Store) CreateGrant ¶
CreateGrant hashes token, sets IssuedAt, stores and persists the grant. The caller must populate g.DatabaseID, g.Capabilities, g.Label, g.ExpiresAt (zero = never expires) before calling. On success g.ID, g.TokenHash, g.IssuedAt, g.PrincipalType are filled in.
func (*Store) ExchangeCode ¶
ExchangeCode consumes a code (one-time use) and, when it is valid, unexpired and bound to the same client, mints and persists a grant. Returns the raw bearer token — the only time it ever exists outside the client.
func (*Store) ListGrants ¶
ListGrants returns all grants (including revoked ones) sorted by IssuedAt ascending. Never exposes token secrets.
func (*Store) Lookup ¶
Lookup resolves a bearer token to its grant, or nil when unknown, expired, or revoked.
func (*Store) PutCode ¶
func (s *Store) PutCode(code, clientID, redirectURI, databaseID string, caps []Capability)
PutCode registers a one-time authorization code.
func (*Store) RevokeGrant ¶
RevokeGrant sets RevokedAt on the grant with the given ID and persists. Returns the updated grant and true on success; nil and false when unknown. Revoking an already-revoked grant is idempotent (updates RevokedAt if not set).