Help us shape the future of Go! Take the 2026 Go Developer Survey

goauth

package module
v0.5.0 Latest Latest
Warning

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

Go to latest
Published: Oct 1, 2026 License: MIT Imports: 27 Imported by: 14

README

goauth

goauth is a PostgreSQL-first authentication Runtime for Go applications, with sessions, recovery, encrypted notification delivery, OIDC and RBAC. PostgreSQL stores canonical auth state; net/http and Fiber are optional HTTP adapters, and Redis is needed only for optional OIDC one-time state.

This checkout prepares unpublished v0.5.0 from the v0.4.1 tagged baseline. The release will go directly to v0.5.0, without alpha, beta or RC tags. Before tagging, integration acceptance will run in the goadmin administration flow composed with site, against the selected goauth source SHA. See the v0.5 migration for changed contracts.

Pre-v1, public-release preparation. A Git tag is not proof of public availability, an independent security audit, or production adoption. See the release plan for the remaining acceptance criteria and RELEASING.md for exact-tag verification. The frozen v0.1 line ends at v0.1.7; consumers crossing that schema boundary must explicitly reset isolated development or test auth state. Existing tags remain immutable.

Documentation

The public-preview specification defines release acceptance criteria. Engineering review notes record historical work and checks; they do not replace consumer guidance or acceptance evidence at the release SHA.

Supported API

The supported imports are intentionally small:

  • github.com/assurrussa/goauth
  • github.com/assurrussa/goauth/postgres
  • github.com/assurrussa/goauth/redis
  • github.com/assurrussa/goauth/fiber
  • github.com/assurrussa/goauth/nethttp
  • github.com/assurrussa/goauth/oidc
  • github.com/assurrussa/goauth/oidc/provider
  • github.com/assurrussa/goauth/oidc/verifier
  • github.com/assurrussa/goauth/rbac
  • github.com/assurrussa/goauth/testkit

reference/externalconsumer.SupportedPackages is the machine-readable source of truth. Earlier v0.1 releases remain available at their immutable tags; the retired implementation is absent from this repository.

HTTP adapter capabilities

Capability net/http Fiber
Register, login, refresh Built in Built in
Password reset and email verification Built in Built in
Logout, logout-all, password change, email change Built in Host handlers call the root Runtime
Realm middleware with current-session checks Default Default
Offline JWT verification Explicit opt-in Explicit opt-in
Request body cap 1 MiB 1 MiB in auth handlers

Fiber hosts should also configure fiber.Config.BodyLimit to bound request buffering before handlers run. Each adapter returns HTTP 501 with notification_delivery_disabled for ErrNotificationDeliveryDisabled; hosts may choose to omit recovery/email routes in disabled delivery mode. See OIDC provider policy for client authentication, public-client S256 PKCE, confidential-client policy and persistent secret verification.

Runnable browser and JSON API example

examples/nethttp runs one PostgreSQL-backed Runtime with standard net/http, browser cookies, coordinated refresh, a bearer-only JSON API, and a local SMTP catcher. Its host owns CSRF, cookies, UI and worker lifetime. The nethttp middleware checks current sessions by default; OfflineJWT is an explicit opt-out. AuthContext(request.Context()) retrieves the checked identity. The profile contracts and required evidence separate browser cookies from explicit native/API credentials. The candidate gate includes a real HTTPS browser and backup/restore; see release verification for the required installed browser selection.

Passwords are bounded before hashing/verification, and each Runtime admits four concurrent hash operations by default (MaxConcurrentPasswordHashes is configurable). Saturation returns ErrPasswordHashOverloaded with 503/Retry-After in HTTP adapters. Returned token lifetimes never exceed their canonical absolute session expiry. OIDC verifier defaults to the embedded access profile; selecting zitadel-jwt is explicit and does not enable acceptance of ID tokens.

PostgreSQL and Fiber quickstart

The host supplies a PostgreSQL DSN, three distinct 32-byte versioned key rings, and a goauth.NotificationSender implementation backed by its email or other delivery provider. This wiring mounts a minimal user-realm API:

import (
    "context"
    "net/url"

    "github.com/assurrussa/goauth"
    goauthfiber "github.com/assurrussa/goauth/fiber"
    "github.com/assurrussa/goauth/postgres"
    "github.com/gofiber/fiber/v3"
)

func wireAuth(
    dsn string,
    signingKeys, tokenKeys, notificationKeys goauth.KeyRing,
    sender goauth.NotificationSender,
) (*fiber.App, *postgres.Runtime, error) {
    auth, err := postgres.NewRuntime(postgres.Config{
        DSN:                dsn,
        AutoMigrate:        true, // use the host's migration lifecycle in production
        NotificationSender: sender,
        Runtime: goauth.Config{
            Signing: goauth.SigningConfig{
                Issuer: "https://auth.example.com", Audience: "example-app", Keys: signingKeys,
            },
            TokenHMACKeys:  tokenKeys,
            OutboxAEADKeys: notificationKeys,
            URLBuilder: goauth.URLBuilderFunc(func(_ context.Context, token string) (string, error) {
                return "https://app.example.com/reset?token=" + url.QueryEscape(token), nil
            }),
        },
    })
    if err != nil {
        return nil, nil, err
    }
    adapter, err := goauthfiber.New(auth)
    if err != nil {
        _ = auth.Close()
        return nil, nil, err
    }
    app := fiber.New()
    app.Post("/auth/register", adapter.Register)
    app.Post("/auth/login", adapter.Login)
    app.Post("/auth/refresh", adapter.Refresh)
    app.Post("/auth/password-reset", adapter.RequestPasswordReset)
    app.Post("/auth/email-challenge", adapter.RequireRealm(goauth.RealmUser,
        goauthfiber.RealmMiddlewareOptions{AllowConfirmation: true}),
        adapter.SendEmailChallenge)
    app.Post("/auth/email-challenge/verify", adapter.RequireRealm(goauth.RealmUser,
        goauthfiber.RealmMiddlewareOptions{AllowConfirmation: true}),
        adapter.VerifyEmailChallenge)
    app.Get("/me", adapter.RequireRealm(goauth.RealmUser,
        goauthfiber.RealmMiddlewareOptions{}), func(c fiber.Ctx) error {
        authContext, _ := goauthfiber.AuthContext(c)
        return c.JSON(fiber.Map{"subjectId": authContext.SubjectID.String()})
    })
    return app, auth, nil
}

To sign in, send POST /auth/login with JSON such as {"scheme":"email","identifier":"user@example.com","password":"...","realm":"user"}. The response contains account and tokens (accessToken, refreshToken, expiry, realm, and scope). Send Authorization: Bearer <accessToken> to /me; the middleware checks the realm and, by default, the current server-side session and subject status. Send POST /auth/refresh with {"refreshToken":"..."} to rotate the refresh token. Registration returns a confirmation-scoped session; use that access token to request an email code, then verify it with {"code":"..."}. After verification, log in again to obtain a full-scope session. Runtime.Logout and LogoutAll revoke sessions; the host must expose its chosen logout routes and token storage policy.

