Documentation
¶
Overview ¶
Package tamper is the root of the Tamper enterprise auth/authz framework — an embeddable, Go-native auth layer extracted from Barista (Barista is its flagship + proving ground, mirroring Espresso <- Barista).
Tamper is consumed import-not-copy: an application depends on the subpackages below directly (Barista wraps them in thin façades under internal/auth, internal/audit, internal/authz, internal/idp, and internal/scimstore). Every subpackage is store-decoupled behind a port interface the app implements; the framework never names a table.
Shipped subpackages:
- crypto: portable auth primitives — JWT issue/verify, bcrypt password hashing, refresh-token hashing, TOTP enrollment/verify, and the KEK keyset + secretbox envelope that seals at-rest secrets (TOTP secrets, OIDC/SAML provider secrets). Lifted in Phase 0b.
- audit: tamper-evident hash-chain logging — per-row canonical-version dispatch, chain-segment anchors, boot-time chain verification — plus the audit/sqlitestore SQLite persistence layer. Lifted in Phase 0c.
- authz: the Authorizer PDP — Check + reverse queries — over two interchangeable engines: the downward-closed rank RBAC and the set-based PermissionSet (which subsumes ranks and expresses non-downward-closed roles), plus a converter that makes PermissionSet decide identically to RBAC by construction. Phase 1.
- identity: the credentials + session core — Register/Login/Refresh/ Logout, refresh-session rotation + revocation, TOTP enrollment, and multi-IdP account linking — behind one identity.Store port, with a caller-supplied ACR and first-user bootstrap signal. Phase 2.
- oidc: the OIDC relying-party substrate (discovery + JWKS rotation, PKCE, ID-token verification, group-claim normalization) and a store-backed provider Manager with a TTL-cached live registry and KEK-sealed client secrets. Phase 3.
- saml: the SAML service-provider substrate (crewjam/saml wrapping — metadata fetch/parse, AuthnRequest building incl. step-up, assertion helpers, signed state cookie) and the mirror-image provider Manager with a KEK-sealed SP signing key. Phase 3.
- scim: the SCIM 2.0 substrate — the filter engine (parse + AST->SQL over a caller-supplied column mapping), the RFC 7644 PATCH applier, and group-cycle detection over a port. Phase 3.
- espresso: the first-class transport adapter for the Espresso HTTP framework — mountable auth/OIDC/SAML/SCIM route surfaces plus the RequireAuth / RequireAuthWS / RequireServiceAccount / RequireDecision / RequireFreshAuth middleware and the Auditor mutation middleware. The core stays transport-agnostic; other adapters are possible later. Phase 4.
The vision, niche, extraction playbook, and phase roadmap live in TAMPER-DESIGN.md next to this file. A top-level composition facade (tamper.New(Config) + tamper/espresso.Routes) and a runnable example are the current milestone — see PHASE6-STANDALONE-PACKAGING-SKETCH.md.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func PermissionSet ¶
func PermissionSet(store authz.PermissionStore) (authz.Authorizer, error)
PermissionSet builds a set-based Authorizer over a PermissionStore, ready to drop into Config.Authz. The set engine decides by permission-key membership over the union of a subject's roles, so it expresses non-downward-closed roles the rank engine cannot; it subsumes RBAC (a downward-closed role is just a particular key set).
func RBAC ¶
func RBAC(store authz.BindingStore, h authz.Hierarchy, p authz.Policy) (authz.Authorizer, error)
RBAC builds a rank-based Authorizer over a BindingStore, ready to drop into Config.Authz. It is a thin one-liner over authz.NewRBAC so a greenfield consumer does not have to reach into the authz subpackage for the common case; applications that build their own PDP (a converter, an overlay, a custom store) pass that directly to Config.Authz instead.
The rank engine expresses downward-closed roles (a higher rank subsumes every lower one). For roles that are NOT downward-closed — arbitrary permission subsets like {view, manage} without deploy — use PermissionSet.
Types ¶
type AuditConfig ¶
type AuditConfig struct {
DBPath string
// EmailLookup optionally enriches service-direct emissions (an event
// carrying a user id but no email) at Log time. Its signature matches
// audit.SQLiteLoggerOptions.EmailLookup exactly. Optional.
EmailLookup func(ctx context.Context, userID string) (email string, ok bool)
}
AuditConfig configures the audit logger. An empty DBPath selects the NoopLogger; otherwise the SQLite hash-chain logger is opened at DBPath.
type Config ¶
type Config struct {
// JWT is required — New returns an error on an empty Secret (the
// underlying crypto.NewJWTService panics on empty, so New guards first).
JWT crypto.JWTConfig
// KEKs + WriteKeyID build the envelope keyset that seals at-rest
// secrets (identity TOTP envelopes, OIDC/SAML provider secrets). Empty
// KEKs leaves Provider.KeySet nil, mirroring crypto.NewKeySet's
// (nil, nil) contract — callers gate "sealing configured" on
// Provider.KeySet != nil exactly as before.
KEKs []crypto.KEKEntry
WriteKeyID uint8
// Audit configures the tamper-evident log. A non-empty DBPath opens the
// SQLite hash-chain logger; an empty DBPath yields a NoopLogger.
// Provider.Audit is always non-nil.
Audit AuditConfig
// Authz is the application's built policy-decision point. Optional —
// nil leaves Provider.Authz nil (the transport's RequireDecision gate is
// then unusable). Greenfield consumers can build one with the RBAC or
// PermissionSet helpers in this package.
Authz authz.Authorizer
// Identity, when non-nil, builds the credentials + session Core over the
// supplied Store, with JWT and (when configured) the KeySet auto-threaded
// in. Applications with a richer identity service of their own leave this
// nil and pass that service to the transport layer instead.
Identity *IdentityConfig
// OIDC / SAML, when non-nil, build the respective federation provider
// Manager over the supplied Store, sealing provider secrets with the
// KeySet. Nil leaves the corresponding Provider field nil.
OIDC *OIDCConfig
SAML *SAMLConfig
}
Config is the single boot input for New. It bundles the engine configuration; the application still supplies the leaves — the Store implementations, the built Authz PDP, and (at the transport layer) the route policy and the Espresso router.
The zero value is NOT valid: JWT.Secret is required. Everything else is optional and nil-encodes "not configured" exactly as the subpackage constructors already do (no KEKs => no KeySet; no DBPath => Noop audit; nil Identity/OIDC/SAML => that engine is absent from the Provider).
type IdentityConfig ¶
IdentityConfig configures the identity Core. Store is required (New returns an error when it is nil). Options are passed through to identity.New; the KeySet is auto-threaded ahead of them when configured, so an explicit identity.WithKeySet in Options still wins.
type OIDCConfig ¶
type OIDCConfig struct {
Store oidc.ProviderStore
RedirectURL func(id string) string
TTL time.Duration
}
OIDCConfig configures the OIDC provider Manager. Store is required. TTL sets the live-registry cache lifetime (0 = the Manager default). RedirectURL maps a provider id to its callback URL — the route shape is the application's, so this is a function, not a base string. Optional.
type Provider ¶
type Provider struct {
JWT *crypto.JWTService // always non-nil
KeySet *crypto.KeySet // nil when KEKs is empty
Audit audit.Logger // always non-nil (NoopLogger fallback)
Authz authz.Authorizer // nil unless Config.Authz is set
Identity *identity.Core // nil unless Config.Identity is set
OIDC *oidc.Manager // nil unless Config.OIDC is set
SAML *saml.Manager // nil unless Config.SAML is set
}
Provider is the constructed engine bag. Every field mirrors a subpackage constructor's output; a nil field means "not configured", encoded the same way the subpackages themselves do. The application reads these to wire its services and (via tamper/espresso.Routes) its HTTP surface.
The Provider owns the audit DB handle when audit is SQLite-backed — call Close on shutdown to release it.
func New ¶
New builds a Provider from cfg. It validates inputs and constructs each configured engine as a DAG rooted at the JWT service + KeySet, so a misconfiguration fails here at boot rather than as a per-request denial.
Cheap validation runs before any resource is allocated; the audit DB is opened only after every input has passed, and is closed again if a later step fails.
type SAMLConfig ¶
type SAMLConfig struct {
Store saml.ProviderStore
SPMetadataURL func(id, acsURL string) string
TTL time.Duration
AllowIDPInitiated bool
SkewTolerance time.Duration
}
SAMLConfig configures the SAML provider Manager. Store is required. SPMetadataURL maps (provider id, ACS URL) to the SP-metadata URL — again an application route shape, so a function. The remaining fields are flow knobs passed straight through to the Manager.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package audit defines the append-only event log primitives for the v0.6 compliance work (closes the v0.4/v0.5-deferred non-goal).
|
Package audit defines the append-only event log primitives for the v0.6 compliance work (closes the v0.4/v0.5-deferred non-goal). |
|
internal/sqlitestore
Package sqlitestore holds the SQLite-backed storage for the audit log (closes the v0.4/v0.5-deferred non-goal).
|
Package sqlitestore holds the SQLite-backed storage for the audit log (closes the v0.4/v0.5-deferred non-goal). |
|
Package authz defines Tamper's Authorizer PDP — the policy decision point every Tamper consumer codes against — plus a built-in RBAC engine (rbac.go) that evaluates scoped-role bindings supplied by a pluggable BindingStore.
|
Package authz defines Tamper's Authorizer PDP — the policy decision point every Tamper consumer codes against — plus a built-in RBAC engine (rbac.go) that evaluates scoped-role bindings supplied by a pluggable BindingStore. |
|
KeySet extends the v0.5 single-KEK SecretBox with key-id versioning so an operator can rotate the KEK without re-issuing every webhook secret.
|
KeySet extends the v0.5 single-KEK SecretBox with key-id versioning so an operator can rotate the KEK without re-issuing every webhook secret. |
|
Package espresso is the Tamper transport adapter for the Espresso HTTP framework (Phase 4).
|
Package espresso is the Tamper transport adapter for the Espresso HTTP framework (Phase 4). |
|
examples
|
|
|
discord
command
Command discord is a runnable, end-to-end example of "Sign in with Discord" — a provider with NO OpenID Connect layer — using tamper/oauth2social.
|
Command discord is a runnable, end-to-end example of "Sign in with Discord" — a provider with NO OpenID Connect layer — using tamper/oauth2social. |
|
federation
command
Command federation is a runnable, end-to-end example of OIDC single sign-on with the Tamper framework: build the engines with tamper.New (including an OIDC provider), aggregate the HTTP surface with tamper/espresso.Routes (including the Federation surface), and run the full authorization-code flow against an embedded fake IdP.
|
Command federation is a runnable, end-to-end example of OIDC single sign-on with the Tamper framework: build the engines with tamper.New (including an OIDC provider), aggregate the HTTP surface with tamper/espresso.Routes (including the Federation surface), and run the full authorization-code flow against an embedded fake IdP. |
|
multitenant
command
Command multitenant is the POOLED PROVING GROUND for Phase 7: one process, one store, two tenants, and a test that asserts a genuine cross-tenant denial.
|
Command multitenant is the POOLED PROVING GROUND for Phase 7: one process, one store, two tenants, and a test that asserts a genuine cross-tenant denial. |
|
quickstart
command
Command quickstart is a runnable, end-to-end example of embedding the Tamper framework: build the engines with tamper.New, aggregate the HTTP surface with tamper/espresso.Routes, register it on an Espresso router, and serve register / login / me / refresh / logout.
|
Command quickstart is a runnable, end-to-end example of embedding the Tamper framework: build the engines with tamper.New, aggregate the HTTP surface with tamper/espresso.Routes, register it on an Espresso router, and serve register / login / me / refresh / logout. |
|
scim
command
Command scim is a runnable, end-to-end example of exposing a SCIM 2.0 provisioning surface with the Tamper framework: build the engines with tamper.New, aggregate the surface with tamper/espresso.Routes (a SCIM bundle), implement the scim.UserStore + scim.GroupStore ports in-memory (store.go), and gate every route behind a service-account bearer token.
|
Command scim is a runnable, end-to-end example of exposing a SCIM 2.0 provisioning surface with the Tamper framework: build the engines with tamper.New, aggregate the surface with tamper/espresso.Routes (a SCIM bundle), implement the scim.UserStore + scim.GroupStore ports in-memory (store.go), and gate every route behind a service-account bearer token. |
|
Package identity is Tamper's identity core — users, credentials, and refresh-session lifecycle (Phase 2a of the extraction roadmap in ../TAMPER-DESIGN.md; TOTP enrollment and multi-IdP identity linking join in later sub-phases).
|
Package identity is Tamper's identity core — users, credentials, and refresh-session lifecycle (Phase 2a of the extraction roadmap in ../TAMPER-DESIGN.md; TOTP enrollment and multi-IdP identity linking join in later sub-phases). |
|
tenanttest
Package tenanttest is the cross-tenant leak conformance harness for identity.Store.
|
Package tenanttest is the cross-tenant leak conformance harness for identity.Store. |
|
Package oauth2social federates identity from providers that speak plain OAuth 2.0 and have no OpenID Connect layer — Discord being the case that forced it into existence.
|
Package oauth2social federates identity from providers that speak plain OAuth 2.0 and have no OpenID Connect layer — Discord being the case that forced it into existence. |
|
Package oidc implements an OpenID Connect Relying Party (RP) substrate for Tamper consumers.
|
Package oidc implements an OpenID Connect Relying Party (RP) substrate for Tamper consumers. |
|
Package saml implements a SAML 2.0 Service Provider (SP) substrate for Tamper consumers.
|
Package saml implements a SAML 2.0 Service Provider (SP) substrate for Tamper consumers. |
|
Package scim is the SCIM 2.0 protocol substrate for Tamper consumers — the transport-agnostic RFC 7643/7644 mechanics an app's HTTP layer composes:
|
Package scim is the SCIM 2.0 protocol substrate for Tamper consumers — the transport-agnostic RFC 7643/7644 mechanics an app's HTTP layer composes: |
|
Package tenant is the tenancy vocabulary for pooled multi-tenant deployments — the neutral Descriptor record, the Store and Resolver persistence ports, the ErrNotFound / ErrSuspended sentinels, and the context helpers that propagate the active tenant across a request.
|
Package tenant is the tenancy vocabulary for pooled multi-tenant deployments — the neutral Descriptor record, the Store and Resolver persistence ports, the ErrNotFound / ErrSuspended sentinels, and the context helpers that propagate the active tenant across a request. |