Documentation
¶
Overview ¶
Package sigil provides the experimental embedded Sigil authentication foundation: immutable configuration, lifecycle and actor-authorized service contracts. Storage and authentication flows are not implemented yet. See docs/core-contracts.md for ownership, cancellation and trust boundaries.
Index ¶
- Constants
- Variables
- func Describe(c Config) (contract.Document, error)
- func Version() string
- type AbuseConfig
- type AbuseFlow
- type AbusePolicy
- type AbuseService
- type AdminService
- func (s AdminService) CompleteStepUp(ctx context.Context, method AuditMethod, ...) (IssuedSession, error)
- func (s AdminService) CreateUser(ctx context.Context, input CreateUser) (User, error)
- func (s AdminService) DeleteUser(ctx context.Context, userID string) error
- func (s AdminService) IssueSession(ctx context.Context, userID string, method AuditMethod) (IssuedSession, error)
- func (s AdminService) RevokeAllSessions(ctx context.Context, userID string) error
- func (s AdminService) VerifyAndIssueSession(ctx context.Context, userID string, method AuditMethod, ...) (IssuedSession, error)
- type AssuranceLevel
- type AssurancePolicy
- type AssuranceRequirement
- type AuditEvent
- type AuditFailure
- type AuditMethod
- type AuditQuery
- type AuditReason
- type AuditService
- type Auth
- func (a *Auth) Abuse() AbuseService
- func (a *Auth) Admin() AdminService
- func (a *Auth) Audit() AuditService
- func (a *Auth) Close(ctx context.Context) error
- func (a *Auth) Contract() contract.Document
- func (a *Auth) Credentials() CredentialsService
- func (a *Auth) Handler() http.Handler
- func (a *Auth) Passwords() PasswordsService
- func (a *Auth) PluginIDs() []string
- func (a *Auth) RequireSession(next http.Handler) http.Handler
- func (a *Auth) Retention() RetentionService
- func (a *Auth) SafeRedirect(target string) (string, error)
- func (a *Auth) Sessions() SessionsService
- func (a *Auth) SetSessionCookie(w http.ResponseWriter, issued IssuedSession) error
- func (a *Auth) Users() UsersService
- func (a *Auth) WithAssurance(ctx context.Context, op plugin.Operation, r AssuranceRequirement, ...) error
- func (a *Auth) WithRecentSession(ctx context.Context, operation plugin.Operation, ...) error
- type AuthenticationState
- type Clock
- type Code
- type Config
- type CookieConfig
- type CreateUser
- type Credentials
- type CredentialsService
- func (s CredentialsService) ChangePassword(ctx context.Context, ip netip.Addr, current, password Secret) error
- func (s CredentialsService) ConfirmEmailChange(ctx context.Context, ip netip.Addr, token Secret) error
- func (s CredentialsService) Register(ctx context.Context, ip netip.Addr, input Credentials) error
- func (s CredentialsService) RequestEmailChange(ctx context.Context, ip netip.Addr, target string) error
- func (s CredentialsService) RequestPasswordReset(ctx context.Context, ip netip.Addr, target string) error
- func (s CredentialsService) ResendVerification(ctx context.Context, ip netip.Addr, target string) error
- func (s CredentialsService) ResetPassword(ctx context.Context, ip netip.Addr, token, password Secret) error
- func (s CredentialsService) SignIn(ctx context.Context, ip netip.Addr, input Credentials) (IssuedSession, error)
- func (s CredentialsService) VerifyEmail(ctx context.Context, ip netip.Addr, token Secret) error
- type Error
- type IssuedSession
- type LocalMailSink
- type Mail
- type MailDelivery
- func (d *MailDelivery) Cleanup(ctx context.Context, limit int) (int64, error)
- func (d *MailDelivery) DispatchOne(ctx context.Context) (bool, error)
- func (d *MailDelivery) Enqueue(ctx context.Context, tx storage.Tx, id string, template MailTemplate, ...) error
- func (d *MailDelivery) Rotate(ctx context.Context, cursor string, limit int) (Rotation, error)
- type MailSender
- type MailTemplate
- type PasswordVerification
- type PasswordsService
- type ProtectionBinding
- type Protector
- type Pruned
- type RegistrationConfig
- type RequestContext
- type RetentionService
- type RetryError
- type Rotation
- type Rotator
- type Secret
- type SecretSource
- type Session
- type SessionConfig
- type SessionView
- type SessionsService
- func (s SessionsService) Authenticate(ctx context.Context, token Secret) (context.Context, error)
- func (s SessionsService) Authentication(ctx context.Context) (SessionView, error)
- func (s SessionsService) Current(ctx context.Context) (SessionView, error)
- func (s SessionsService) List(ctx context.Context, afterID string, limit int) ([]Session, error)
- func (s SessionsService) Renew(ctx context.Context) (SessionView, error)
- func (s SessionsService) RequireAssurance(ctx context.Context, r AssuranceRequirement) error
- func (s SessionsService) RequireRecent(ctx context.Context, maxAge time.Duration) error
- func (s SessionsService) RevokeAll(ctx context.Context) error
- func (s SessionsService) RevokeCurrent(ctx context.Context) error
- type StartupChecker
- type User
- type UsersService
Constants ¶
const ( // MaxPasswordBytes bounds input before UTF-8 scanning or copying. MaxPasswordBytes = 1024 // MinPasswordRunes applies to new passwords, not historical verification. MinPasswordRunes = 15 )
Variables ¶
var ( ErrRateLimited = &Error{Code: CodeRateLimited, Message: "too many attempts"} ErrOverloaded = &Error{Code: CodeOverloaded, Message: "authentication capacity exhausted"} ErrInvalidConfiguration = &Error{Code: CodeInvalidConfiguration, Message: "invalid configuration"} ErrUnauthenticated = &Error{Code: CodeUnauthenticated, Message: "authentication required"} ErrAssuranceRequired = &Error{Code: CodeAssuranceRequired, Message: "stronger authentication required"} ErrForbidden = &Error{Code: CodeForbidden, Message: "operation forbidden"} ErrRecentAuthenticationRequired = &Error{Code: CodeRecentAuthenticationRequired, Message: "recent authentication required"} ErrClosed = &Error{Code: CodeClosed, Message: "authentication service is closed"} ErrBadRequest = &Error{Code: CodeBadRequest, Message: "invalid request"} ErrNotFound = &Error{Code: CodeNotFound, Message: "endpoint not found"} ErrMethodNotAllowed = &Error{Code: CodeMethodNotAllowed, Message: "method not allowed"} ErrBodyTooLarge = &Error{Code: CodeBodyTooLarge, Message: "request body too large"} ErrUnsupportedMediaType = &Error{Code: CodeUnsupportedMediaType, Message: "unsupported content type"} )
var ErrDelivery = errors.New("sigil: email delivery failed")
var ErrProtection = errors.New("sigil: secret protection failed")
Functions ¶
func Describe ¶
Describe exports the API contract for a configuration without side effects. It compiles the plugin registry and resolves the browser cookie policy, and reads nothing else: no storage connection, secret load, startup check or mail. The same description is validated by New, so a configuration that starts also exports, and an export never requires production credentials.
func Version ¶
func Version() string
Version reports the release this build was compiled from, exactly as the Go toolchain recorded it and with the leading "v" the module version carries, so it is the Git tag rather than a transformation of it. It is empty when the toolchain had nothing to record.
Nothing in the repository states a release number. The tag is the only source of truth, which is why no file has to be remembered at release time and why nothing can disagree with the tag. The cost is that this is best-effort: it reports a published version to a consumer who resolved one, a pseudo-version with a "+dirty" suffix inside a working checkout, and nothing at all under `go run`, in a test binary, with -buildvcs=false, or when a replace directive reaches this module. Log it; never decide on it.
Compatibility for each surface is docs/versioning.md.
Types ¶
type AbuseConfig ¶
type AbuseConfig struct {
Store storage.AbuseStore
Policies map[AbuseFlow]AbusePolicy
MaxRequests, MaxExpensive int
}
AbuseConfig limits each Auth instance's active requests and expensive work. There are no waiting queues. Store defaults to Config.Store when supported; without a shared store, protected operations fail closed (including in development). All replicas must use identical secrets and policies.
type AbusePolicy ¶
type AbusePolicy struct{ IPAttempts, IdentifierAttempts int }
type AbuseService ¶
type AbuseService struct {
// contains filtered or unexported fields
}
func (AbuseService) Do ¶
func (s AbuseService) Do(ctx context.Context, flow AbuseFlow, ip netip.Addr, identifier string, expensive bool, work func(context.Context) error) error
Do reserves local capacity, consumes shared budgets, then runs work once. The callback must honor context cancellation; slots remain held until it returns. Use expensive=false with Passwords(), which acquires its own KDF slot. Use expensive=true for other expensive callbacks. A flow inside the HTTP request callback reuses its request slot, as do nested service flow budgets. Do not recursively nest expensive work.
type AdminService ¶
type AdminService struct {
// contains filtered or unexported fields
}
AdminService is a host-only capability for explicit administrative operations. Possession of Auth grants access; never expose this capability to untrusted plugins or dispatch it from browser routes. The host must authorize its admin caller. Request authentication is not an administrative authorization grant.
func (AdminService) CompleteStepUp ¶
func (s AdminService) CompleteStepUp(ctx context.Context, method AuditMethod, verify func(storage.Tx, storage.User, storage.Session) (AssuranceLevel, error)) (IssuedSession, error)
CompleteStepUp is a trusted host/plugin capability, never a browser DTO. verify must verify/consume fresh proof bound to this user, session and epoch inside tx and return the assurance actually proven. MultiFactor requires two independent factors (or a user-verified multi-factor authenticator); a method name or two proofs of the same factor is insufficient. Stale existing evidence cannot be counted. Proof consumption, old-bearer revocation and issuance commit together. All returned credentials must be discarded on error.
func (AdminService) CreateUser ¶
func (s AdminService) CreateUser(ctx context.Context, input CreateUser) (User, error)
CreateUser is the administrative import contract, not browser registration. It remains unavailable until transactional identity storage is implemented.
func (AdminService) DeleteUser ¶
func (s AdminService) DeleteUser(ctx context.Context, userID string) error
DeleteUser is the administrative deletion for operator-driven removal, such as a data-subject request. The host authorizes its caller; no session is involved. It performs the same transactional cascade, mail withdrawal and audit event as DeleteCurrent, attributed to the host. Like RevokeAllSessions, an unknown user is reported as unauthenticated.
func (AdminService) IssueSession ¶
func (s AdminService) IssueSession(ctx context.Context, userID string, method AuditMethod) (IssuedSession, error)
IssueSession is a host-only authentication capability. The host must have just authenticated this user and names how (MethodPassword, MethodMagicLink, OAuthMethod or a host identifier) so the audit event records the method. Never dispatch it from a user-ID browser input.
func (AdminService) RevokeAllSessions ¶
func (s AdminService) RevokeAllSessions(ctx context.Context, userID string) error
func (AdminService) VerifyAndIssueSession ¶
func (s AdminService) VerifyAndIssueSession(ctx context.Context, userID string, method AuditMethod, verify func(storage.Tx, storage.User) (AssuranceLevel, error)) (IssuedSession, error)
VerifyAndIssueSession is a trusted authentication plugin capability. userID only selects the account to lock; verify must authenticate that account using fresh proof and consume the proof in tx. The proof and session commit together. Never expose a callback that accepts browser claims as verified assurance.
type AssuranceLevel ¶
type AssuranceLevel int
AssuranceLevel describes verified evidence, not a claim about NIST compliance.
const ( SingleFactor AssuranceLevel = 1 MultiFactor AssuranceLevel = 2 )
type AssurancePolicy ¶
AssurancePolicy reads factor enrollment under the user's transaction lock. It must return a valid minimum on every call; errors fail closed. It must not perform network I/O or retain tx. Nil preserves single-factor deployments.
type AssuranceRequirement ¶
type AssuranceRequirement struct {
Level AssuranceLevel
MaxAge time.Duration
}
AssuranceRequirement is checked against authoritative state within a mutation. MaxAge zero imposes no age limit; positive ages are exclusive upper bounds.
type AuditEvent ¶
type AuditEvent = storage.AuditEvent
AuditEvent and AuditQuery are the storage records: identifiers, timestamps and closed vocabularies only. See docs/audit-events.md for the field contract.
type AuditFailure ¶
type AuditFailure struct {
Operation plugin.Operation
Reason AuditReason
Method AuditMethod
SubjectUserID string
}
AuditFailure describes a rejected attempt for RecordFailure. Method names the sign-in method when the attempt was a sign-in; SubjectUserID names the account the attempt targeted when the caller established it.
type AuditMethod ¶
type AuditMethod string
AuditMethod names how a user authenticated on sign-in events. Use the constants, OAuthMethod, or a host-defined identifier matching ^[a-z][a-z0-9_.-]{0,79}$. Events that are not sign-ins carry no method.
const ( MethodPassword AuditMethod = "password" MethodMagicLink AuditMethod = "magic_link" )
func OAuthMethod ¶
func OAuthMethod(providerID string) AuditMethod
OAuthMethod names a sign-in through the configured provider identifier, so events distinguish providers ("oauth.google", "oauth.github").
type AuditQuery ¶
type AuditQuery = storage.AuditQuery
type AuditReason ¶
type AuditReason string
AuditReason is the closed failure vocabulary. Reasons describe why a presented credential or proof was rejected; they never carry input values.
const ( ReasonInvalidCredentials AuditReason = "invalid_credentials" // password mismatch or malformed password ReasonUnknownIdentity AuditReason = "unknown_identity" // no account, or no password for it ReasonEmailUnverified AuditReason = "email_unverified" // a correct password for an unverified account ReasonInvalidProof AuditReason = "invalid_proof" // unknown, expired, consumed or mismatched proof or provider transaction )
type AuditService ¶
type AuditService struct {
// contains filtered or unexported fields
}
AuditService is a host-only capability over the durable trail. Possession of Auth grants it; never dispatch it from browser routes without the host's own administrative authorization. Events contain no credential material.
func (AuditService) Events ¶
func (s AuditService) Events(ctx context.Context, q AuditQuery) ([]AuditEvent, error)
Events lists events in sequence order after the query cursor. Pages are current views; use the final Sequence as the next AfterSequence.
func (AuditService) Prune ¶
Prune deletes at most limit events recorded before the cutoff and returns the count. The library never prunes on its own; the host schedules retention.
func (AuditService) RecordFailure ¶
func (s AuditService) RecordFailure(ctx context.Context, f AuditFailure) error
RecordFailure records a rejected attempt from trusted host or plugin code, attributing actor and client address from ctx. The event is durable in its own transaction; a storage failure is returned after being logged with the operation and reason only.
type Auth ¶
type Auth struct {
// contains filtered or unexported fields
}
Auth is safe for concurrent use. It owns its cancellation scope and admitted work, but never closes host-owned storage, loggers or secret providers.
func New ¶
New validates and freezes configuration without starting workers or changing schema. ctx is the instance lifetime, not an individual HTTP request context. Dependency implementations must honor cancellation and must not panic.
func (*Auth) Abuse ¶
func (a *Auth) Abuse() AbuseService
func (*Auth) Admin ¶
func (a *Auth) Admin() AdminService
func (*Auth) Audit ¶
func (a *Auth) Audit() AuditService
func (*Auth) Close ¶
Close refuses new work, cancels admitted work, and waits for it to finish. A deadline only bounds this wait; another Close can continue waiting. Closing is idempotent. Future durable workers must requeue or drain before returning.
func (*Auth) Contract ¶
Contract returns the description validated at startup for this instance's configured core and plugin endpoints. Treat the result as read-only.
func (*Auth) Credentials ¶
func (a *Auth) Credentials() CredentialsService
func (*Auth) Handler ¶
Handler serves the authentication API. Mount with http.StripPrefix. Session verification uses authoritative storage; GET requests never renew sessions.
func (*Auth) Passwords ¶
func (a *Auth) Passwords() PasswordsService
func (*Auth) RequireSession ¶
RequireSession checks shared service authorization. It does not apply the auth API's host/body/CSRF policies to host application routes; those are owned by the host. Session cookies are verified against authoritative storage.
func (*Auth) Retention ¶
func (a *Auth) Retention() RetentionService
func (*Auth) SafeRedirect ¶
SafeRedirect returns an absolute URL under BaseURL. Redirects deliberately permit only local absolute paths, never a caller-supplied external origin. Host and forwarding headers cannot affect the destination.
func (*Auth) Sessions ¶
func (a *Auth) Sessions() SessionsService
func (*Auth) SetSessionCookie ¶
func (a *Auth) SetSessionCookie(w http.ResponseWriter, issued IssuedSession) error
SetSessionCookie delivers a committed issuance result using the configured browser policy. Host authentication flows must not serialize Token themselves.
func (*Auth) Users ¶
func (a *Auth) Users() UsersService
func (*Auth) WithAssurance ¶
func (a *Auth) WithAssurance(ctx context.Context, op plugin.Operation, r AssuranceRequirement, fn func(storage.Tx, storage.User, storage.Session) error) error
WithAssurance runs a trusted mutation and its assurance check atomically.
func (*Auth) WithRecentSession ¶
func (a *Auth) WithRecentSession(ctx context.Context, operation plugin.Operation, fn func(storage.Tx, storage.User, storage.Session) error) error
WithRecentSession runs a trusted plugin mutation under the actor's owner lock. It revalidates the live session and five-minute authentication age in the same transaction as fn and plugin hooks. fn must not perform network I/O or retain the transaction. Possession of Auth is a trusted host/plugin capability.
type AuthenticationState ¶
type AuthenticationState string
AuthenticationState deliberately distinguishes a pending MFA flow from a usable session. Unknown values must fail closed just like Pending.
const ( Unauthenticated AuthenticationState = "unauthenticated" Pending AuthenticationState = "pending" Authenticated AuthenticationState = "authenticated" )
type Clock ¶
Clock implementations must be safe for concurrent use. Production defaults to the system clock. Tests may inject a clock without sleeping.
type Code ¶
type Code string
Code is a stable machine-readable error category. New codes are additive.
const ( CodeRateLimited Code = "rate_limited" CodeOverloaded Code = "overloaded" CodeInvalidConfiguration Code = "invalid_configuration" CodeUnauthenticated Code = "unauthenticated" CodeForbidden Code = "forbidden" CodeAssuranceRequired Code = "assurance_required" CodeRecentAuthenticationRequired Code = "recent_authentication_required" CodeClosed Code = "closed" CodeBadRequest Code = "bad_request" CodeNotFound Code = "not_found" CodeMethodNotAllowed Code = "method_not_allowed" CodeBodyTooLarge Code = "body_too_large" CodeUnsupportedMediaType Code = "unsupported_media_type" )
type Config ¶
type Config struct {
AssurancePolicy AssurancePolicy
SensitiveAssurance AssuranceLevel // zero defaults to SingleFactor
Plugins []plugin.Definition
Registration RegistrationConfig
Abuse AbuseConfig
BaseURL string
Secret SecretSource
TrustedOrigins []string
TrustedProxies []string
Cookie CookieConfig
Session SessionConfig
Development bool // Explicit HTTP loopback exception; never for deployment.
Clock Clock
Random io.Reader // Test override, permitted only in Development mode.
Logger *slog.Logger
Store storage.Store // Optional; session operations fail closed without storage.
Startup StartupChecker
Protector Protector
}
Config is copied and validated by New. Mutating its slices afterwards cannot alter an instance. Injected interfaces remain host-owned and must be safe for concurrent use. No request-specific state belongs in Config.
func (Config) MarshalJSON ¶
type CookieConfig ¶
CookieConfig is optional; zero values select Secure, HttpOnly, Lax, Path=/ and a host-only __Host-sigil_session cookie. Insecure flags aren't exposed.
type CreateUser ¶
type Credentials ¶
Credentials contains untrusted input. Passwords are never normalized.
type CredentialsService ¶
type CredentialsService struct {
// contains filtered or unexported fields
}
func (CredentialsService) ChangePassword ¶
func (s CredentialsService) ChangePassword(ctx context.Context, ip netip.Addr, current, password Secret) error
ChangePassword requires a verified, recently authenticated actor and the current password. All proof bindings are rechecked after hashing, under the user lock. Success invalidates every session (including the caller) and pending challenge.
func (CredentialsService) ConfirmEmailChange ¶
func (s CredentialsService) ConfirmEmailChange(ctx context.Context, ip netip.Addr, token Secret) error
ConfirmEmailChange proves control of the pending address. Changing and verifying the address, consuming the proof and invalidating all sessions/challenges commit together. Sign in again after success; this method never issues a session.
func (CredentialsService) Register ¶
func (s CredentialsService) Register(ctx context.Context, ip netip.Addr, input Credentials) error
Register returns the same success for a new and an existing identity, and never issues a session. User, password, challenge and mail commit together. ip must come from the host's trusted client-IP policy; HTTP derives it itself.
func (CredentialsService) RequestEmailChange ¶
func (s CredentialsService) RequestEmailChange(ctx context.Context, ip netip.Addr, target string) error
RequestEmailChange requires a verified account and authentication less than five minutes old, rechecked under the user lock. The old address remains authoritative until confirmation. Existing targets receive the same accepted result and no mail.
func (CredentialsService) RequestPasswordReset ¶
func (s CredentialsService) RequestPasswordReset(ctx context.Context, ip netip.Addr, target string) error
RequestPasswordReset accepts all canonical addresses uniformly. Only verified accounts with a password receive a durable, fifteen-minute recovery proof.
func (CredentialsService) ResendVerification ¶
func (s CredentialsService) ResendVerification(ctx context.Context, ip netip.Addr, target string) error
ResendVerification accepts existing, missing and already verified addresses uniformly. Only an unverified current identity receives a new proof; issuing a resend does not revoke older, still valid verification links.
func (CredentialsService) ResetPassword ¶
func (s CredentialsService) ResetPassword(ctx context.Context, ip netip.Addr, token, password Secret) error
ResetPassword consumes a recovery proof, replaces the password and invalidates all sessions and outstanding challenges in one transaction. It issues no session.
func (CredentialsService) SignIn ¶
func (s CredentialsService) SignIn(ctx context.Context, ip netip.Addr, input Credentials) (IssuedSession, error)
SignIn verifies outside the database transaction, then revalidates all proof bindings and issues under one user lock. Email verification is required; unverified accounts receive the same failure as invalid credentials.
func (CredentialsService) VerifyEmail ¶
VerifyEmail consumes the registration proof and verifies its bound address. It never issues or renews a session. The host must require explicit confirmation.
type Error ¶
type Error struct {
Code Code `json:"code"`
Message string `json:"message"`
Field string `json:"field,omitempty"`
}
Error contains only library-controlled diagnostics, never dependency errors, request values or credentials. Field identifies an invalid configuration field.
type IssuedSession ¶
type IssuedSession struct {
View SessionView `json:"view"`
Token Secret `json:"-"`
}
IssuedSession exposes the bearer only through Secret.Reveal. It must be sent to its intended client only after the issuing transaction has committed.
type LocalMailSink ¶
type LocalMailSink struct {
// contains filtered or unexported fields
}
LocalMailSink is a bounded, in-memory DEVELOPMENT sender. It has no HTTP route, disk writes or automatic logging. Drain explicitly transfers sensitive previews to host code and clears retained messages. Full sinks fail so work is retried.
func NewLocalMailSink ¶
func NewLocalMailSink(capacity int) (*LocalMailSink, error)
func (*LocalMailSink) Drain ¶
func (s *LocalMailSink) Drain() []Mail
type Mail ¶
Mail contains rendered, sensitive content. Reveal only inside the sender or an explicitly host-controlled local preview. ID is a provider idempotency key.
type MailDelivery ¶
type MailDelivery struct {
// contains filtered or unexported fields
}
func NewMailDelivery ¶
func NewMailDelivery(store storage.DeliveryStore, protector Protector, sender MailSender) (*MailDelivery, error)
func (*MailDelivery) DispatchOne ¶
func (d *MailDelivery) DispatchOne(ctx context.Context) (bool, error)
DispatchOne synchronously claims and attempts one message, with a 30-second send deadline (shorter than the lease). The host schedules calls and cleanup. A failed send is durably rescheduled and returns ErrDelivery, with no provider text. A crash after provider acceptance can cause duplicate delivery.
func (*MailDelivery) Enqueue ¶
func (d *MailDelivery) Enqueue(ctx context.Context, tx storage.Tx, id string, template MailTemplate, ttl time.Duration) error
Enqueue must be called inside Store.WithinTx. Use a fresh random operation ID and persist it with the business operation. A conflict aborts the transaction; an ambiguous commit must be reconciled, never retried with a new operation ID.
func (*MailDelivery) Rotate ¶
Rotate re-encrypts up to limit live payloads sealed under a key other than the protector's active key, scanning IDs after cursor. Run it from a trusted process after every replica holds the new keyring, repeating from "" until a full pass reports nothing rewrapped and nothing unreadable; only then retire the old key. Replacement is compare-and-swap, so a concurrent send or another pass is never overwritten. Plaintext exists only in this process's memory.
type MailSender ¶
MailSender must honor cancellation; success means provider acceptance, not inbox receipt. Providers should deduplicate Mail.ID whenever supported.
type MailTemplate ¶
MailTemplate renders before enqueue, freezing template content across retries. Hosts own URLs, escaping (use html/template), localization and sender address.
type PasswordVerification ¶
PasswordVerification contains no plaintext. Upgrade is nonempty only after a matching historical verifier, and must be persisted with a credential revision check under the user lock. This result alone never authorizes session issuance.
type PasswordsService ¶
type PasswordsService struct {
// contains filtered or unexported fields
}
PasswordsService is a host-only cryptographic primitive, not a sign-in flow. Network-facing callers must charge Abuse().Do with expensive=false first; these methods reserve the shared expensive slot themselves, with no queue. Do not invoke them inside an expensive=true callback.
func (PasswordsService) Hash ¶
Hash validates new-password policy and returns a randomly salted PHC verifier.
func (PasswordsService) Verify ¶
func (s PasswordsService) Verify(ctx context.Context, password Secret, verifier storage.Material) (PasswordVerification, error)
Verify accepts only bounded Argon2id v19 PHC records. Mismatch returns an empty result and nil error; malformed/unsupported records return ErrBadRequest. Matching older parameters are rehashed within the same admission slot.
type ProtectionBinding ¶
type ProtectionBinding struct{ Purpose, RecordID string }
ProtectionBinding must be authenticated along with the envelope. A ciphertext for one purpose or record must never decrypt under another binding.
type Protector ¶
type Protector interface {
Seal(context.Context, ProtectionBinding, Secret) ([]byte, error)
Open(context.Context, ProtectionBinding, []byte) (Secret, error)
}
Protector is the host-injectable recoverable-secret boundary. Implementations must use versioned authenticated encryption, key IDs and binding metadata; fail closed on tampering/unknown keys; and respect cancellation. AUTH-016 supplies the AES-256-GCM envelope; the Rotator extension and MailDelivery.Rotate (AUTH-031) re-encrypt live ciphertext under a new key. Ciphertext is internal persistence material, never a public response DTO.
func NewAESProtector ¶
NewAESProtector copies a keyring of raw 32-byte AES keys. Key IDs are public, immutable identifiers; keep old keys while their ciphertext remains live. This constructor starts no workers and never derives keys from passwords.
type RegistrationConfig ¶
type RegistrationConfig struct {
Delivery *MailDelivery
RenderVerification func(context.Context, string, Secret) (Mail, error)
// RenderEmailChange optionally enables pending address changes. It has the
// same rendering-only contract and must link to the change confirmation flow.
RenderEmailChange func(context.Context, string, Secret) (Mail, error)
// RenderPasswordReset optionally enables password recovery, using the same rendering-only contract.
RenderPasswordReset func(context.Context, string, Secret) (Mail, error)
}
RegistrationConfig enables registration with durable verification mail. RenderVerification must be concurrency-safe, honor cancellation, and only render content (no sending or other external side effects). The host owns safe confirmation URLs and escaping. Sigil sets the canonical recipient. Delivery's dispatcher must use the same store as Config.Store.
type RequestContext ¶
type RequestContext struct {
State AuthenticationState `json:"state"`
User User `json:"user"`
Session Session `json:"session"`
}
RequestContext is a credential-free snapshot of validated authentication. It is not an input capability: constructing one does not authorize a call.
func RequestFromContext ¶
func RequestFromContext(ctx context.Context) RequestContext
RequestFromContext returns a value copy. Missing authentication is explicit.
type RetentionService ¶
type RetentionService struct {
// contains filtered or unexported fields
}
RetentionService is the host-only capability over expired-row cleanup. The library never prunes on its own; the host schedules it alongside MailDelivery.Cleanup, Audit().Prune and the plugin pruners.
func (RetentionService) PruneExpired ¶
func (s RetentionService) PruneExpired(ctx context.Context, before time.Time, limit int) (Pruned, error)
PruneExpired deletes from each core table at most limit (1..1000) rows that expired at or before the cutoff (clamped to database time, so live rows are never removed) and returns the counts. Repeat while any count equals limit. A store without the extension is unavailable.
type RetryError ¶
RetryError exposes a bounded retry hint without identity or storage diagnostics.
func (*RetryError) Error ¶
func (e *RetryError) Error() string
func (*RetryError) Unwrap ¶
func (e *RetryError) Unwrap() error
type Rotation ¶
type Rotation struct {
Next string // cursor for the next call; empty once the scan reached the end
Visited int
Rewrapped int
Unreadable int // malformed envelopes or keys missing from the keyring; retire nothing while nonzero
}
Rotation reports one re-encryption pass over live outbox payloads.
type Rotator ¶
Rotator is an optional Protector extension for operational key rotation. ActiveKeyID names the key new ciphertext uses. KeyID names the key an existing envelope was sealed under without decrypting it, whether or not that key is still in the keyring, so rotation can find ciphertext that depends on a key scheduled for retirement; a malformed envelope is ErrProtection.
type Secret ¶
type Secret struct {
// contains filtered or unexported fields
}
Secret prevents accidental disclosure through formatting, JSON and slog. Reveal is an explicit trust boundary; callers must not log its result. Go cannot guarantee erasure of strings or protect against in-process code.
func (Secret) MarshalJSON ¶
type SecretSource ¶
SecretSource resolves deployment key material at startup, respecting ctx. It must return at least 32 random bytes (encoded text is allowed). Length validation cannot establish entropy. Sources are host-owned and not closed.
func SecretFromEnv ¶
func SecretFromEnv(name string) SecretSource
SecretFromEnv reads the environment at construction time, not on each request.
type Session ¶
type Session struct {
Assurance AssuranceLevel `json:"assurance"`
Method string `json:"method"`
ID string `json:"id"`
UserID string `json:"userId"`
State AuthenticationState `json:"state"`
CreatedAt time.Time `json:"createdAt"`
AuthenticatedAt time.Time `json:"authenticatedAt"`
ExpiresAt time.Time `json:"expiresAt"`
}
Session is a public view. ID is a non-bearer identifier; no raw token or digest may be assigned to it. AuthenticatedAt is the last completed authentication, not session renewal. Pending timestamps describe the first-factor proof only.
type SessionConfig ¶
type SessionView ¶
type SessionsService ¶
type SessionsService struct {
// contains filtered or unexported fields
}
func (SessionsService) Authenticate ¶
Authenticate validates an opaque bearer against authoritative storage without renewing it. The resulting context is instance-bound; service calls revalidate it so a saved context cannot bypass subsequent revocation.
func (SessionsService) Authentication ¶
func (s SessionsService) Authentication(ctx context.Context) (SessionView, error)
Authentication returns the live flow state, including pending. It grants no ordinary access. Factor routes may use it to render a challenge.
func (SessionsService) Current ¶
func (s SessionsService) Current(ctx context.Context) (SessionView, error)
func (SessionsService) List ¶
List returns at most 100 active, owned sessions in ID order. Use the final ID as afterID for the next page. The empty cursor starts a new listing.
func (SessionsService) Renew ¶
func (s SessionsService) Renew(ctx context.Context) (SessionView, error)
Renew extends idle expiry only at/after RenewAfter. It preserves the bearer, absolute deadline and authentication time, and never renews on read requests.
func (SessionsService) RequireAssurance ¶
func (s SessionsService) RequireAssurance(ctx context.Context, r AssuranceRequirement) error
RequireAssurance is a read-only check. Mutations must use WithAssurance to avoid revocation, enrollment and check/use races.
func (SessionsService) RequireRecent ¶
RequireRecent authenticates from storage, then requires age strictly below maxAge. Renewal never refreshes this evidence. Future sensitive mutations must apply this predicate inside their own transaction to avoid a check/use race.
func (SessionsService) RevokeAll ¶
func (s SessionsService) RevokeAll(ctx context.Context) error
RevokeAll invalidates all existing sessions and challenges through the user's security epoch. Issuance serialized after this operation belongs to the new epoch.
func (SessionsService) RevokeCurrent ¶
func (s SessionsService) RevokeCurrent(ctx context.Context) error
type StartupChecker ¶
StartupChecker checks connectivity/schema compatibility without writes or migrations. The host owns the dependency and its shutdown. AUTH-008/010 will connect this contract to the storage adapter.
type User ¶
type User struct {
ID string `json:"id"`
Email string `json:"email"`
EmailVerified bool `json:"emailVerified"`
CreatedAt time.Time `json:"createdAt"`
}
User is a public identity view, never a credential persistence record.
type UsersService ¶
type UsersService struct {
// contains filtered or unexported fields
}
UsersService and SessionsService enforce actor authorization for both HTTP and direct calls. They never accept caller-asserted actors or admin flags.
func (UsersService) DeleteCurrent ¶
func (s UsersService) DeleteCurrent(ctx context.Context) error
DeleteCurrent deletes the signed-in user's account. It requires an active session authenticated strictly less than five minutes ago, rechecked under the user lock in the deleting transaction. The user row and everything that cascades from it (password, linked identities, sessions including the caller's, pending proofs and cascading plugin rows) go in one transaction with the account.delete audit event; queued proof mail addressed to the account is withdrawn. Afterwards no supported method can authenticate the account, the address is free to register again, and the trail keeps the account's events until the host prunes them. Deletion is final.
Source Files
¶
- abuse.go
- assurance.go
- audit.go
- config.go
- contract.go
- credentials.go
- deletion.go
- delivery.go
- doc.go
- email.go
- errors.go
- http.go
- http_body.go
- http_cookie.go
- http_credentials.go
- http_csrf.go
- http_email.go
- http_password.go
- http_policy.go
- models.go
- password_recovery.go
- passwords.go
- plugins.go
- protector.go
- secrets.go
- services.go
- sessions.go
- sigil.go
- version.go
Directories
¶
| Path | Synopsis |
|---|---|
|
Package cli implements the operator command set behind cmd/sigil: contract export, migration status, apply and SQL export, deployment diagnostics and bounded expired-row cleanup.
|
Package cli implements the operator command set behind cmd/sigil: contract export, migration status, apply and SQL export, deployment diagnostics and bounded expired-row cleanup. |
|
cmd
|
|
|
sigil
command
Command sigil is the operator tool for this repository: contract export, migrations, diagnostics and cleanup over the core and the in-repo plugins.
|
Command sigil is the operator tool for this repository: contract export, migrations, diagnostics and cleanup over the core and the in-repo plugins. |
|
Package contract describes the HTTP API as data: endpoint metadata, JSON schemas, the safe error catalogue and a deterministic OpenAPI 3.1 export.
|
Package contract describes the HTTP API as data: endpoint metadata, JSON schemas, the safe error catalogue and a deterministic OpenAPI 3.1 export. |
|
examples
|
|
|
browser
command
Command browser runs the local password authentication journey.
|
Command browser runs the local password authentication journey. |
|
browser/testdb
command
Command testdb removes only a uniquely named browser, client or react test schema.
|
Command testdb removes only a uniquely named browser, client or react test schema. |
|
client
command
Command client hosts the framework-neutral TypeScript client against the real Go server: the shared HTTPS loopback host (core credentials, the magic-link plugin, the OAuth plugin bound to a built-in mock OIDC provider and a local mail preview) plus a page that talks to the API only through @sigil-auth/client.
|
Command client hosts the framework-neutral TypeScript client against the real Go server: the shared HTTPS loopback host (core credentials, the magic-link plugin, the OAuth plugin bound to a built-in mock OIDC provider and a local mail preview) plus a page that talks to the API only through @sigil-auth/client. |
|
internal/loopback
Package loopback is the HTTPS loopback host shared by the browser examples that drive the TypeScript packages against the real Go server: core credentials with verification and reset mail, the magic-link plugin, the OAuth plugin bound to a built-in mock OIDC provider, and a development mail preview, served under a throwaway certificate on a loopback address.
|
Package loopback is the HTTPS loopback host shared by the browser examples that drive the TypeScript packages against the real Go server: core credentials with verification and reset mail, the magic-link plugin, the OAuth plugin bound to a built-in mock OIDC provider, and a development mail preview, served under a throwaway certificate on a loopback address. |
|
nethttp
command
Command nethttp demonstrates embedding Sigil in an application-owned net/http server: validated configuration, the mounted authentication API, host routes protected by RequireSession, trusted host capabilities, abuse admission and graceful shutdown.
|
Command nethttp demonstrates embedding Sigil in an application-owned net/http server: validated configuration, the mounted authentication API, host routes protected by RequireSession, trusted host capabilities, abuse admission and graceful shutdown. |
|
oauth-smoke
command
Command oauth-smoke drives the link, sign-in and unlink journeys of the Google and GitHub adapters against the real providers from an operator's browser.
|
Command oauth-smoke drives the link, sign-in and unlink journeys of the Google and GitHub adapters against the real providers from an operator's browser. |
|
react
command
Command react is the Go-plus-React example: the shared HTTPS loopback host (core credentials, magic links, the OAuth plugin with a mock provider and a local mail preview) in front of a Node renderer that server-renders the React pages with @sigil-auth/react and forwards each request's session cookie to the API through a per-request server client.
|
Command react is the Go-plus-React example: the shared HTTPS loopback host (core credentials, magic links, the OAuth plugin with a mock provider and a local mail preview) in front of a Node renderer that server-renders the React pages with @sigil-auth/react and forwards each request's session cookie to the API through a per-request server client. |
|
Package plugin defines explicitly registered, compiled extensions.
|
Package plugin defines explicitly registered, compiled extensions. |
|
magiclink
Package magiclink provides optional, existing-account email sign-in using public plugin, storage, mail and session contracts.
|
Package magiclink provides optional, existing-account email sign-in using public plugin, storage, mail and session contracts. |
|
oauth
Package oauth implements optional authorization-code sign-in for already linked external identities.
|
Package oauth implements optional authorization-code sign-in for already linked external identities. |
|
oauth/github
Package github configures GitHub OAuth 2.0 sign-in for the oauth plugin.
|
Package github configures GitHub OAuth 2.0 sign-in for the oauth plugin. |
|
oauth/google
Package google configures Google OpenID Connect sign-in for the oauth plugin.
|
Package google configures Google OpenID Connect sign-in for the oauth plugin. |
|
oauth/internal/providertest
Package providertest is the PostgreSQL fixture shared by the provider adapter integration tests.
|
Package providertest is the PostgreSQL fixture shared by the provider adapter integration tests. |
|
organization
Package organization provides optional PostgreSQL tenant membership and session context.
|
Package organization provides optional PostgreSQL tenant membership and session context. |
|
passkey
Package passkey provides user-verified discoverable WebAuthn credentials.
|
Package passkey provides user-verified discoverable WebAuthn credentials. |
|
totp
Package totp implements optional password-plus-TOTP authentication on PostgreSQL.
|
Package totp implements optional password-plus-TOTP authentication on PostgreSQL. |
|
Package storage defines identity records and transactional adapter guarantees.
|
Package storage defines identity records and transactional adapter guarantees. |
|
conformance
Package conformance is the reusable storage gate described in docs/storage-conformance.md.
|
Package conformance is the reusable storage gate described in docs/storage-conformance.md. |
|
postgres
Package postgres provides explicit versioned core PostgreSQL migrations and the transactional storage.Store adapter over them.
|
Package postgres provides explicit versioned core PostgreSQL migrations and the transactional storage.Store adapter over them. |