NotificationSender.SendNotification(ctx, delivery) receives the typed notification, a stable delivery ID, and its validity deadline. Return success only after the host delivery provider accepts responsibility; it does not mean that the recipient received the message. Retries may repeat an ID, and expiry or the attempt limit can end processing without a successful send. The sender must honor context cancellation and set network timeouts so shutdown can join the worker. NotificationWorkerConfig.OnBlocked reports a delivery ID and safe reason when decryption fails; alert on this signal without logging payloads. Run auth.RunNotifications(ctx) in a supervised worker, cancel and join it on shutdown, schedule auth.Cleanup(ctx, postgres.CleanupPolicy{}), and close the Runtime when the process stops. The host chooses how to serve the Fiber app. The queue checks one-time state before sending; an account change during an in-flight external send can still reach the provider. Token validity is always enforced by the Runtime when the recipient acts on the message. Use goauth.NewKeyRing with separate random material for signing, token HMAC, and notification encryption; retain prior key versions through rotation. Managed delivery cannot be combined with custom event, renderer, audit or transaction hooks. PostgreSQL assembles AuthTransaction for all participating auth writes, audit records and native encrypted events. Advanced direct root assembly requires an AuthTransaction and transactional AuditSink; custom stores and event sinks must join the supplied context, including reads. The old NotificationTransaction configuration is rejected instead of silently running without the full transaction contract. See migration details. Direct RuntimeStore implementations must guard reset issuance against the expected primary email, security version, and active status and invalidate outstanding reset records on password, email, or status changes. They must also reject email-challenge issuance when the expected primary email or security version no longer matches, and invalidate old challenges when the primary email changes.

Runtime contract

postgres.NewRuntime assembles the canonical store, Argon2id password hashing, realm sessions, refresh families, password reset, email confirmation, identity links, trusted bootstrap, password/profile/email lifecycle, logout, audit persistence, cleanup, atomic rate limiting, and a digest-only OIDC refresh store. The native PostgreSQL notification queue is selected by providing a goauth.NotificationSender; it stores encrypted deliveries and exposes a worker and cleanup operation. Optional OIDC and RBAC are assembled through Runtime methods. The host supplies:

  • a PostgreSQL connection or DSN;
  • distinct versioned keys for JWT signing, token HMAC, and outbox AES-256-GCM;
  • a notification sender and a supervised worker lifecycle;
  • a password-reset URL builder;
  • optional membership, claims, and identifier hooks.

Custom encrypted event sinks, rendering and transactional audit wiring require direct root Runtime assembly. postgres.NewRuntime rejects custom EventSink, AuditSink and AuthTransaction overrides before database side effects. All participants in direct root assembly must enlist in the same auth transaction; receiving its context alone does not guarantee that an external sink will roll back with the canonical writes.

The HTTP adapters own only JSON handlers, typed error mapping, and realm middleware. Hosts continue to own route prefixes, cookies, redirects, UI, projection tables, and application permission catalogs. auth_subjects is the canonical principal; host users and admin rows are projections or memberships keyed by its subject_id. Realm plus host membership is the admin boundary. RBAC role assignments use that same canonical ID; hosts extend the permission catalog and own their presentation DTOs.

See docs/v0.2-runtime.md for the underlying model, docs/compatibility.md for the verified consumer line, and AUTH_INVARIANTS.md for security invariants. See LICENSE and SECURITY.md for reuse and private security reporting.

Verification

Use the Go version/toolchain in go.mod and the tool versions in .github/workflows/ci.yml; see CONTRIBUTING.md.

make prepare
make check
make integration-local
make vulnerability-check

For the unpublished v0.5 candidate, select the installed browser tools as described in release verification, then start disposable integration services:

make integration-up
make release-candidate-readiness
make integration-down

After a new public tag exists, check out that tag and run make public-module-check VERSION=<tag>. For candidate and public gates together, use make release-readiness VERSION=<tag> with integration services running. An explicit version is required; there is no stale default tag.

The published probe runs the Runtime/nethttp/Fiber/OIDC/Redis/RBAC wiring example in a fresh consumer environment through the public Go proxy and checksum service, without credentials, reused caches, a workspace, or a local replace. It checks the exact selected version after the test as well as before it. Database-backed acceptance runs through the separate local PostgreSQL consumer. Candidate checks do not establish publication; the public probe does not establish a security audit or production deployment.

Delivery-free operator administration

Config.NotificationDelivery = NotificationDeliveryDisabled explicitly disables password-reset issuance/consumption, email challenge issuance/verification, and email-change commands. Commands return ErrNotificationDeliveryDisabled before attempt bookkeeping or storage writes. Password login, profile updates, and password changes remain available; password changes still commit mandatory audit and security-state revocation. No sender, URL builder, or outbox AEAD keys are required in this mode. The zero policy retains required encrypted delivery.

PostgreSQL hosts can use Runtime.InAuthTransaction and Runtime.SQLExecutor(ctx) to commit host projections with canonical writes, audit, and RBAC. The executor exposes only SQL execution/query methods, never transaction ownership. Participating adapters must share Runtime.Database() exactly; a separate handle is rejected. RBAC authorization reads join the transaction and bypass caches. Snapshot needs a standalone repeatable-read transaction; invoking it in a managed auth transaction returns rbac.ErrSnapshotTransactionUnsupported. Supplied caches must implement rbac.CacheInvalidator; invalidation follows successful outer commit, never rollback. RBAC writes reserve cache bypass before SQL mutation, including standalone transactions. Managed operations use savepoints: an operation error restores its changes even if the host handles it and commits other writes. RBAC mutations on the same managed connection are serialized. Failure to restore a savepoint aborts the outer transaction. Rollback releases only its own reservations. While a write is pending, other authorization checks read committed PostgreSQL state. Each transaction/cache pair shares one guard and one invalidation regardless of write count. Each invalidation, including waiting for pending cache fills, has a two-second deadline detached from the request cancellation. Invalidators must honor that deadline and return after clearing the cache. Timeout leaves committed writes successful and the cache bypassed; an older completion cannot clear a newer failure. Failed cache invalidation bypasses the cache and reads authoritative PostgreSQL permissions; successful invalidation restores normal cached reads. An uncertain RBAC write commit returns ErrOperationOutcomeUnknown and keeps this cache wrapper bypassed for its lifetime. A read-only snapshot commit error keeps its classification without changing cache trust. Database read failures still deny authorization.

Repeated NewRBAC or Runtime.RBAC calls with the same live, comparable cache value and exact *sql.DB share the cache guard, invalidation lock and outcome state. Keep these services alive to retain cache acceleration. The registry uses weak references and does not own the database or cache. Mixing different cache values, adding an uncached service, repeating a noncomparable value cache, or reassembling after its wrapper is collected permanently switches cached reads to PostgreSQL for that DB handle. Invalidation still follows management commits. This conservative fallback cannot be cleared by later assembly or invalidation. Cache backends must belong to one canonical database; distinct DB handles and external SQL writers are outside this coordination contract.

Managed NotificationDelivery.EncryptedEvent contains the original sealed envelope for hosts that forward delivery into durable transports. The native queue remains the initiating auth transaction owner; downstream transports persist ciphertext only and deduplicate by delivery ID. Notification plaintext is ephemeral; never persist its reset URLs or codes. Acceptance is at least once, so SMTP or downstream transport acceptance may repeat after an uncertain acknowledgement. The worker renews its owned unexpired lease while reserving the attempt before sending; reservation and send share a bounded deadline. Expiry of that delivery deadline expires, retries or exhausts only the delivery; it does not stop the worker pool. After an ambiguous reservation timeout, the worker reads the persisted attempt count under the same lease token before settling the delivery. Parent cancellation stops the worker. Senders must honor cancellation and should deduplicate by delivery ID.

Privileged hosts can use Runtime.RequestPasswordResetWithReceipt to bind a PasswordResetReceipt (subject ID and public selector only) within the same transaction as the reset record, encrypted enqueue and mandatory audit. A callback failure rolls back those writes; no callback runs when issuance is suppressed. Use the PostgreSQL runtime's context SQL executor in the callback. Keep receipt presence private from unauthenticated clients. On ErrOperationOutcomeUnknown, preserve the host claim and reconcile durable state before attempting issuance again; never clear a cooldown and resend blindly.

PostgreSQL trusted bootstrap flows can call PrepareCredential before the host transaction. Its opaque, runtime-bound proof expires after five minutes. Inside InAuthTransaction, call RevalidateCredential before granting membership: it holds the canonical subject row lock through outer commit and checks active status, security version and normalized primary email. It creates no session and does not bypass durable rate admission. Fresh trusted provisioning can remain inside the transaction.

Documentation

Index

Constants

View Source
const (
	DefaultPasswordMinLength           = 8
	DefaultPasswordMaxLength           = 128
	DefaultArgon2MemoryKiB             = 19 * 1024
	DefaultArgon2Iterations            = 2
	DefaultArgon2Parallelism           = 1
	DefaultMaxConcurrentPasswordHashes = 4
)
View Source
const (
	MaxAccessTokenTTL = 5 * time.Minute
)

Variables

View Source
var (
	ErrOperationOutcomeUnknown   = errors.New("operation commit outcome is unknown")
	ErrInvalidSubjectID          = errors.New("invalid subject ID")
	ErrInvalidSubjectStatus      = errors.New("invalid subject status")
	ErrInvalidIdentifierScheme   = errors.New("invalid identifier scheme")
	ErrInvalidIdentifier         = errors.New("invalid identifier")
	ErrIdentifierAlreadyExists   = errors.New("identifier already exists")
	ErrInvalidRealm              = errors.New("invalid realm")
	ErrRealmNotRegistered        = errors.New("realm is not registered")
	ErrInvalidChallengePurpose   = errors.New("invalid email challenge purpose")
	ErrAccountNotFound           = errors.New("account not found")
	ErrAccountUnavailable        = errors.New("account unavailable")
	ErrInvalidCredentials        = errors.New("invalid credentials")
	ErrCurrentPasswordInvalid    = errors.New("current password is invalid")
	ErrPasswordUnchanged         = errors.New("new password matches current password")
	ErrPasswordChangeConflict    = errors.New("password changed concurrently")
	ErrAuthenticationRateLimited = errors.New("authentication rate limited")
	ErrPasswordHashOverloaded    = errors.New("password hashing capacity exhausted")
	ErrMembershipDenied          = errors.New("realm membership denied")
	ErrEmailVerificationRequired = errors.New("verified email required")
	ErrInvalidPassword           = errors.New("invalid password")
	ErrCommonPassword            = errors.New("common password is not allowed")
	ErrInvalidToken              = errors.New("invalid token")
	ErrReservedAccessTokenClaim  = errors.New("claims enricher set a reserved access-token claim")
	ErrExpiredToken              = errors.New("token expired")
	ErrRefreshReplay             = errors.New("refresh token replay detected")
	ErrSessionRevoked            = errors.New("session revoked")
	ErrSecurityVersionMismatch   = errors.New("security version mismatch")
	ErrResetAlreadyUsed          = errors.New("password reset token already used")
	ErrInvalidConfirmationCode   = errors.New("invalid confirmation code")
	ErrConfirmationExpired       = errors.New("confirmation code expired")
	ErrConfirmationAttempts      = errors.New("confirmation attempts exhausted")
	ErrConfirmationRateLimited   = errors.New("confirmation rate limited")
	ErrConfirmationResendDelay   = errors.New("wait before resending confirmation")
	ErrEmailChangeNotFound       = errors.New("pending email change not found")
	ErrEmailChangeSameValue      = errors.New("new email matches current email")
	ErrKeyRingInvalid            = errors.New("invalid key ring")
	ErrKeyPurposeCollision       = errors.New("cryptographic key reused across purposes")
	ErrNotificationSinkRequired  = errors.New("encrypted notification sink is required")
	ErrURLBuilderRequired        = errors.New("URL builder is required")
	ErrIdentityLinkNotFound      = errors.New("identity link not found")
	ErrIdentityLinkConflict      = errors.New("identity link conflict")
	ErrExplicitIdentityLink      = errors.New("explicit authenticated identity linking is required")
)
View Source
var ErrNotificationDeliveryDisabled = errors.New("notification delivery is disabled")
View Source
var ErrPasswordVerificationUnavailable = errors.New("password verification unavailable")

ErrPasswordVerificationUnavailable marks an operational failure from a custom PasswordHasher. Wrap or join the underlying cause to preserve diagnostics.

View Source
var ErrRateLimitTransactionUnsupported = errors.New("rate-limit admission requires no active auth transaction")

ErrRateLimitTransactionUnsupported means attempt admission was invoked inside a managed auth transaction. Call the rate-limited Runtime operation before opening an outer AuthTransaction; do not strip the callback context to retry. The built-in stores reject this composition before consuming an attempt or verifying a password. This is a wiring error, not a throttling or credential failure. Transactional email-issue quotas are not affected.

View Source
var NilSubjectID = SubjectID(uuid.Nil)

Functions

func NormalizeEmail

func NormalizeEmail(value string) (string, error)

Types

type Account

type Account struct {
	Subject      Subject
	PrimaryEmail Identifier
	Profile      BasicProfile
}

func (Account) EmailVerified

func (a Account) EmailVerified() bool

func (Account) IsZero

func (a Account) IsZero() bool

type AccountStore

type AccountStore interface {
	// LockAccount requires AuthTransaction and locks the canonical subject before dependent state.
	LockAccount(ctx context.Context, subjectID SubjectID) (Account, error)
	CreateLocalAccount(ctx context.Context, record LocalAccountRecord) (Account, error)
	FindAccount(ctx context.Context, identifier IdentifierInput) (Account, error)
	FindLocalAccount(ctx context.Context, identifier IdentifierInput) (LocalAccountRecord, error)
	GetLocalAccount(ctx context.Context, subjectID SubjectID) (LocalAccountRecord, error)
	GetAccount(ctx context.Context, subjectID SubjectID) (Account, error)
	UpdateBasicProfile(ctx context.Context, subjectID SubjectID, profile BasicProfile, now time.Time) (Account, error)
	ChangePassword(ctx context.Context, request PasswordChangeStoreRequest) (PasswordChangeStoreResult, error)
}

type Argon2idConfig

type Argon2idConfig struct {
	MemoryKiB   uint32
	Iterations  uint32
	Parallelism uint8
	SaltLength  uint32
	KeyLength   uint32
	Random      io.Reader
}

type Argon2idHasher

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

func NewArgon2idHasher

func NewArgon2idHasher(config Argon2idConfig) (*Argon2idHasher, error)

func (*Argon2idHasher) HashPassword

func (h *Argon2idHasher) HashPassword(password string) (string, error)

func (*Argon2idHasher) VerifyPassword

func (h *Argon2idHasher) VerifyPassword(phc, password string) error

type AuditSink

type AuditSink interface {
	RecordSecurityEvent(ctx context.Context, event SecurityEvent) error
}

type AuditSinkFunc

type AuditSinkFunc func(context.Context, SecurityEvent) error

func (AuditSinkFunc) RecordSecurityEvent

func (f AuditSinkFunc) RecordSecurityEvent(ctx context.Context, event SecurityEvent) error

type AuthContext

type AuthContext struct {
	SubjectID       SubjectID
	Realm           Realm
	Scope           SessionScope
	SessionID       string
	SecurityVersion int64
	Claims          map[string]any
}

type AuthTransaction added in v0.5.0

type AuthTransaction interface {
	InAuthTransaction(ctx context.Context, fn func(context.Context) error) error
}

AuthTransaction atomically commits all participating auth, audit and encrypted event writes. Callback errors roll back. A failed commit with uncertain server outcome must wrap ErrOperationOutcomeUnknown. Nested auth writes join the same scope. Credential-attempt admission is separate: built-in stores reject TakeRateLimit in this scope with ErrRateLimitTransactionUnsupported. Call rate-limited Runtime operations outside an outer transaction. Hooks must not perform irreversible external actions inside this callback.

type BasicProfile

type BasicProfile struct {
	Username    string
	DisplayName string
	GivenName   string
	FamilyName  string
}

type ChangePasswordRequest

type ChangePasswordRequest struct {
	SubjectID       SubjectID
	CurrentPassword string
	NewPassword     string
}

type ClaimsEnricher

type ClaimsEnricher interface {
	EnrichClaims(ctx context.Context, realm Realm, account Account, claims map[string]any) error
}

type ClaimsEnricherFunc

type ClaimsEnricherFunc func(context.Context, Realm, Account, map[string]any) error

func (ClaimsEnricherFunc) EnrichClaims

func (f ClaimsEnricherFunc) EnrichClaims(ctx context.Context, realm Realm, account Account, claims map[string]any) error

type CommonPasswordChecker

type CommonPasswordChecker interface {
	IsCommonPassword(password string) bool
}

type CommonPasswordCheckerFunc

type CommonPasswordCheckerFunc func(string) bool

func (CommonPasswordCheckerFunc) IsCommonPassword

func (f CommonPasswordCheckerFunc) IsCommonPassword(password string) bool

type Config

type Config struct {
	NotificationDelivery    NotificationDeliveryPolicy
	Store                   RuntimeStore
	Signing                 SigningConfig
	TokenHMACKeys           KeyRing
	OutboxAEADKeys          KeyRing
	EventSink               EncryptedEventSink
	NotificationTransaction NotificationTransaction // Deprecated: use AuthTransaction.
	AuthTransaction         AuthTransaction
	// Nil allows all trusted issuers; empty disables verified-email auto-linking.
	AutoLinkVerifiedEmailIssuers []string
	// ManagedNotificationDelivery enables typed, lease-owned PostgreSQL delivery.
	// A custom transaction alone keeps the configured renderer and event sink.
	ManagedNotificationDelivery bool
	IdentifierResolvers         map[IdentifierScheme]IdentifierResolver
	MembershipGate              MembershipGate
	ClaimsEnricher              ClaimsEnricher
	NotificationRenderer        NotificationRenderer
	URLBuilder                  URLBuilder
	AuditSink                   AuditSink
	PasswordHasher              PasswordHasher
	// Zero defaults to four concurrent hashes/verifications per shared Runtime.
	// Excess work fails immediately with ErrPasswordHashOverloaded.
	MaxConcurrentPasswordHashes int
	PasswordPolicy              PasswordPolicy
	AdditionalRealms            []Realm
	AccessTTL                   time.Duration
	SessionTTL                  time.Duration
	RefreshTTL                  time.Duration
	PasswordResetTTL            time.Duration
	ChallengeTTL                time.Duration
	EmailChangeTTL              time.Duration
	EnvelopeRetention           time.Duration
	ResetResponseFloor          time.Duration
	IdentityLinkAuthMaxAge      time.Duration
	LoginRateLimit              RateLimitPolicy
	PasswordResetRateLimit      RateLimitPolicy
	Now                         func() time.Time
	Random                      io.Reader
}

type Credential

type Credential struct {
	Identifier IdentifierInput
	Password   string
}

type EmailChallengeIssueResult

type EmailChallengeIssueResult struct {
	Status  EmailChallengeIssueStatus
	RetryAt time.Time
}

type EmailChallengeIssueStatus

type EmailChallengeIssueStatus string
const (
	EmailChallengeIssued      EmailChallengeIssueStatus = "issued"
	EmailChallengeWait        EmailChallengeIssueStatus = "wait"
	EmailChallengeHourlyLimit EmailChallengeIssueStatus = "hourly_limit"
	EmailChallengeDailyLimit  EmailChallengeIssueStatus = "daily_limit"
)

type EmailChallengeLimits

type EmailChallengeLimits struct {
	MinResendInterval time.Duration
	PerHour           int
	PerDay            int
}

type EmailChallengePurpose

type EmailChallengePurpose string
const EmailChallengePurposeVerification EmailChallengePurpose = "verification"

func (EmailChallengePurpose) Validate

func (p EmailChallengePurpose) Validate() error

type EmailChallengeRecord

type EmailChallengeRecord struct {
	ID           string
	SubjectID    SubjectID
	IdentifierID string
	// Runtime supplies these together so a store can reject a challenge if
	// the account changes after the caller reads its email destination.
	ExpectedNormalizedEmail string
	ExpectedSecurityVersion int64
	Purpose                 EmailChallengePurpose
	Digest                  SecretDigest
	RateDigest              SecretDigest
	MaxAttempts             int
	ExpiresAt               time.Time
	CreatedAt               time.Time
}

type EmailChallengeStore

type EmailChallengeStore interface {
	IssueEmailChallenge(
		ctx context.Context,
		record EmailChallengeRecord,
		limits EmailChallengeLimits,
	) (EmailChallengeIssueResult, error)
	VerifyEmailChallenge(ctx context.Context, request EmailChallengeVerifyRequest) (EmailChallengeVerifyResult, error)
}

type EmailChallengeVerifyRequest

type EmailChallengeVerifyRequest struct {
	SubjectID SubjectID
	Purpose   EmailChallengePurpose
	Digests   []SecretDigest
	Now       time.Time
}

type EmailChallengeVerifyResult

type EmailChallengeVerifyResult struct {
	Status   EmailChallengeVerifyStatus
	Account  Account
	Attempts int
}

type EmailChallengeVerifyStatus

type EmailChallengeVerifyStatus string
const (
	EmailChallengeVerified        EmailChallengeVerifyStatus = "verified"
	EmailChallengeInvalid         EmailChallengeVerifyStatus = "invalid"
	EmailChallengeExpired         EmailChallengeVerifyStatus = "expired"
	EmailChallengeAttemptsUsed    EmailChallengeVerifyStatus = "attempts_used"
	EmailChallengeAlreadyVerified EmailChallengeVerifyStatus = "already_verified"
)

type EmailChangeIssueResult

type EmailChangeIssueResult struct {
	Status  EmailChangeIssueStatus
	RetryAt time.Time
}

type EmailChangeIssueStatus

type EmailChangeIssueStatus string
const (
	EmailChangeIssued      EmailChangeIssueStatus = "issued"
	EmailChangeWait        EmailChangeIssueStatus = "wait"
	EmailChangeHourlyLimit EmailChangeIssueStatus = "hourly_limit"
	EmailChangeDailyLimit  EmailChangeIssueStatus = "daily_limit"
	EmailChangeSameValue   EmailChangeIssueStatus = "same_value"
)

type EmailChangeRecord

type EmailChangeRecord struct {
	ID                 string
	SubjectID          SubjectID
	NewDisplayValue    string
	NewNormalizedValue string
	Digest             SecretDigest
	RateDigest         SecretDigest
	MaxAttempts        int
	ExpiresAt          time.Time
	CreatedAt          time.Time
}

type EmailChangeStore

type EmailChangeStore interface {
	IssueEmailChange(
		ctx context.Context,
		record EmailChangeRecord,
		limits EmailChallengeLimits,
	) (EmailChangeIssueResult, error)
	GetPendingEmailChange(ctx context.Context, subjectID SubjectID, now time.Time) (PendingEmailChange, error)
	VerifyEmailChange(ctx context.Context, request EmailChangeVerifyRequest) (EmailChangeVerifyResult, error)
}

type EmailChangeVerifyRequest

type EmailChangeVerifyRequest struct {
	SubjectID SubjectID
	Digests   []SecretDigest
	Now       time.Time
}

type EmailChangeVerifyResult

type EmailChangeVerifyResult struct {
	Status   EmailChangeVerifyStatus
	Account  Account
	Attempts int
}

type EmailChangeVerifyStatus

type EmailChangeVerifyStatus string
const (
	EmailChangeVerified     EmailChangeVerifyStatus = "verified"
	EmailChangeInvalid      EmailChangeVerifyStatus = "invalid"
	EmailChangeExpired      EmailChangeVerifyStatus = "expired"
	EmailChangeAttemptsUsed EmailChangeVerifyStatus = "attempts_used"
	EmailChangeNotFound     EmailChangeVerifyStatus = "not_found"
)

type EncryptedEnvelope

type EncryptedEnvelope struct {
	KeyID          string
	Nonce          []byte
	Ciphertext     []byte
	AdditionalData []byte
	CreatedAt      time.Time
	DeleteAfter    time.Time
}

type EncryptedEvent

type EncryptedEvent struct {
	ID          string
	Type        string
	SubjectID   SubjectID
	ReferenceID string
	ValidUntil  time.Time
	Envelope    EncryptedEnvelope
}

type EncryptedEventSink

type EncryptedEventSink interface {
	EnqueueEncrypted(ctx context.Context, event EncryptedEvent) error
	DeleteEncrypted(ctx context.Context, eventID string) error
	DeleteExpiredEncrypted(ctx context.Context, before time.Time) (int64, error)
}

type ExternalIdentity

type ExternalIdentity struct {
	Issuer        string
	Subject       string
	Email         string
	EmailVerified bool
	Profile       BasicProfile
}

type ExternalLoginRequest

type ExternalLoginRequest struct {
	Identity ExternalIdentity
	Realm    Realm
}

type Identifier

type Identifier struct {
	ID              string
	SubjectID       SubjectID
	Scheme          IdentifierScheme
	DisplayValue    string
	NormalizedValue string
	VerifiedAt      *time.Time
	CreatedAt       time.Time
	UpdatedAt       time.Time
}

Identifier stores both a display value and its scheme-specific canonical value. VerifiedAt is the only source for verification claims.

func (Identifier) IsVerified

func (i Identifier) IsVerified() bool

type IdentifierInput

type IdentifierInput struct {
	Scheme IdentifierScheme
	Value  string
}

type IdentifierResolver

type IdentifierResolver interface {
	NormalizeIdentifier(ctx context.Context, input IdentifierInput) (IdentifierInput, error)
}

type IdentifierResolverFunc

type IdentifierResolverFunc func(context.Context, IdentifierInput) (IdentifierInput, error)

func (IdentifierResolverFunc) NormalizeIdentifier

func (f IdentifierResolverFunc) NormalizeIdentifier(ctx context.Context, input IdentifierInput) (IdentifierInput, error)

type IdentifierScheme

type IdentifierScheme string
const IdentifierSchemeEmail IdentifierScheme = "email"

func (IdentifierScheme) Validate

func (s IdentifierScheme) Validate() error
type IdentityLink struct {
	ID              string
	SubjectID       SubjectID
	Issuer          string
	ExternalSubject string
	EmailNormalized string
	EmailVerified   bool
	CreatedAt       time.Time
	UpdatedAt       time.Time
}

type IdentityLinkStore

type IdentityLinkStore interface {
	ResolveIdentityLink(ctx context.Context, issuer, externalSubject string) (Account, IdentityLink, error)
	CreateSSOAccount(ctx context.Context, record SSOAccountRecord) (Account, IdentityLink, error)
	LinkIdentity(ctx context.Context, link IdentityLink) (IdentityLink, error)
}

type Key

type Key struct {
	ID       string
	Material []byte
}

type KeyRing

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

KeyRing is an immutable versioned set of 256-bit symmetric keys.

func NewKeyRing

func NewKeyRing(activeKeyID string, keys ...Key) (KeyRing, error)

func (KeyRing) Active

func (r KeyRing) Active() (Key, error)

func (KeyRing) ActiveKeyID

func (r KeyRing) ActiveKeyID() string

func (KeyRing) Get

func (r KeyRing) Get(keyID string) (Key, error)

func (KeyRing) IDs

func (r KeyRing) IDs() []string

type LocalAccountRecord

type LocalAccountRecord struct {
	Account     Account
	PasswordPHC string
}

type LoginRequest

type LoginRequest struct {
	Credential Credential
	Realm      Realm
}

type LoginResult

type LoginResult struct {
	Account Account
	Tokens  TokenPair
}

type MembershipGate

type MembershipGate interface {
	AllowMembership(ctx context.Context, realm Realm, account Account) error
}

type MembershipGateFunc

type MembershipGateFunc func(context.Context, Realm, Account) error

func (MembershipGateFunc) AllowMembership

func (f MembershipGateFunc) AllowMembership(ctx context.Context, realm Realm, account Account) error

type Notification

type Notification struct {
	Template string            `json:"template"`
	To       string            `json:"to"`
	Data     map[string]string `json:"data,omitempty"`
}

type NotificationDelivery

type NotificationDelivery struct {
	ID string
	// EncryptedEvent is the exact sealed event claimed by the managed worker.
	// Delivery transports may persist this envelope, but never Notification's
	// plaintext codes/tokens. Use ID for downstream idempotency; delivery is at-least-once.
	EncryptedEvent EncryptedEvent
	Notification   Notification
	ValidUntil     time.Time
}

NotificationDelivery may be sent more than once before it is accepted, expires, or exhausts the attempt limit. A sender must use ID as its idempotency key when the underlying transport supports deduplication.

type NotificationDeliveryPolicy added in v0.5.0

type NotificationDeliveryPolicy uint8

NotificationDeliveryPolicy selects whether recovery and email commands are available. The zero value retains mandatory encrypted delivery.

const (
	NotificationDeliveryRequired NotificationDeliveryPolicy = iota
	NotificationDeliveryDisabled
)

type NotificationRenderer

type NotificationRenderer interface {
	RenderNotification(ctx context.Context, notification Notification) ([]byte, error)
}

type NotificationRendererFunc

type NotificationRendererFunc func(context.Context, Notification) ([]byte, error)

func (NotificationRendererFunc) RenderNotification

func (f NotificationRendererFunc) RenderNotification(ctx context.Context, notification Notification) ([]byte, error)

type NotificationSender

type NotificationSender interface {
	SendNotification(ctx context.Context, delivery NotificationDelivery) error
}

NotificationSender is the only delivery integration required from a host using the PostgreSQL managed notification queue.

type NotificationSenderFunc

type NotificationSenderFunc func(context.Context, NotificationDelivery) error

func (NotificationSenderFunc) SendNotification

func (f NotificationSenderFunc) SendNotification(ctx context.Context, delivery NotificationDelivery) error

type NotificationTransaction deprecated

type NotificationTransaction interface {
	InNotificationTransaction(ctx context.Context, fn func(context.Context) error) error
}

NotificationTransaction is the previous notification-only contract.

Deprecated: implement AuthTransaction for every Runtime operation.

type PasswordChangeStoreRequest

type PasswordChangeStoreRequest struct {
	SubjectID           SubjectID
	ExpectedPasswordPHC string
	NewPasswordPHC      string
	Now                 time.Time
}

type PasswordChangeStoreResult

type PasswordChangeStoreResult struct {
	Status  PasswordChangeStoreStatus
	Account Account
}

type PasswordChangeStoreStatus

type PasswordChangeStoreStatus string
const (
	PasswordChangeStoreSucceeded PasswordChangeStoreStatus = "succeeded"
	PasswordChangeStoreConflict  PasswordChangeStoreStatus = "conflict"
	PasswordChangeStoreMissing   PasswordChangeStoreStatus = "missing"
)

type PasswordEmailChangeRequest added in v0.5.0

type PasswordEmailChangeRequest struct {
	SubjectID       SubjectID
	CurrentPassword string
	NewEmail        string
}

PasswordEmailChangeRequest binds reauthentication to the actor selected by the trusted host. Never populate SubjectID from an unauthenticated request body.

type PasswordHasher

type PasswordHasher interface {
	HashPassword(password string) (string, error)
	// VerifyPassword returns nil on a match and an error on a mismatch. Runtime
	// preserves native mismatch errors as invalid credentials. Operational errors
	// must wrap ErrPasswordVerificationUnavailable, ErrPasswordHashOverloaded,
	// context.Canceled or context.DeadlineExceeded instead.
	VerifyPassword(phc, password string) error
}

type PasswordPolicy

type PasswordPolicy struct {
	MinLength        int
	MaxLength        int
	Blocklist        CommonPasswordChecker
	DisableBlocklist bool
}

func DefaultPasswordPolicy

func DefaultPasswordPolicy() PasswordPolicy

func (PasswordPolicy) Validate

func (p PasswordPolicy) Validate(password string) error

type PasswordResetConsumeRequest

type PasswordResetConsumeRequest struct {
	Selector    string
	Digest      SecretDigest
	PasswordPHC string
	Now         time.Time
}

type PasswordResetConsumeResult

type PasswordResetConsumeResult struct {
	Status  PasswordResetConsumeStatus
	Account Account
}

type PasswordResetConsumeStatus

type PasswordResetConsumeStatus string
const (
	PasswordResetConsumed PasswordResetConsumeStatus = "consumed"
	PasswordResetInvalid  PasswordResetConsumeStatus = "invalid"
	PasswordResetExpired  PasswordResetConsumeStatus = "expired"
	PasswordResetUsed     PasswordResetConsumeStatus = "used"
)

type PasswordResetReceipt added in v0.5.0

type PasswordResetReceipt struct {
	SubjectID SubjectID
	Selector  string
}

PasswordResetReceipt contains only public correlation identifiers, never a reset credential.

type PasswordResetRecord

type PasswordResetRecord struct {
	SubjectID SubjectID
	Selector  string
	Digest    SecretDigest
	// Expected fields reject a reset issued from a stale account lookup.
	ExpectedNormalizedEmail string
	ExpectedSecurityVersion int64
	ExpiresAt               time.Time
	CreatedAt               time.Time
}

type PasswordResetStore

type PasswordResetStore interface {
	CreatePasswordReset(ctx context.Context, record PasswordResetRecord) error
	ConsumePasswordReset(ctx context.Context, request PasswordResetConsumeRequest) (PasswordResetConsumeResult, error)
}

type PendingEmailChange

type PendingEmailChange struct {
	ID              string
	SubjectID       SubjectID
	NewDisplayValue string
	Attempts        int
	MaxAttempts     int
	CreatedAt       time.Time
	ExpiresAt       time.Time
}

type RateLimitPolicy

type RateLimitPolicy struct {
	Window time.Duration
	Limit  int
}

func (RateLimitPolicy) Validate

func (p RateLimitPolicy) Validate() error

type RateLimitRequest

type RateLimitRequest struct {
	SubjectID SubjectID
	Action    string
	Bucket    SecretDigest
	Window    time.Duration
	Limit     int
	Now       time.Time
}

type RateLimitResult

type RateLimitResult struct {
	Allowed bool
	RetryAt time.Time
}

type RateLimitStore

type RateLimitStore interface {
	TakeRateLimit(ctx context.Context, request RateLimitRequest) (RateLimitResult, error)
}

type Realm

type Realm string
const (
	RealmUser  Realm = "user"
	RealmAdmin Realm = "admin"
)

func (Realm) Validate

func (r Realm) Validate() error

type RefreshRotationRequest

type RefreshRotationRequest struct {
	ExpectedSecurityVersion int64
	ExpectedNormalizedEmail string
	ExpectedEmailVerified   bool
	CurrentSelector         string
	CurrentDigest           SecretDigest
	NextSelector            string
	NextDigest              SecretDigest
	NextExpiresAt           time.Time
	Now                     time.Time
}

type RefreshRotationResult

type RefreshRotationResult struct {
	Status   RefreshRotationStatus
	Account  Account
	Session  Session
	FamilyID string
}

type RefreshRotationStatus

type RefreshRotationStatus string
const (
	RefreshRotationSucceeded RefreshRotationStatus = "succeeded"
	RefreshRotationInvalid   RefreshRotationStatus = "invalid"
	RefreshRotationExpired   RefreshRotationStatus = "expired"
	RefreshRotationRevoked   RefreshRotationStatus = "revoked"
	RefreshRotationReplayed  RefreshRotationStatus = "replayed"
)

type RegisterRequest

type RegisterRequest struct {
	Email    string
	Password string
	Profile  BasicProfile
}

type RegisterResult

type RegisterResult struct {
	Account Account
	Tokens  TokenPair
}

type Runtime

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

func NewRuntime

func NewRuntime(config Config) (*Runtime, error)

func (*Runtime) AcknowledgeEncryptedEvent

func (r *Runtime) AcknowledgeEncryptedEvent(ctx context.Context, eventID string) error

func (*Runtime) AuthenticateSession added in v0.5.0

func (r *Runtime) AuthenticateSession(ctx context.Context, token string) (AuthContext, error)

AuthenticateSession validates current session/security state and realm membership.

func (*Runtime) ChangePassword

func (r *Runtime) ChangePassword(ctx context.Context, request ChangePasswordRequest) (Account, error)

func (*Runtime) CleanupExpiredEncryptedEvents

func (r *Runtime) CleanupExpiredEncryptedEvents(ctx context.Context) (int64, error)

func (*Runtime) ConfirmEmailChange

func (r *Runtime) ConfirmEmailChange(ctx context.Context, subjectID SubjectID, code string) (Account, error)

func (*Runtime) DecryptNotificationEvent

func (r *Runtime) DecryptNotificationEvent(event EncryptedEvent) (Notification, error)

func (*Runtime) FindAccount

func (r *Runtime) FindAccount(ctx context.Context, identifier IdentifierInput) (Account, error)

func (*Runtime) GetAccount

func (r *Runtime) GetAccount(ctx context.Context, subjectID SubjectID) (Account, error)

func (*Runtime) LinkExternalIdentity

func (r *Runtime) LinkExternalIdentity(ctx context.Context, auth AuthContext, external ExternalIdentity) (IdentityLink, error)

func (*Runtime) Login

func (r *Runtime) Login(ctx context.Context, request LoginRequest) (LoginResult, error)

func (*Runtime) LoginExternal

func (r *Runtime) LoginExternal(ctx context.Context, request ExternalLoginRequest) (LoginResult, error)

LoginExternal resolves or provisions an SSO-only account and creates a realm-bound session. An unverified email can receive only the user realm's limited confirmation session.

func (*Runtime) Logout

func (r *Runtime) Logout(ctx context.Context, subjectID SubjectID, sessionID string) error

func (*Runtime) LogoutAll

func (r *Runtime) LogoutAll(ctx context.Context, subjectID SubjectID) (int64, error)

func (*Runtime) PendingEmailChange

func (r *Runtime) PendingEmailChange(ctx context.Context, subjectID SubjectID) (PendingEmailChange, error)

func (*Runtime) ProvisionTrustedLocalAccount

func (r *Runtime) ProvisionTrustedLocalAccount(ctx context.Context, request RegisterRequest) (Account, error)

ProvisionTrustedLocalAccount creates a verified local account for an operator-controlled bootstrap or seed path. Interactive registration must use Register so that the account starts in confirmation scope.

func (*Runtime) Refresh

func (r *Runtime) Refresh(ctx context.Context, rawRefreshToken string) (TokenPair, error)

func (*Runtime) Register

func (r *Runtime) Register(ctx context.Context, request RegisterRequest) (RegisterResult, error)

func (*Runtime) RequestEmailChange

func (r *Runtime) RequestEmailChange(ctx context.Context, subjectID SubjectID, newEmail string) error

RequestEmailChange is a trusted host API. The host must authorize the actor and require fresh authentication before calling it. Local-password HTTP flows should use RequestEmailChangeWithPassword instead.

func (*Runtime) RequestEmailChangeWithPassword added in v0.5.0

func (r *Runtime) RequestEmailChangeWithPassword(ctx context.Context, request PasswordEmailChangeRequest) error

RequestEmailChangeWithPassword verifies this subject's current local password before issuing an email-change challenge. The caller still owns session and actor-to-target authorization. SSO-only accounts need an explicit host-owned recent-IdP-authentication flow through RequestEmailChange; there is no fallback.

func (*Runtime) RequestPasswordReset

func (r *Runtime) RequestPasswordReset(ctx context.Context, email string) error

func (*Runtime) RequestPasswordResetWithReceipt added in v0.5.0

func (r *Runtime) RequestPasswordResetWithReceipt(
	ctx context.Context, email string, bind func(context.Context, PasswordResetReceipt) error,
) error

RequestPasswordResetWithReceipt calls bind inside the initiating auth transaction after reset, encrypted notification and audit writes. A bind error rolls back all participating writes. No callback runs when no reset is issued. Privileged hosts must not expose whether bind ran to unauthenticated clients.

func (*Runtime) ResetPassword

func (r *Runtime) ResetPassword(ctx context.Context, rawToken, newPassword string) error

func (*Runtime) ResolveExternalIdentity

func (r *Runtime) ResolveExternalIdentity(ctx context.Context, external ExternalIdentity) (Account, error)

ResolveExternalIdentity accepts only identity claims verified by a trusted provider adapter. Never bind unverified client JSON directly to this method.

func (*Runtime) SendEmailChallenge

func (r *Runtime) SendEmailChallenge(
	ctx context.Context,
	subjectID SubjectID,
	purpose EmailChallengePurpose,
) error

func (*Runtime) SetSubjectStatus

func (r *Runtime) SetSubjectStatus(ctx context.Context, subjectID SubjectID, status SubjectStatus) (Subject, error)

func (*Runtime) UpdateBasicProfile

func (r *Runtime) UpdateBasicProfile(
	ctx context.Context,
	subjectID SubjectID,
	profile BasicProfile,
) (Account, error)

func (*Runtime) VerifyAccessToken deprecated

func (r *Runtime) VerifyAccessToken(ctx context.Context, token string, introspect bool) (AuthContext, error)

VerifyAccessToken is retained for source compatibility.

Deprecated: use VerifyJWT or AuthenticateSession explicitly.

func (*Runtime) VerifyCredential

func (r *Runtime) VerifyCredential(ctx context.Context, credential Credential) (Account, error)

VerifyCredential verifies a local credential without creating a session. It is intended for recent-authentication checks and trusted bootstrap flows.

func (*Runtime) VerifyEmailChallenge

func (r *Runtime) VerifyEmailChallenge(
	ctx context.Context,
	subjectID SubjectID,
	purpose EmailChallengePurpose,
	code string,
) (Account, error)

func (*Runtime) VerifyJWT added in v0.5.0

func (r *Runtime) VerifyJWT(ctx context.Context, token string) (AuthContext, error)

VerifyJWT validates an access JWT offline. Revocation takes effect only when it expires; use AuthenticateSession for current authorization.

type SSOAccountRecord

type SSOAccountRecord struct {
	Account Account
	Link    IdentityLink
}

type SecretDigest

type SecretDigest struct {
	KeyID  string
	Digest []byte
}

type SecurityEvent

type SecurityEvent struct {
	Type       SecurityEventType
	SubjectID  SubjectID
	Realm      Realm
	At         time.Time
	Attributes map[string]string
}

type SecurityEventType

type SecurityEventType string
const (
	SecurityEventLoginFailed          SecurityEventType = "login.failed"
	SecurityEventRefreshReplay        SecurityEventType = "refresh.replay"
	SecurityEventPasswordResetIssued  SecurityEventType = "password_reset.issued"
	SecurityEventPasswordResetUsed    SecurityEventType = "password_reset.used"
	SecurityEventPasswordChanged      SecurityEventType = "password.changed"
	SecurityEventEmailChallengeIssued SecurityEventType = "email_challenge.issued"
	SecurityEventEmailVerified        SecurityEventType = "email.verified"
	SecurityEventEmailChangeIssued    SecurityEventType = "email_change.issued"
	SecurityEventEmailChanged         SecurityEventType = "email.changed"
	SecurityEventSessionRevoked       SecurityEventType = "session.revoked"
	SecurityEventSessionsRevoked      SecurityEventType = "sessions.revoked"
	SecurityEventSubjectStatusChanged SecurityEventType = "subject.status_changed"
	SecurityEventIdentityLinked       SecurityEventType = "identity.linked"
)

type Session

type Session struct {
	ID              string
	SubjectID       SubjectID
	Realm           Realm
	Scope           SessionScope
	SecurityVersion int64
	CreatedAt       time.Time
	ExpiresAt       time.Time
	RevokedAt       *time.Time
}

func (Session) Active

func (s Session) Active(now time.Time) bool

type SessionRecord

type SessionRecord struct {
	Session          Session
	FamilyID         string
	RefreshSelector  string
	RefreshDigest    SecretDigest
	RefreshExpiresAt time.Time
}

type SessionScope

type SessionScope string
const (
	SessionScopeAuthenticated SessionScope = "authenticated"
	SessionScopeConfirmation  SessionScope = "confirmation"
)

type SessionSecurity

type SessionSecurity struct {
	Session        Session
	SubjectStatus  SubjectStatus
	CurrentVersion int64
}

type SessionStore

type SessionStore interface {
	// PeekRefresh authenticates a token without consuming it. Rotation revalidates all state.
	PeekRefresh(ctx context.Context, request RefreshRotationRequest) (RefreshRotationResult, error)
	CreateSession(ctx context.Context, record SessionRecord) error
	RotateRefresh(ctx context.Context, request RefreshRotationRequest) (RefreshRotationResult, error)
	IntrospectSession(ctx context.Context, sessionID string) (SessionSecurity, error)
	RevokeSession(ctx context.Context, subjectID SubjectID, sessionID string, now time.Time) (bool, error)
	RevokeSubjectSessions(ctx context.Context, subjectID SubjectID, now time.Time) (int64, error)
	SetSubjectStatus(ctx context.Context, subjectID SubjectID, status SubjectStatus, now time.Time) (Subject, error)
}

type SigningConfig

type SigningConfig struct {
	Issuer   string
	Audience string
	Keys     KeyRing
}

type Subject

type Subject struct {
	ID              SubjectID
	Status          SubjectStatus
	SecurityVersion int64
	CreatedAt       time.Time
	UpdatedAt       time.Time
}

Subject is the canonical security principal. Profile, identifiers, credentials, memberships, and permissions deliberately live elsewhere.

func (Subject) IsZero

func (s Subject) IsZero() bool

type SubjectID

type SubjectID uuid.UUID

SubjectID is the canonical identifier shared by every goauth relation.

func MustParseSubjectID

func MustParseSubjectID(value string) SubjectID

func NewSubjectID

func NewSubjectID() SubjectID

func ParseSubjectID

func ParseSubjectID(value string) (SubjectID, error)

func (SubjectID) IsZero

func (id SubjectID) IsZero() bool

func (SubjectID) MarshalText

func (id SubjectID) MarshalText() ([]byte, error)

func (*SubjectID) Scan

func (id *SubjectID) Scan(source any) error

func (SubjectID) String

func (id SubjectID) String() string

func (*SubjectID) UnmarshalText

func (id *SubjectID) UnmarshalText(value []byte) error

func (SubjectID) Validate

func (id SubjectID) Validate() error

func (SubjectID) Value

func (id SubjectID) Value() (driver.Value, error)

type SubjectStatus

type SubjectStatus string
const (
	SubjectStatusActive    SubjectStatus = "active"
	SubjectStatusSuspended SubjectStatus = "suspended"
	SubjectStatusDisabled  SubjectStatus = "disabled"
)

func (SubjectStatus) Validate

func (s SubjectStatus) Validate() error

type TokenPair

type TokenPair struct {
	AccessToken      string
	RefreshToken     string
	AccessExpiresAt  time.Time
	RefreshExpiresAt time.Time
	Session          Session
}

type URLBuilder

type URLBuilder interface {
	PasswordResetURL(ctx context.Context, token string) (string, error)
}

type URLBuilderFunc

type URLBuilderFunc func(context.Context, string) (string, error)

func (URLBuilderFunc) PasswordResetURL

func (f URLBuilderFunc) PasswordResetURL(ctx context.Context, token string) (string, error)

Directories

Path Synopsis
cmd
releaseevidence command
Command releaseevidence validates a complete public-preview evidence manifest.
Command releaseevidence validates a complete public-preview evidence manifest.
examples
nethttp command
Command nethttp demonstrates host-owned browser transport and bearer-only API routes backed by a single PostgreSQL Runtime.
Command nethttp demonstrates host-owned browser transport and bearer-only API routes backed by a single PostgreSQL Runtime.
internal
authclock
Package authclock carries the Runtime clock across its transaction boundary.
Package authclock carries the Runtime clock across its transaction boundary.
retryafter
Package retryafter extracts a transport-independent retry deadline.
Package retryafter extracts a transport-independent retry deadline.
Package nethttp adapts goauth to standard-library JSON HTTP handlers.
Package nethttp adapts goauth to standard-library JSON HTTP handlers.
reference
externalconsumer
Package externalconsumer is a compile-checked manifest of the goauth packages that are currently supported for external consumers such as backend and goadmin.
Package externalconsumer is a compile-checked manifest of the goauth packages that are currently supported for external consumers such as backend and goadmin.

Jump to

Keyboard shortcuts

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