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

authsome

package module
v1.9.0 Latest Latest
Warning

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

Go to latest
Published: Nov 2, 2026 License: MIT Imports: 74 Imported by: 0

README ¶

Authsome, a plugin-driven authentication and identity engine for Go with sessions, social login, passkeys and MFA

Authsome: authentication and identity for Go

Authsome is an authentication and user management engine for Go. The core gives you users, sessions with refresh tokens, organizations, RBAC, devices, apps and environments; every sign-in method (password, social login, magic links, passkeys and WebAuthn, MFA, SSO, phone, API keys) is a plugin you register, along with risk controls such as GeoIP, impossible travel and VPN detection. It runs inside your Go service against your own database, records every security event to Chronicle and checks permissions with Warden, and it plugs into Forge as an extension.

CI Go Reference GitHub Stars

Documentation · Quick start · Features · Plugins · Forge · Configuration · Client SDKs · FAQ

Quick start

You need Go 1.26 or later.

go get github.com/xraph/authsome@latest github.com/xraph/chronicle@latest github.com/xraph/warden@latest

Save this as main.go. It runs an auth server with email and password sign-in, using in-memory stores for Authsome, its audit trail and its permissions:

package main

import (
	"context"
	"log"
	"net/http"

	"github.com/xraph/chronicle"
	chstore "github.com/xraph/chronicle/store"
	chmemory "github.com/xraph/chronicle/store/memory"
	"github.com/xraph/forge"
	"github.com/xraph/warden"
	wmemory "github.com/xraph/warden/store/memory"

	"github.com/xraph/authsome"
	"github.com/xraph/authsome/api"
	"github.com/xraph/authsome/bridge/chronicleadapter"
	"github.com/xraph/authsome/middleware"
	"github.com/xraph/authsome/plugins/password"
	"github.com/xraph/authsome/store/memory"
)

func main() {
	ctx := context.Background()

	// Authsome records every security event to Chronicle and checks
	// permissions with Warden, so both are required.
	trail, err := chronicle.New(chronicle.WithStore(chstore.NewAdapter(chmemory.New())))
	if err != nil {
		log.Fatal(err)
	}
	authz, err := warden.NewEngine(warden.WithStore(wmemory.New()))
	if err != nil {
		log.Fatal(err)
	}

	engine, err := authsome.NewEngine(
		authsome.WithStore(memory.New()),
		authsome.WithChronicle(chronicleadapter.New(trail)),
		authsome.WithWarden(authz),
		authsome.WithDisableMigrate(), // the memory store has nothing to migrate
		authsome.WithPlugin(password.New()),
		authsome.WithBootstrap(authsome.WithBootstrapAppName("Acme")),
	)
	if err != nil {
		log.Fatal(err)
	}
	if err := engine.Start(ctx); err != nil {
		log.Fatal(err)
	}
	defer engine.Stop(ctx) //nolint:errcheck // example

	router := forge.NewRouter()
	router.Use(middleware.AuthMiddleware(engine.ResolveSessionByToken, engine.ResolveUser, nil))
	if err := api.New(engine).RegisterRoutes(router); err != nil {
		log.Fatal(err)
	}

	log.Printf("listening on :8080, app_id=%s", engine.PlatformAppID())
	log.Fatal(http.ListenAndServe(":8080", router.Handler())) //nolint:gosec // example
}

Authsome encrypts the tokens it stores, so give it a key, then run:

go mod tidy
export AUTHSOME_TOKEN_ENCRYPTION_KEY=$(openssl rand -hex 32)
go run .
# listening on :8080, app_id=aapp_...

Sign a user up, from another terminal. Put the app_id from the log line in the body:

curl -X POST http://localhost:8080/v1/signup \
  -H 'Content-Type: application/json' \
  -d '{"app_id":"aapp_...","email":"alice@example.com","password":"SecureP@ss123"}'
# {"session_token":"...","refresh_token":"...","expires_at":"...","user":{...}}

curl http://localhost:8080/v1/me -H 'Authorization: Bearer <session_token>'

WithBootstrap created a platform app with its default environments and roles on start, which is why you have an app ID to sign up against. Browser and mobile clients usually send an X-Publishable-Key header instead of the ID. One thing that catches people: /v1/signin refuses an account whose email isn't verified, because the auth.require_email_verification setting is on by default. Verify the address through /v1/verify-email, or switch the setting off for local work.

The OpenAPI spec for everything you've mounted is at /.well-known/authsome/openapi.

Why Authsome?

Auth in a SaaS product grows sideways. You start with a password form and soon need Google sign-in, organizations with invitations, an admin who can see sessions, and a way to block a login from the wrong country. Authsome keeps those in one engine with one user model, and makes each extra method or control a plugin, so your binary only carries what you register and you add the next one when your customers ask for it.

You need Authsome gives you
Accounts Sign-up, sign-in, sign-out, email verification, password reset and change, profile updates and account deletion
Sessions Session and refresh tokens with configurable lifetimes, refresh rotation, a per-user session cap, session listing and revocation, and JWT or opaque token formats
Sign-in methods Plugins for password, social login (22 providers including Google, GitHub, Apple and Microsoft), magic links, passkeys, MFA, SSO, phone and API keys
B2B tenancy Apps and environments in the core, and organizations, members, invitations and teams through the organization plugin
Authorization RBAC backed by Warden, with roles seeded from .warden files on bootstrap
An audit trail Every security-relevant action recorded to Chronicle, with no silent fallback
Abuse controls Rate limiting, account lockout and breached-password checks, plus GeoIP, geofencing, impossible travel, VPN detection, IP reputation and a risk engine as plugins
To be an identity provider The oauth2provider plugin, and agentauth for AI agents acting on a user's behalf
Your database PostgreSQL, SQLite and MongoDB through Grove, plus an in-memory store
Clients A Go client, a TypeScript client, Dart and Flutter packages, and React, Vue and Next.js UI packages in this repo

Plugins

Nothing beyond the core is on until you register it. You pick. Pass each plugin to the engine with authsome.WithPlugin, or to the Forge extension with WithPlugins:

authsome.WithPlugin(password.New()),
authsome.WithPlugin(mfa.New(mfa.Config{Issuer: "Acme"})),
authsome.WithPlugin(social.New(social.Config{
	Providers: []social.Provider{
		social.NewGitHubProvider(social.ProviderConfig{
			ClientID:     os.Getenv("GITHUB_CLIENT_ID"),
			ClientSecret: os.Getenv("GITHUB_CLIENT_SECRET"),
			RedirectURL:  "https://app.example.com/authsome/v1/social/github/callback",
		}),
	},
})),
Category Plugins
Authentication password, social, magiclink, passkey, mfa, apikey, sso, phone
Identity organization, consent, subscription, waitlist
Communication email, notification
Risk and security anomaly, geofence, geoip, impossibletravel, ipreputation, riskengine, sharedsignals, vpndetect
Provisioning scim, deviceverify, oauth2provider, agentauth
Data retention

Some depend on others, so watch the order you register them in. geofence, vpndetect and impossibletravel read the location geoip resolves, so register geoip first, and sso and scim scope their connections to an organization. plugins/README.md has a "pick by what you are trying to do" table, the full dependency list and a README per plugin with its config, settings and endpoints.

Storage backends

Backend Package Constructor
In-memory store/memory memory.New()
PostgreSQL store/postgres postgres.New(db *grove.DB)
SQLite store/sqlite sqlite.New(db *grove.DB)
MongoDB store/mongo mongo.New(db *grove.DB)

All four pass the same store conformance suite in CI, so you can develop on SQLite and deploy on PostgreSQL. Plugins that keep their own tables (social connections, MFA factors, passkeys and others) ship a store per backend too, and the engine runs their migrations alongside its own on start unless you pass WithDisableMigrate. MongoDB needs a replica set, because app and organization deletes cascade inside transactions.

Use it with Forge

The extension package registers Authsome as a Forge extension. It reads extensions.authsome (or a top-level authsome key), builds a store from the container's *grove.DB, mounts the API under /authsome, runs the bootstrap, and adds the auth middleware to your app.

go get github.com/xraph/authsome@latest github.com/xraph/chronicle@latest \
  github.com/xraph/warden@latest github.com/xraph/grove/extension@latest
import (
	grove "github.com/xraph/grove/extension"

	authsome "github.com/xraph/authsome/extension"
	"github.com/xraph/authsome/plugins/mfa"
	"github.com/xraph/authsome/plugins/password"
	chronicle "github.com/xraph/chronicle/extension"
	warden "github.com/xraph/warden/extension"
)

app := forge.NewApp(forge.AppConfig{
	Name: "acme-api",
	Extensions: []forge.Extension{
		grove.New(),     // extensions.grove: driver and dsn
		chronicle.New(), // audit trail, required by authsome
		warden.New(),    // authorization, required by authsome
		authsome.New(
			authsome.WithPlugins(
				password.New(),
				mfa.New(mfa.Config{Issuer: "Acme"}),
			),
		),
	},
})

Order matters here, and you'll find out quickly if you get it wrong. Authsome looks for chronicle.Emitter and *warden.Engine in the container during Register and refuses to start without them, so list Chronicle and Warden first. Chronicle in turn wants you to decide who may call its admin API (see its README). Anything else it finds in the container gets wired up as a bridge: Keysmith for API keys, Relay for webhooks, Herald for notifications, Dispatch for background jobs and Ledger for billing. Without them it uses no-op bridges.

You still need AUTHSOME_TOKEN_ENCRYPTION_KEY (64 hex characters), or AUTHSOME_TOKEN_ENCRYPTION_KEYS as kid:hex pairs when you rotate, or an encryptor passed with authsome.WithEngineOption(authsome.WithTokenEncryptor(...)).

A service that should trust an Authsome deployment elsewhere can run the extension in client mode with WithClientMode(portalURL), WithServiceAPIKey and WithServiceAppID.

Configuration

extensions:
  authsome:
    base_path: /authsome
    grove_database: primary          # optional; the default *grove.DB otherwise
    session:
      token_ttl: 1h
      refresh_token_ttl: 720h
      max_active_sessions: 5         # 0 means unlimited
      rotate_refresh_token: true
    password:
      min_length: 12
      algorithm: argon2id            # bcrypt by default; old hashes are rehashed on sign-in
      check_breached: true
    mailer:
      provider: resend               # resend or smtp; nothing is sent otherwise
      resend:
        api_key: re_your_resend_key
        from: auth@example.com
    bootstrap:
      app_name: Acme
      initial_owners: [founder@example.com]

The Config struct in extension/config.go also covers rate_limit, lockout, retention, csrf, webhooks, per-app session settings under apps, bootstrap environments and warden_dir for your own role files, and the client-mode keys. In code, the extension takes WithConfig, WithPlugin, WithPlugins, WithEngineOption, WithEngineOptions, WithBasePath, WithDisableRoutes, WithDisableMigrate, WithGroveDatabase, WithRequireConfig and WithLogger.

Outside Forge, pass an authsome.Config with authsome.WithConfig, or use the individual engine options such as WithBasePath, WithRateLimiter, WithLockoutConfig, WithDefaultTokenFormat and WithJWTFormat. Settings that admins change at runtime, like email verification, live in the engine's settings manager.

Client SDKs and UI

Client Where
Go github.com/xraph/authsome/sdk/go (package authclient, authclient.NewClient(baseURL))
TypeScript sdk/, package @authsome/client
Dart sdk/dart
Flutter flutter/packages: authsome_core, authsome_flutter, authsome_flutter_ui
React, Vue, Next.js ui/packages: @authsome/ui-core, @authsome/ui-components, @authsome/ui-react, @authsome/ui-vue, @authsome/ui-nextjs

The clients follow the OpenAPI spec the server serves at /.well-known/authsome/openapi, and /.well-known/authsome/manifest returns the service version, base path and available endpoints.

Documentation

Topic Where
Getting started Getting started, architecture, full example
Sign-in methods Overview, password, social login, passkeys, MFA, SSO, OAuth2 provider
Organizations Overview, members, teams, environments
Security RBAC, rate limiting, lockout, audit trail, webhooks
Integrations Forge extension, bridges, custom plugin, Next.js app, UI
Storage and API Stores, HTTP API

The Go API is on pkg.go.dev.

Examples

_examples/standalone and _examples/forge show the engine on its own and as a Forge extension. Both are behind the current API: the standalone one doesn't pass a Warden engine, so NewEngine refuses it, and the Forge one doesn't register Chronicle or Warden. Follow the quick start and the Forge section above until they're updated.

FAQ

How do I add authentication to a Go web app?

Build an engine with a store, Chronicle, Warden and the plugins you want, mount the API on a Forge router, and add middleware.AuthMiddleware so handlers can see the current user. Under Forge, the extension does all of that from config.

Does Authsome support social login and passkeys?

Yes, through the social plugin (22 OAuth providers, among them Google, GitHub, Apple, Microsoft and Discord) and the passkey plugin for WebAuthn.

Can Authsome handle multi-tenant B2B apps?

Yes. Apps and environments are part of the core, and the organization plugin adds organizations, memberships, invitations and teams, with org-scoped roles through Warden.

Why does Authsome need Chronicle and Warden?

Every security event has to land in an audit trail, and every permission check needs an authorization engine. If you leave either out, Authsome refuses to start, because an auth server that can't tell you who did what is the one thing your auditor won't accept.

Can I use Authsome without Forge?

Partly. The engine and stores are plain Go, but the HTTP API registers on a Forge router (forge.NewRouter()), which you can serve with the standard net/http server as the quick start does.

Contributing

Issues and focused pull requests are welcome on GitHub.

git clone https://github.com/xraph/authsome.git
cd authsome
GOWORK=off go test ./...     # memory and SQLite
make check                   # fmt, vet, golangci-lint and govulncheck
make test-integration        # PostgreSQL and a MongoDB replica set, needs Docker

make test-integration runs the same store conformance suite as CI's Postgres and MongoDB job, starting a fresh Mongo replica set each time, because several of those tests leave rows behind that would break a second run against the same database. Commits follow Conventional Commits. SECURITY.md explains how to report a vulnerability.

License

MIT. See LICENSE.

Authsome is part of the Forge forgery stack, built by Rex Raphael and contributors.

Documentation ¶

Overview ¶

Package authsome provides a composable authentication engine for the Forge ecosystem.

AuthSome v0.5.0 is an interface-driven, store-abstracted authentication engine with a type-cached plugin registry, global hook bus, strategy-based authentication, and optional bridges to forgery extensions (Chronicle, Warden, Keysmith, Relay).

Index ¶

Constants ¶

View Source
const EmailVerificationTTL = 24 * time.Hour

EmailVerificationTTL is the lifetime of a freshly issued email verification token. 24h is the default operators expect for "click the link in your inbox" flows; the token can always be reissued.

View Source
const MFATicketTTL = 5 * time.Minute

MFATicketTTL is how long a partial-auth ticket remains valid between the moment the gate fires and the user submitting their second factor. Five minutes balances "user has time to find their authenticator" against "leaked ticket has a short window."

View Source
const MaxMFATicketAttempts = 5

MaxMFATicketAttempts caps wrong codes against a single ticket before it is discarded and the user must sign in again.

A ticket is partial authentication: the password leg already succeeded, so the only thing between the holder and a session is a 6-digit code. Route rate limiting is the other defence, but it returns no middleware at all when disabled by config — so the ticket has to bound its own attempts rather than rely on a limiter that may not be there.

Variables ¶

View Source
var (
	// SettingCaptchaRequired toggles the captcha middleware. When false
	// (default) the middleware passes every request through, preserving
	// back-compat for existing deployments.
	SettingCaptchaRequired = settings.Define("auth.captcha_required", false,
		settings.WithDisplayName("Require Captcha"),
		settings.WithDescription("Gate sensitive endpoints (signup, signin) on a verified captcha challenge"),
		settings.WithCategory("Captcha"),
		settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp),
		settings.WithEnforceable(),
		settings.WithHelpText("When enabled, requests to gated endpoints must include a valid captcha token (X-Captcha-Token header or captcha_token form field)."),
		settings.WithOrder(200),
	)

	// SettingCaptchaProvider selects the captcha backend. Phase 2B only
	// ships "turnstile"; future providers may extend this list.
	SettingCaptchaProvider = settings.Define("auth.captcha_provider", "turnstile",
		settings.WithDisplayName("Captcha Provider"),
		settings.WithDescription("Captcha provider used to verify tokens"),
		settings.WithCategory("Captcha"),
		settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp),
		settings.WithEnforceable(),
		settings.WithInputType(formconfig.FieldSelect),
		settings.WithOptions(
			formconfig.SelectOption{Value: "turnstile", Label: "Cloudflare Turnstile"},
		),
		settings.WithHelpText("Phase 2B ships Turnstile only. hCaptcha and reCAPTCHA may follow."),
		settings.WithOrder(210),
		settings.WithVisibleWhen("auth.captcha_required", true),
	)

	// SettingCaptchaSiteKey is the public site key rendered in the
	// frontend captcha widget. Not used by the verifier — surfaced for
	// clients via the manifest.
	SettingCaptchaSiteKey = settings.Define("auth.captcha_site_key", "",
		settings.WithDisplayName("Captcha Site Key"),
		settings.WithDescription("Public site key for the captcha widget"),
		settings.WithCategory("Captcha"),
		settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp),
		settings.WithInputType(formconfig.FieldText),
		settings.WithHelpText("The public site key as shown by the provider's dashboard. Safe to expose to browsers."),
		settings.WithOrder(220),
		settings.WithVisibleWhen("auth.captcha_required", true),
	)

	// SettingCaptchaSecretKey is the secret used to call the provider's
	// siteverify endpoint. Marked Sensitive: the API redacts it, the
	// dashboard renders a password input, and the manifest never includes
	// it. NEVER log this value.
	SettingCaptchaSecretKey = settings.Define("auth.captcha_secret_key", "",
		settings.WithDisplayName("Captcha Secret Key"),
		settings.WithDescription("Secret key used to call the captcha provider's siteverify endpoint"),
		settings.WithCategory("Captcha"),
		settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp),
		settings.WithSensitive(),
		settings.WithInputType(formconfig.FieldText),
		settings.WithHelpText("Secret — never log, never expose via the manifest, never echo in API responses. Rotating this value invalidates the cached verifier on the next request."),
		settings.WithOrder(230),
		settings.WithVisibleWhen("auth.captcha_required", true),
	)
)
View Source
var (
	// SettingDPoPMode controls whether tokens are bound to a client key.
	//
	// Defaults to off so existing deployments are unaffected. A per-client
	// value on OAuth2Client can raise this but never lower it.
	SettingDPoPMode = settings.Define("dpop.mode", string(dpop.ModeOff),
		settings.WithDisplayName("DPoP Mode"),
		settings.WithDescription("Bind issued tokens to a client-held key (RFC 9449)"),
		settings.WithCategory("DPoP"),
		settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp),
		settings.WithEnforceable(),
		settings.WithInputType(formconfig.FieldSelect),
		settings.WithOptions(
			formconfig.SelectOption{Value: string(dpop.ModeOff), Label: "Off"},
			formconfig.SelectOption{Value: string(dpop.ModeOptional), Label: "Optional"},
			formconfig.SelectOption{Value: string(dpop.ModeRequired), Label: "Required"},
		),
		settings.WithHelpText("Off ignores proofs. Optional binds when a client proves and issues a bearer token otherwise. Required refuses to issue unbound tokens and will break clients that do not send proofs."),
		settings.WithOrder(200),
		settings.WithValidation(validateDPoPMode),
	)

	// SettingDPoPNonceRequired demands a server-issued nonce in every proof.
	//
	// Off by default because it costs every client one extra round trip: the
	// first request is answered with a challenge and has to be retried.
	SettingDPoPNonceRequired = settings.Define("dpop.nonce_required", false,
		settings.WithDisplayName("Require DPoP Nonce"),
		settings.WithDescription("Demand a server-issued nonce in every DPoP proof"),
		settings.WithCategory("DPoP"),
		settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp),
		settings.WithEnforceable(),
		settings.WithHelpText("Adds replay resistance at the cost of one extra round trip per client when its nonce expires."),
		settings.WithOrder(210),
	)

	// SettingDPoPIatLeewayPast is how far in the past a proof iat may sit.
	SettingDPoPIatLeewayPast = settings.Define("dpop.iat_leeway_past_seconds", 60,
		settings.WithDisplayName("DPoP Proof Age Tolerance (seconds)"),
		settings.WithDescription("How old a DPoP proof may be"),
		settings.WithCategory("DPoP"),
		settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp),
		settings.WithEnforceable(),
		settings.WithInputType(formconfig.FieldNumber),
		settings.WithUIValidation(formconfig.Validation{Required: true, Min: new(1), Max: new(300)}),
		settings.WithHelpText("Default: 60. Most proofs arriving late are clients with drifting clocks."),
		settings.WithOrder(220),
		settings.WithValidation(validateDPoPLeeway(1, 300)),
	)

	// SettingDPoPIatLeewayFuture is how far ahead a proof iat may sit.
	//
	// Tighter than the past window on purpose: a future-dated proof means
	// somebody minted it ahead of time, which has no innocent explanation.
	SettingDPoPIatLeewayFuture = settings.Define("dpop.iat_leeway_future_seconds", 30,
		settings.WithDisplayName("DPoP Clock Skew Tolerance (seconds)"),
		settings.WithDescription("How far ahead of server time a DPoP proof may be dated"),
		settings.WithCategory("DPoP"),
		settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp),
		settings.WithEnforceable(),
		settings.WithInputType(formconfig.FieldNumber),
		settings.WithUIValidation(formconfig.Validation{Required: true, Min: new(0), Max: new(120)}),
		settings.WithHelpText("Default: 30. Keep this small; a future-dated proof is pre-minting."),
		settings.WithOrder(230),
		settings.WithValidation(validateDPoPLeeway(0, 120)),
	)
)
View Source
var (
	// SettingTokenTTLSeconds controls the access token lifetime in seconds.
	SettingTokenTTLSeconds = settings.Define("session.token_ttl_seconds", 3600,
		settings.WithDisplayName("Access Token TTL (seconds)"),
		settings.WithDescription("Lifetime of access tokens in seconds"),
		settings.WithCategory("Token Lifetimes"),
		settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp),
		settings.WithEnforceable(),
		settings.WithInputType(formconfig.FieldNumber),
		settings.WithUIValidation(formconfig.Validation{Required: true, Min: new(60), Max: new(86400)}),
		settings.WithHelpText("How long access tokens remain valid. Default: 3600 (1 hour)"),
		settings.WithOrder(10),
		settings.WithValidation(validateTokenTTL),
	)

	// SettingRefreshTokenTTLSeconds controls the refresh token lifetime in seconds.
	SettingRefreshTokenTTLSeconds = settings.Define("session.refresh_token_ttl_seconds", 2592000,
		settings.WithDisplayName("Refresh Token TTL (seconds)"),
		settings.WithDescription("Lifetime of refresh tokens in seconds"),
		settings.WithCategory("Token Lifetimes"),
		settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp),
		settings.WithEnforceable(),
		settings.WithInputType(formconfig.FieldNumber),
		settings.WithUIValidation(formconfig.Validation{Required: true, Min: new(3600), Max: new(7776000)}),
		settings.WithHelpText("How long refresh tokens remain valid. Default: 2592000 (30 days)"),
		settings.WithOrder(20),
		settings.WithValidation(validateRefreshTokenTTL),
	)
)
View Source
var (
	// SettingRotateRefreshToken controls whether refresh operations issue a new refresh token.
	SettingRotateRefreshToken = settings.Define("session.rotate_refresh_token", true,
		settings.WithDisplayName("Rotate Refresh Token"),
		settings.WithDescription("Issue a new refresh token on each refresh, invalidating the old one"),
		settings.WithCategory("Session Behavior"),
		settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp),
		settings.WithEnforceable(),
		settings.WithHelpText("When enabled, each token refresh issues a new refresh token. Prevents replay attacks."),
		settings.WithOrder(30),
	)

	// SettingBindToIP controls whether sessions are locked to the originating IP.
	SettingBindToIP = settings.Define("session.bind_to_ip", false,
		settings.WithDisplayName("Bind Session to IP"),
		settings.WithDescription("Reject requests from a different IP than the one that created the session"),
		settings.WithCategory("Session Behavior"),
		settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp),
		settings.WithEnforceable(),
		settings.WithHelpText("When enabled, sessions are locked to the original client IP address"),
		settings.WithOrder(40),
	)

	// SettingBindToDevice controls whether sessions are locked to the originating device.
	SettingBindToDevice = settings.Define("session.bind_to_device", false,
		settings.WithDisplayName("Bind Session to Device"),
		settings.WithDescription("Reject requests from a different device/user-agent than the one that created the session"),
		settings.WithCategory("Session Behavior"),
		settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp),
		settings.WithEnforceable(),
		settings.WithHelpText("When enabled, sessions are locked to the original device fingerprint or user-agent"),
		settings.WithOrder(50),
	)

	// SettingCleanupIntervalSeconds controls how often expired sessions are cleaned up.
	SettingCleanupIntervalSeconds = settings.Define("session.cleanup_interval_seconds", 3600,
		settings.WithDisplayName("Cleanup Interval (seconds)"),
		settings.WithDescription("Interval between expired session cleanup runs"),
		settings.WithCategory("Session Behavior"),
		settings.WithScopes(settings.ScopeGlobal),
		settings.WithInputType(formconfig.FieldNumber),
		settings.WithUIValidation(formconfig.Validation{Required: true, Min: new(60), Max: new(86400)}),
		settings.WithHelpText("How often the engine removes expired sessions. Default: 3600 (1 hour)"),
		settings.WithOrder(60),
	)
)
View Source
var (
	// SettingMultiSessionEnabled controls whether users can have multiple concurrent sessions.
	SettingMultiSessionEnabled = settings.Define("session.multi_session_enabled", true,
		settings.WithDisplayName("Allow Multiple Sessions"),
		settings.WithDescription("Whether users can have multiple concurrent sessions"),
		settings.WithCategory("Multi-Session"),
		settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp),
		settings.WithEnforceable(),
		settings.WithHelpText("When disabled, signing in from a new device revokes all existing sessions first"),
		settings.WithOrder(70),
	)

	// SettingMaxActiveSessions controls the maximum number of concurrent sessions per user.
	SettingMaxActiveSessions = settings.Define("session.max_active_sessions", 0,
		settings.WithDisplayName("Maximum Active Sessions"),
		settings.WithDescription("Maximum concurrent sessions per user. 0 means unlimited."),
		settings.WithCategory("Multi-Session"),
		settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp),
		settings.WithEnforceable(),
		settings.WithInputType(formconfig.FieldNumber),
		settings.WithUIValidation(formconfig.Validation{Min: new(0), Max: new(100)}),
		settings.WithHelpText("When set, oldest sessions are evicted when the limit is reached. 0 = unlimited."),
		settings.WithOrder(80),
		settings.WithVisibleWhen("session.multi_session_enabled", true),
	)
)
View Source
var (
	// SettingAutoRefreshEnabled controls whether near-expiry tokens are auto-refreshed in middleware.
	SettingAutoRefreshEnabled = settings.Define("session.auto_refresh_enabled", true,
		settings.WithDisplayName("Auto-Refresh Tokens"),
		settings.WithDescription("Automatically refresh near-expiry access tokens in middleware"),
		settings.WithCategory("Auto-Refresh"),
		settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp),
		settings.WithEnforceable(),
		settings.WithHelpText("When enabled, the middleware transparently refreshes access tokens approaching expiry and returns new tokens in response headers"),
		settings.WithOrder(90),
	)

	// SettingAutoRefreshThresholdSeconds controls the time window before expiry to trigger auto-refresh.
	SettingAutoRefreshThresholdSeconds = settings.Define("session.auto_refresh_threshold_seconds", 300,
		settings.WithDisplayName("Auto-Refresh Threshold (seconds)"),
		settings.WithDescription("Time before expiry to trigger auto-refresh"),
		settings.WithCategory("Auto-Refresh"),
		settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp),
		settings.WithInputType(formconfig.FieldNumber),
		settings.WithUIValidation(formconfig.Validation{Required: true, Min: new(30), Max: new(3600)}),
		settings.WithHelpText("Access tokens within this many seconds of expiry will be auto-refreshed. Default: 300 (5 minutes)"),
		settings.WithOrder(100),
		settings.WithVisibleWhen("session.auto_refresh_enabled", true),
	)

	// SettingAutoRefreshExposeRefreshToken controls whether the refresh token is
	// included in the X-Auth-Refresh-Token header during auto-refresh.
	SettingAutoRefreshExposeRefreshToken = settings.Define("session.auto_refresh_expose_refresh_token", false,
		settings.WithDisplayName("Expose Refresh Token in Auto-Refresh"),
		settings.WithDescription("Include refresh token in X-Auth-Refresh-Token header during auto-refresh"),
		settings.WithCategory("Auto-Refresh"),
		settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp),
		settings.WithHelpText("When disabled (recommended), only the access token is returned in auto-refresh headers. The refresh token should only be obtained via the /v1/refresh endpoint."),
		settings.WithOrder(101),
		settings.WithVisibleWhen("session.auto_refresh_enabled", true),
	)
)
View Source
var (
	// SettingJWTRequireActiveSession controls whether JWT tokens are cross-checked
	// against the session store to enable immediate revocation. On by default:
	// a JWT that cannot be revoked is a session that outlives a ban.
	SettingJWTRequireActiveSession = settings.Define("session.jwt_require_active_session", true,
		settings.WithDisplayName("Require Active Session for JWT"),
		settings.WithDescription("Cross-check JWT tokens against the session store to enable revocation"),
		settings.WithCategory("JWT Security"),
		settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp),
		settings.WithEnforceable(),
		settings.WithHelpText("On by default. JWT tokens are validated against the session store on each request, which adds a DB lookup but makes revocation and IP/device binding take effect at once. Turning it off makes a JWT valid until it expires, whatever happens to the session."),
		settings.WithOrder(55),
	)

	// SettingResourceIdentifier is the resource identifier this app answers to
	// as an OAuth2 resource server (RFC 8707). Empty disables the audience
	// check, which is the default and preserves existing behaviour.
	//
	// Per app rather than per deployment: two apps in one deployment are two
	// different resources, and a token minted for one must not authenticate at
	// the other.
	SettingResourceIdentifier = settings.Define("session.resource_identifier", "",
		settings.WithDisplayName("Resource Identifier"),
		settings.WithDescription("Absolute URI this app answers to as an OAuth2 resource server. Empty disables audience checking."),
		settings.WithCategory("JWT Security"),
		settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp),
		settings.WithEnforceable(),
		settings.WithInputType(formconfig.FieldText),
		settings.WithHelpText("When set, a token whose aud names a different resource is refused. Example: https://api.example.com"),
		settings.WithValidation(validateResourceIdentifier),
		settings.WithOrder(56),
	)
)
View Source
var (
	// SettingExtendOnActivity controls whether sessions are extended on each authenticated request.
	SettingExtendOnActivity = settings.Define("session.extend_on_activity", true,
		settings.WithDisplayName("Extend Session on Activity"),
		settings.WithDescription("Automatically extend session expiry on each authenticated request"),
		settings.WithCategory("Session Extension"),
		settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp),
		settings.WithEnforceable(),
		settings.WithHelpText("When enabled, each authenticated request resets the session expiry timer, implementing a sliding session window"),
		settings.WithOrder(105),
	)

	// SettingInactivityTimeoutSeconds controls how long a session lives without activity.
	SettingInactivityTimeoutSeconds = settings.Define("session.inactivity_timeout_seconds", 604800,
		settings.WithDisplayName("Inactivity Timeout (seconds)"),
		settings.WithDescription("Session expires after this many seconds of inactivity"),
		settings.WithCategory("Session Extension"),
		settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp),
		settings.WithInputType(formconfig.FieldNumber),
		settings.WithUIValidation(formconfig.Validation{Required: true, Min: new(60), Max: new(2592000)}),
		settings.WithHelpText("The session expiry is reset to now + this value on each request. Default: 604800 (7 days)"),
		settings.WithOrder(106),
		settings.WithVisibleWhen("session.extend_on_activity", true),
	)

	// SettingAbsoluteLifetimeSeconds bounds how long a session may live from
	// the moment it was issued, however active it is. The sliding window and
	// refresh both stop at this deadline.
	SettingAbsoluteLifetimeSeconds = settings.Define("session.absolute_lifetime_seconds", 2592000,
		settings.WithDisplayName("Absolute Session Lifetime (seconds)"),
		settings.WithDescription("A session ends this many seconds after it was issued, no matter how active it is"),
		settings.WithCategory("Session Extension"),
		settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp),
		settings.WithEnforceable(),
		settings.WithInputType(formconfig.FieldNumber),
		settings.WithUIValidation(formconfig.Validation{Required: true, Min: new(3600), Max: new(31536000)}),
		settings.WithHelpText("Activity extension and refresh never move a session past created_at + this value. Default: 2592000 (30 days)"),
		settings.WithOrder(107),
	)
)
View Source
var (
	// SettingCookieName controls the name of the httpOnly session cookie.
	SettingCookieName = settings.Define("session.cookie_name", "authsome_session_token",
		settings.WithDisplayName("Session Cookie Name"),
		settings.WithDescription("Name of the httpOnly session cookie set on sign-in"),
		settings.WithCategory("Cookie Configuration"),
		settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp),
		settings.WithEnforceable(),
		settings.WithInputType(formconfig.FieldText),
		settings.WithHelpText("The cookie name used to store the session token. Default: authsome_session_token"),
		settings.WithOrder(110),
	)

	// SettingCookieDomain controls the Domain attribute of the session cookie.
	SettingCookieDomain = settings.Define("session.cookie_domain", "",
		settings.WithDisplayName("Cookie Domain"),
		settings.WithDescription("Domain attribute for the session cookie. Empty means exact host only."),
		settings.WithCategory("Cookie Configuration"),
		settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp),
		settings.WithEnforceable(),
		settings.WithInputType(formconfig.FieldText),
		settings.WithHelpText("Leave empty to scope cookie to the exact host. Set to '.example.com' for cross-subdomain sharing."),
		settings.WithOrder(120),
	)

	// SettingCookiePath controls the Path attribute of the session cookie.
	SettingCookiePath = settings.Define("session.cookie_path", "/",
		settings.WithDisplayName("Cookie Path"),
		settings.WithDescription("Path attribute for the session cookie"),
		settings.WithCategory("Cookie Configuration"),
		settings.WithScopes(settings.ScopeGlobal),
		settings.WithInputType(formconfig.FieldText),
		settings.WithHelpText("URL path scope for the cookie. Default: / (all paths)"),
		settings.WithOrder(130),
	)

	// SettingCookieSecure controls whether the Secure flag is set on the session cookie.
	SettingCookieSecure = settings.Define("session.cookie_secure", true,
		settings.WithDisplayName("Secure Cookie"),
		settings.WithDescription("Only send cookie over HTTPS connections"),
		settings.WithCategory("Cookie Configuration"),
		settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp),
		settings.WithEnforceable(),
		settings.WithHelpText("When enabled, cookies are only sent over HTTPS. Automatically disabled for HTTP in development."),
		settings.WithOrder(140),
	)

	// SettingCookieHTTPOnly controls whether the HttpOnly flag is set on the session cookie.
	SettingCookieHTTPOnly = settings.Define("session.cookie_http_only", true,
		settings.WithDisplayName("HttpOnly Cookie"),
		settings.WithDescription("Prevent JavaScript from accessing the session cookie"),
		settings.WithCategory("Cookie Configuration"),
		settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp),
		settings.WithEnforceable(),
		settings.WithHelpText("When enabled, cookies are inaccessible to document.cookie (XSS protection). Strongly recommended."),
		settings.WithOrder(150),
	)

	// SettingCookieSameSite controls the SameSite attribute of the session cookie.
	SettingCookieSameSite = settings.Define("session.cookie_same_site", "lax",
		settings.WithDisplayName("SameSite Policy"),
		settings.WithDescription("SameSite attribute for cross-site request control"),
		settings.WithCategory("Cookie Configuration"),
		settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp),
		settings.WithEnforceable(),
		settings.WithInputType(formconfig.FieldSelect),
		settings.WithOptions(
			formconfig.SelectOption{Value: "strict", Label: "Strict"},
			formconfig.SelectOption{Value: "lax", Label: "Lax (Recommended)"},
			formconfig.SelectOption{Value: "none", Label: "None"},
		),
		settings.WithHelpText("Lax allows top-level navigations (OAuth redirects). Strict blocks all cross-site. None requires Secure=true."),
		settings.WithOrder(160),
	)

	// SettingCookieUseHostPrefix opts the session cookie into the
	// __Host- name prefix (RFC 6265bis §4.1.3.2). Browsers require any
	// __Host- cookie to be Secure, have no Domain attribute, and Path=/,
	// which closes the "sibling subdomain plants the cookie" attack on
	// cookie-based session hijacking.
	//
	// Disabled by default for back-compat: enabling renames the cookie
	// from "authsome_session_token" to "__Host-authsome_session_token"
	// (and similarly for any custom name), invalidating sessions from
	// before the toggle. Operators should enable in production AFTER
	// the dashboard surfaces the rename to clients/extensions.
	//
	// When enabled, this setting forces Secure=true and Domain="" on the
	// cookie regardless of the corresponding settings, because a __Host-
	// cookie that violates those is silently rejected by the browser.
	SettingCookieUseHostPrefix = settings.Define("session.cookie_use_host_prefix", false,
		settings.WithDisplayName("Use __Host- Cookie Prefix"),
		settings.WithDescription("Prepend __Host- to the session cookie name; forces Secure=true and Domain=\"\""),
		settings.WithCategory("Cookie Configuration"),
		settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp),
		settings.WithEnforceable(),
		settings.WithHelpText("RFC 6265bis prefix. Closes 'sibling subdomain plants the cookie' attacks on session hijacking. Renames the cookie when toggled — existing sessions become invalid until users sign in again."),
		settings.WithOrder(170),
	)
)
View Source
var ErrChildMintNotPermitted = errors.New("authsome: mint child: parent is not permitted to mint children")

ErrChildMintNotPermitted is returned when the parent itself is not eligible to mint a child at all: it is inactive, or it is itself an ephemeral child (one level only). No amount of narrowing the request fixes either case, so the API layer maps it to 403 rather than 400.

View Source
var ErrChildScopeExceedsParent = errors.New("authsome: mint child: requested scope exceeds the parent's own scopes")

ErrChildScopeExceedsParent is returned when a requested child scope falls outside the parent's own scopes. This is something the caller can fix by asking for less, so the API layer maps it to 400 rather than 403.

View Source
var ErrChronicleRequired = errors.New("authsome: a Chronicle audit trail is required; pass WithChronicle")

ErrChronicleRequired is returned by NewEngine when no audit trail was configured. Authsome does not run without one: every security-relevant action is recorded through Chronicle and there is no fallback sink.

View Source
var ErrDPoPBindingRequired = errors.New("authsome: this app requires a DPoP-bound session")

ErrDPoPBindingRequired reports a session refused because the app mandates DPoP (dpop.mode = required) and the issuing path resolved no thumbprint to bind it to.

This exists because enforcement follows the token, not the app: the middleware's enforceDPoP returns nil the moment it sees an empty thumbprint, so an unbound session is exempt from proof-of-possession for its whole life however the app is configured. A mint site that forgets to resolve a binding therefore doesn't produce a weaker session, it produces one the mandate never applies to at all. Refusing at the mint is the only place that distinction can still be made.

View Source
var ErrExchangeRefused = errors.New("authsome: exchange refused")

ErrExchangeRefused wraps every reason ExchangeToken declines to mint a session: no live grant, a wrong actor, a disabled actor, a scope the grant or the actor's own scopes do not carry, or a risk-plugin denial. All of it collapses to the same refusal from the caller's point of view, and to the same HTTP 403 at the API layer: this endpoint never creates authority, so every path that is not "the grant said yes" ends here rather than at a distinct error for each reason.

View Source
var ErrNotStarted = errors.New("authsome: engine not started, please wait for initialization to complete")

ErrNotStarted is returned when a service method is called before Start().

View Source
var ErrPlatformAppProtected = errors.New("authsome: the platform app cannot be deleted")

ErrPlatformAppProtected is returned when a caller tries to delete the platform app. That app hosts the platform owners themselves; removing it would cascade to every administrator and cannot be recovered from.

View Source
var ErrScopeEscalation = errors.New("authsome: requested scopes exceed the service account's scopes")

ErrScopeEscalation is returned when a credential would be minted with scopes wider than the identity it belongs to.

View Source
var ErrServiceAccountEnvironmentRequired = errors.New("authsome: service account environment is required to issue an api key")

ErrServiceAccountEnvironmentRequired is returned when a machine key has no persisted account environment to inherit.

View Source
var ErrTokenEncryptionKeyRequired = errors.New("authsome: token encryption key is required: set " +
	envTokenEncryptionKeys + " (kid:hex,kid:hex) or " + envTokenEncryptionKey +
	" (64 hex chars), or pass WithTokenEncryptor")

ErrTokenEncryptionKeyRequired is returned by NewEngine when no encryption key is configured and no Encryptor was passed. Provider tokens and SSO secrets are never written in the clear by accident: an operator who wants that in a development setup passes WithTokenEncryptor(bridge.NoopEncryptor{}).

View Source
var ErrWebhookEvents = errors.New("authsome: webhook events invalid")

ErrWebhookEvents is returned when a subscription names no event, or an event that is not shaped like one.

View Source
var ErrWebhookURLRejected = errors.New("authsome: webhook url rejected")

ErrWebhookURLRejected wraps a URL the safety check refused.

View Source
var ErrWebhooksUnavailable = errors.New("authsome: webhooks unavailable: the relay cannot manage endpoints")

ErrWebhooksUnavailable is returned by every webhook operation when the engine's relay cannot manage delivery endpoints (no relay, or a send-only one). A webhook that cannot deliver is not registered at all, so an operator never holds a subscription that silently does nothing.

View Source
var (
	// SettingRequireEmailVerification gates login on email-verified status.
	//
	// Default flipped from false to true in Phase 2A (2026-05) so signup-created
	// accounts can't be exploited before the user proves email ownership.
	// Existing app configs with an explicit override (true OR false) are
	// unchanged — only the default resolution changes. Apps that relied on
	// the previous false default and have unverified active users should
	// either:
	//   (a) set the global override to false during the rollout window, or
	//   (b) batch-verify their unverified user base before deploy.
	SettingRequireEmailVerification = settings.Define("auth.require_email_verification", true,
		settings.WithDisplayName("Require Email Verification"),
		settings.WithDescription("Block sign-in for accounts with unverified email addresses"),
		settings.WithCategory("Authentication"),
		settings.WithScopes(settings.ScopeGlobal, settings.ScopeApp),
		settings.WithEnforceable(),
		settings.WithHelpText("When enabled, users must verify their email before signing in."),
		settings.WithOrder(5),
	)
)

Functions ¶

func IsMFATicketNotFound ¶ added in v1.9.0

func IsMFATicketNotFound(err error) bool

IsMFATicketNotFound reports whether err indicates a missing or expired ticket. Hides the ceremony package from callers that don't otherwise depend on it.

func PluginIdentifierLimit ¶ added in v1.9.0

func PluginIdentifierLimit(ctx context.Context, engine any, scope string, appID id.AppID, identifier string, pick func(RateLimitConfig) int) error

PluginIdentifierLimit is AllowIdentifier for a plugin that holds the engine as plugin.Engine. pick selects the limit from the rate-limit config. It returns nil when the engine is not the concrete *Engine.

func PluginRateLimit ¶ added in v1.9.0

func PluginRateLimit(engine any, pick func(RateLimitConfig) int) []forge.RouteOption

PluginRateLimit returns forge route options that rate-limit an endpoint using the engine's limiter, or nil when rate limiting is off. Plugins use it so their routes share the engine's window and behaviour.

pick selects the limit from the engine's rate-limit config; a zero limit disables limiting for that route.

func PluginRateLimitMiddleware ¶ added in v1.9.0

func PluginRateLimitMiddleware(engine any, pick func(RateLimitConfig) int) forge.Middleware

PluginRateLimitMiddleware is RateLimitMiddleware for a plugin that holds the engine as plugin.Engine; nil when the engine is not the concrete *Engine or limiting is off.

func SessionCookieTemplate ¶ added in v1.9.0

func SessionCookieTemplate(ctx context.Context, mgr *settings.Manager, appID string, isHTTPS bool) *http.Cookie

SessionCookieTemplate resolves the dynamic cookie configuration for a given app and returns a *http.Cookie pre-populated with Name, Path, Domain, Secure, HttpOnly, and SameSite. The caller fills in Value and MaxAge.

Behaviour:

  • All five base attributes (name, domain, path, secure, http_only, same_site) come from the dynamic settings cascade — same as before.
  • When SettingCookieUseHostPrefix resolves to true, the cookie name is prefixed with "__Host-" (idempotent if already present), Domain is forced to empty, Path is forced to "/", and Secure is forced to true regardless of isHTTPS. These are the browser's prerequisites for accepting a __Host-prefixed cookie; relaxing any of them silently breaks session persistence.
  • When the prefix is disabled, isHTTPS gates the Secure attribute (so dev HTTP doesn't break) and Domain is whatever the setting resolves to.

Pass appID="" to resolve global-only.

func UnwrapStore ¶ added in v1.9.0

func UnwrapStore(s any) any

UnwrapStore returns the backend behind whatever decorators the engine installed over it, or s itself when there are none.

Takes and returns any rather than store.Store because the engine hands the same backend out through several narrower interfaces (APIKeyStore returns an apikey.Store, for one), and every one of them can be sitting behind the same decorator.

Types ¶

type AdminUserUpdates ¶ added in v1.9.0

type AdminUserUpdates struct {
	FirstName     *string
	LastName      *string
	Username      *string
	EmailVerified *bool
}

AdminUserUpdates holds optional fields for an admin user update.

type Argon2Config ¶ added in v1.9.0

type Argon2Config struct {
	Memory      uint32 `json:"memory"`      // KiB (default: 65536 = 64 MiB)
	Iterations  uint32 `json:"iterations"`  // Time cost (default: 3)
	Parallelism uint8  `json:"parallelism"` // Threads (default: 2)
	SaltLength  uint32 `json:"salt_length"` // Bytes (default: 16)
	KeyLength   uint32 `json:"key_length"`  // Bytes (default: 32)
}

Argon2Config holds Argon2id parameters.

type BootstrapConfig ¶ added in v1.9.0

type BootstrapConfig struct {
	// Platform app settings.
	AppName string // default: "Platform"
	AppSlug string // default: "platform"

	// Default environments to create for every new app.
	Environments []BootstrapEnv

	// Whether to skip creating default environments.
	SkipDefaultEnvs bool

	// Whether to skip applying the default warden DSL roles + permission catalog.
	SkipDefaultRoles bool

	// SeedOptions configures the warden DSL seed apply path. Use this to
	// override the embedded .warden files (e.g. with a custom directory
	// shipped via cfg.WardenDir, or a custom fs.FS).
	SeedOptions wardenseed.ApplyOptions

	// InitialOwners is a list of email addresses that receive the
	// platform-owner role once they verify that address, regardless of how
	// many owners already exist. Comparison is case-insensitive.
	InitialOwners []string

	// InitialOwnerCount is the number of platform-owner slots claimed by
	// bootstrap. While fewer owners than this exist on the platform app, the
	// next user to verify their email is promoted. Defaults to 1. Set to 0
	// to disable slot-based promotion entirely (only InitialOwners emails
	// are promoted).
	InitialOwnerCount int

	// Callback for custom post-bootstrap logic.
	OnBootstrap func(ctx context.Context, e *Engine, appID id.AppID) error
}

BootstrapConfig holds settings for automatic platform app setup.

func DefaultBootstrapConfig ¶ added in v1.9.0

func DefaultBootstrapConfig() *BootstrapConfig

DefaultBootstrapConfig returns sensible defaults. Roles + permissions are sourced from the embedded warden DSL files (see bootstrap/warden/embed), not from this struct.

type BootstrapEnv ¶ added in v1.9.0

type BootstrapEnv struct {
	Name      string
	Slug      string
	Type      environment.Type
	IsDefault bool
}

BootstrapEnv describes an environment to auto-create.

type BootstrapOption ¶ added in v1.9.0

type BootstrapOption func(*BootstrapConfig)

BootstrapOption configures the bootstrap system.

func WithBootstrapAppLogo(logo string) BootstrapOption

WithBootstrapAppLogo sets the platform app logo URL.

func WithBootstrapAppName ¶ added in v1.9.0

func WithBootstrapAppName(name string) BootstrapOption

WithBootstrapAppName sets the platform app name.

func WithBootstrapAppSlug ¶ added in v1.9.0

func WithBootstrapAppSlug(slug string) BootstrapOption

WithBootstrapAppSlug sets the platform app slug.

func WithBootstrapEnvs ¶ added in v1.9.0

func WithBootstrapEnvs(envs []BootstrapEnv) BootstrapOption

WithBootstrapEnvs overrides the default environments to create.

func WithBootstrapWardenDir ¶ added in v1.9.0

func WithBootstrapWardenDir(dir string) BootstrapOption

WithBootstrapWardenDir tells bootstrap to load .warden files from the given directory instead of the embedded defaults. The directory must contain a `shared/` subtree (applied to every app); a `platform/` subtree is consulted only for the platform app and is optional.

func WithBootstrapWardenSources ¶ added in v1.9.0

func WithBootstrapWardenSources(shared, platform *wardenseed.Source) BootstrapOption

WithBootstrapWardenSources lets advanced callers replace the embedded .warden sources with custom ones (e.g. their own fs.FS or directory). Either argument may be nil to fall back to the embedded default for that scope.

func WithInitialOwnerCount ¶ added in v1.9.0

func WithInitialOwnerCount(n int) BootstrapOption

WithInitialOwnerCount sets how many platform-owner slots bootstrap fills from verified sign-ups on the platform app. The default is 1. Pass 0 to disable slot-based promotion entirely.

func WithInitialOwners ¶ added in v1.9.0

func WithInitialOwners(emails ...string) BootstrapOption

WithInitialOwners registers email addresses that receive the platform-owner role once verified, regardless of how many owners already exist. Comparison is case-insensitive. The option is additive: calling it multiple times appends to the existing list.

func WithOnBootstrap ¶ added in v1.9.0

func WithOnBootstrap(fn func(ctx context.Context, e *Engine, appID id.AppID) error) BootstrapOption

WithOnBootstrap sets a callback invoked after the platform app is created.

func WithSkipDefaultEnvs ¶ added in v1.9.0

func WithSkipDefaultEnvs() BootstrapOption

WithSkipDefaultEnvs disables automatic environment creation.

func WithSkipDefaultRoles ¶ added in v1.9.0

func WithSkipDefaultRoles() BootstrapOption

WithSkipDefaultRoles disables automatic role + permission seeding via the warden DSL.

type BulkError ¶ added in v1.9.0

type BulkError struct {
	Index int    `json:"index"`
	Email string `json:"email,omitempty"`
	Error string `json:"error"`
}

BulkError records an error for a single item in a bulk operation.

type BulkImportResult ¶ added in v1.9.0

type BulkImportResult struct {
	Created int         `json:"created"`
	Skipped int         `json:"skipped"`
	Errors  []BulkError `json:"errors,omitempty"`
}

BulkImportResult holds the results of a bulk user import operation.

type CSRFConfig ¶ added in v1.9.0

type CSRFConfig struct {
	// Enabled turns the check on. Unset means on: an explicit false is the
	// only way to switch it off, and session.cookie_same_site=none is
	// refused while it is off.
	Enabled *bool `json:"enabled,omitempty"`

	// AllowedOrigins are origins (scheme://host[:port]) whose cross-site
	// cookie requests are accepted, for a first-party front end served
	// from another host.
	AllowedOrigins []string `json:"allowed_origins"`
}

CSRFConfig governs the check that keeps another site from riding a session cookie: an unsafe request (anything but GET, HEAD, OPTIONS, TRACE) authenticated by cookie must come from the same site, or from an origin listed here. Bearer, DPoP and API-key requests are not affected.

func (CSRFConfig) IsEnabled ¶ added in v1.9.0

func (c CSRFConfig) IsEnabled() bool

IsEnabled reports whether the cross-site check runs.

func (CSRFConfig) ToMiddleware ¶ added in v1.9.0

func (c CSRFConfig) ToMiddleware() middleware.CSRFConfig

ToMiddleware is the middleware's view of this configuration.

type ClientConfigBranding ¶ added in v1.9.0

type ClientConfigBranding struct {
	AppName string `json:"app_name,omitempty"`
	LogoURL string `json:"logo_url,omitempty"`
}

ClientConfigBranding holds app branding information.

type ClientConfigCaptcha ¶ added in v1.9.0

type ClientConfigCaptcha struct {
	Required bool   `json:"required"`
	Provider string `json:"provider,omitempty"`
	SiteKey  string `json:"site_key,omitempty"`
}

ClientConfigCaptcha tells the frontend whether — and how — to render a captcha challenge before submitting auth forms. Mirrors the auth.captcha_* settings; only public-safe fields are exposed (the secret_key is intentionally NOT included).

type ClientConfigEmailVerification ¶ added in v1.9.0

type ClientConfigEmailVerification struct {
	Enabled  bool `json:"enabled"`
	Required bool `json:"required"`
}

ClientConfigEmailVerification represents email verification settings.

type ClientConfigFieldValidation ¶ added in v1.9.0

type ClientConfigFieldValidation struct {
	Required bool   `json:"required,omitempty"`
	MinLen   int    `json:"min_len,omitempty"`
	MaxLen   int    `json:"max_len,omitempty"`
	Pattern  string `json:"pattern,omitempty"`
	Min      *int   `json:"min,omitempty"`
	Max      *int   `json:"max,omitempty"`
}

ClientConfigFieldValidation defines validation rules for a signup field.

type ClientConfigMFA ¶ added in v1.9.0

type ClientConfigMFA struct {
	Enabled  bool     `json:"enabled"`
	Required bool     `json:"required"`
	Methods  []string `json:"methods"`
}

ClientConfigMFA represents the MFA configuration.

type ClientConfigResponse ¶ added in v1.9.0

type ClientConfigResponse struct {
	Version             string                         `json:"version"`
	AppID               string                         `json:"app_id"`
	SignupEnabled       bool                           `json:"signup_enabled"`
	Branding            *ClientConfigBranding          `json:"branding,omitempty"`
	Password            *ClientConfigToggle            `json:"password,omitempty"`
	Social              *ClientConfigSocial            `json:"social,omitempty"`
	Passkey             *ClientConfigToggle            `json:"passkey,omitempty"`
	MFA                 *ClientConfigMFA               `json:"mfa,omitempty"`
	MagicLink           *ClientConfigToggle            `json:"magiclink,omitempty"`
	SSO                 *ClientConfigSSO               `json:"sso,omitempty"`
	EmailVerification   *ClientConfigEmailVerification `json:"email_verification,omitempty"`
	DeviceAuthorization *ClientConfigToggle            `json:"device_authorization,omitempty"`
	Waitlist            *ClientConfigToggle            `json:"waitlist,omitempty"`
	Captcha             *ClientConfigCaptcha           `json:"captcha,omitempty"`
	SupportedPlugins    []string                       `json:"supported_plugins"`
	SignupFields        []ClientConfigSignupField      `json:"signup_fields,omitempty"`
}

ClientConfigResponse is the public client-facing configuration returned by the /client-config endpoint. It tells the frontend SDK which auth methods are enabled, available providers, branding, etc.

type ClientConfigSSO ¶ added in v1.9.0

type ClientConfigSSO struct {
	Enabled     bool                        `json:"enabled"`
	Connections []ClientConfigSSOConnection `json:"connections"`
}

ClientConfigSSO represents the SSO configuration.

type ClientConfigSSOConnection ¶ added in v1.9.0

type ClientConfigSSOConnection struct {
	ID   string `json:"id"`
	Name string `json:"name"`
}

ClientConfigSSOConnection represents an SSO connection/provider.

type ClientConfigSelectOption ¶ added in v1.9.0

type ClientConfigSelectOption struct {
	Label string `json:"label"`
	Value string `json:"value"`
}

ClientConfigSelectOption represents a select/radio option.

type ClientConfigSignupField ¶ added in v1.9.0

type ClientConfigSignupField struct {
	Key         string                       `json:"key"`
	Label       string                       `json:"label"`
	Type        string                       `json:"type"`
	Placeholder string                       `json:"placeholder,omitempty"`
	Description string                       `json:"description,omitempty"`
	Options     []ClientConfigSelectOption   `json:"options,omitempty"`
	Default     string                       `json:"default,omitempty"`
	Validation  *ClientConfigFieldValidation `json:"validation,omitempty"`
	Order       int                          `json:"order"`
}

ClientConfigSignupField describes a custom signup form field.

type ClientConfigSocial ¶ added in v1.9.0

type ClientConfigSocial struct {
	Enabled   bool                         `json:"enabled"`
	Providers []ClientConfigSocialProvider `json:"providers"`
}

ClientConfigSocial represents the social auth configuration.

type ClientConfigSocialProvider ¶ added in v1.9.0

type ClientConfigSocialProvider struct {
	ID   string `json:"id"`
	Name string `json:"name"`
}

ClientConfigSocialProvider represents a social login provider.

type ClientConfigToggle ¶ added in v1.9.0

type ClientConfigToggle struct {
	Enabled bool `json:"enabled"`
}

ClientConfigToggle represents a simple enabled/disabled auth method.

type Config ¶

type Config struct {
	// AppID is the forge.Scope app identity.
	AppID string `json:"app_id"`

	// BasePath is the URL prefix for all auth routes (default: "/authsome").
	BasePath string `json:"base_path"`

	// Session configuration
	Session SessionConfig `json:"session"`

	// Password policy
	Password PasswordConfig `json:"password"`

	// Rate limiting per endpoint
	RateLimit RateLimitConfig `json:"rate_limit"`

	// Account lockout after failed attempts
	Lockout LockoutConfig `json:"lockout"`

	// DriverName is the grove driver name ("pg", "sqlite", "mongo", "memory").
	// Set automatically by the extension based on the detected grove driver.
	DriverName string `json:"driver_name"`

	// Debug enables verbose logging of all operations.
	Debug bool `json:"debug"`

	// DisableRoutes prevents automatic route registration.
	DisableRoutes bool `json:"disable_routes"`

	// DisableMigrate prevents automatic database migration on Start.
	DisableMigrate bool `json:"disable_migrate"`

	// ProtectedResourceMetadataURL is the RFC 9728 metadata URL advertised in
	// the WWW-Authenticate header on a 401. Empty means no hint is emitted.
	ProtectedResourceMetadataURL string `json:"protected_resource_metadata_url"`

	// TokenEncryption configures at-rest encryption of provider tokens, MFA
	// secrets and SSO connection secrets. A key is required unless
	// WithTokenEncryptor is passed; see AUTHSOME_TOKEN_ENCRYPTION_KEYS.
	TokenEncryption TokenEncryptionConfig `json:"token_encryption"`

	// APIKeyPepper is a server-side secret mixed into API key digests, so a
	// copy of the database cannot be checked against a guessed key without
	// it. Falls back to AUTHSOME_API_KEY_PEPPER. Keys hashed before a pepper
	// was set keep verifying and are rewritten on their next use.
	APIKeyPepper string `json:"api_key_pepper"`

	// Retention sets how long expired rows are kept before the sweeper that
	// runs every Session.CleanupInterval removes them.
	Retention RetentionConfig `json:"retention"`

	// Webhooks governs how webhook URLs are checked before a Relay endpoint
	// is created for them.
	Webhooks WebhookConfig `json:"webhooks"`

	// CSRF governs the cross-site check on cookie-authenticated writes.
	CSRF CSRFConfig `json:"csrf"`
}

Config holds all configuration for the AuthSome engine.

func DefaultConfig ¶ added in v1.9.0

func DefaultConfig() Config

DefaultConfig returns a Config with sensible defaults.

type DPoPRequiredError ¶ added in v1.9.0

type DPoPRequiredError struct{}

DPoPRequiredError renders ErrDPoPBindingRequired over HTTP in the shape RFC 9449 clients already watch for, matching what the OAuth2 token endpoint returns when a required-mode client presents no proof.

func (*DPoPRequiredError) Error ¶ added in v1.9.0

func (*DPoPRequiredError) Error() string

Error returns the underlying sentinel's message.

func (*DPoPRequiredError) ResponseBody ¶ added in v1.9.0

func (*DPoPRequiredError) ResponseBody() any

ResponseBody returns the JSON envelope the client receives.

func (*DPoPRequiredError) StatusCode ¶ added in v1.9.0

func (*DPoPRequiredError) StatusCode() int

StatusCode lets forge render this directly, without every plugin handler needing its own mapping.

func (*DPoPRequiredError) Unwrap ¶ added in v1.9.0

func (*DPoPRequiredError) Unwrap() error

Unwrap exposes the sentinel for errors.Is checks.

type Engine ¶ added in v1.9.0

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

Engine is the core AuthSome orchestrator. It holds only interfaces — no concrete implementations — for maximum pluggability.

func NewEngine ¶ added in v1.9.0

func NewEngine(opts ...Option) (*Engine, error)

NewEngine creates a new AuthSome engine with the given options.

func (*Engine) APIKeyStore ¶ added in v1.9.0

func (e *Engine) APIKeyStore() apikey.Store

APIKeyStore returns the API key store. When Keysmith is available, operations delegate to Keysmith's full key management engine (gaining rate limiting, policy enforcement, key rotation, scope management, and usage tracking). Otherwise, it returns the composite store's API key methods.

func (*Engine) AbsoluteLifetimeFor ¶ added in v1.9.0

func (e *Engine) AbsoluteLifetimeFor(ctx context.Context, appID id.AppID) time.Duration

AbsoluteLifetimeFor resolves session.absolute_lifetime_seconds for an app. Zero means no absolute deadline, which only an explicit setting of zero produces; the default is thirty days.

func (*Engine) AddPermission ¶ added in v1.9.0

func (e *Engine) AddPermission(ctx context.Context, appID id.AppID, p *rbac.Permission) error

AddPermission adds a permission to one of the app's roles.

func (*Engine) AdminBanUser ¶ added in v1.9.0

func (e *Engine) AdminBanUser(ctx context.Context, adminID, userID id.UserID, reason string, expiresAt *time.Time) error

AdminBanUser bans a user account. Optionally sets ban reason and expiration.

func (*Engine) AdminBulkImportUsers ¶ added in v1.9.0

func (e *Engine) AdminBulkImportUsers(ctx context.Context, adminID id.UserID, users []*user.User) (*BulkImportResult, error)

AdminBulkImportUsers creates multiple users in a single operation. Users that already exist (duplicate email or username) are skipped. The operation is best-effort: individual failures don't abort the entire import.

func (*Engine) AdminBulkRevokeSessions ¶ added in v1.9.0

func (e *Engine) AdminBulkRevokeSessions(ctx context.Context, adminID, userID id.UserID) (int, error)

AdminBulkRevokeSessions revokes all sessions for a specific user. Returns the number of sessions that were revoked.

func (*Engine) AdminCopyUserToApp ¶ added in v1.9.0

func (e *Engine) AdminCopyUserToApp(ctx context.Context, adminID, sourceUserID id.UserID, targetAppID id.AppID, envID id.EnvironmentID) (*user.User, error)

AdminCopyUserToApp creates a new user record in targetAppID by reusing the source user's email, profile fields, and stored password hash — so the imported user can authenticate with their original password without ever exposing the hash outside the engine. Returns account.ErrEmailTaken if the target app already has a user with the same email (caller can treat as idempotent), or a wrapped store error otherwise.

envID is resolved to the target app's default environment when callers pass a zero EnvironmentID.

func (*Engine) AdminCreateUser ¶ added in v1.9.0

func (e *Engine) AdminCreateUser(ctx context.Context, adminID id.UserID, appID id.AppID, envID id.EnvironmentID, email, password, firstName, lastName, username string) (*user.User, error)

AdminCreateUser creates a new user without going through the signup flow. The user is created with the given password (hashed) and email marked as verified.

func (*Engine) AdminDeleteUser ¶ added in v1.9.0

func (e *Engine) AdminDeleteUser(ctx context.Context, adminID, userID id.UserID) error

AdminDeleteUser permanently deletes a user and all associated data.

func (*Engine) AdminGetUser ¶ added in v1.9.0

func (e *Engine) AdminGetUser(ctx context.Context, userID id.UserID) (*user.User, error)

AdminGetUser returns a user by ID (admin access — no ownership check).

func (*Engine) AdminListUsers ¶ added in v1.9.0

func (e *Engine) AdminListUsers(ctx context.Context, q *user.Query) (*user.List, error)

AdminListUsers returns a paginated list of users for the given app.

func (*Engine) AdminUnbanUser ¶ added in v1.9.0

func (e *Engine) AdminUnbanUser(ctx context.Context, adminID, userID id.UserID) error

AdminUnbanUser removes a ban from a user account.

func (*Engine) AdminUnlockUser ¶ added in v1.9.0

func (e *Engine) AdminUnlockUser(ctx context.Context, adminID, userID id.UserID) error

AdminUnlockUser clears the lockout counters and locks recorded against the user's email and username, on every client network, and records the action on the trail.

func (*Engine) AdminUpdateUser ¶ added in v1.9.0

func (e *Engine) AdminUpdateUser(ctx context.Context, adminID, userID id.UserID, updates AdminUserUpdates) error

AdminUpdateUser updates a user's profile fields. Only non-nil pointer fields in the updates struct are applied.

func (*Engine) AllowIdentifier ¶ added in v1.9.0

func (e *Engine) AllowIdentifier(ctx context.Context, scope string, appID id.AppID, identifier string, limit int) error

AllowIdentifier counts an attempt against an identifier (an email, a username, a phone number) within an app and scope, and returns account.ErrRateLimited once the limit for the window is spent.

The route limiters key on the client address, which a distributed attacker rotates. This second limiter keys on the target instead, so one account cannot be hammered from many addresses. The identifier is hashed before it becomes a key, so the shared store never holds an address in the clear. A zero limit, a disabled limiter or no limiter at all lets the attempt through; a limiter error follows the fail-open setting.

func (*Engine) AssignUserRole ¶ added in v1.9.0

func (e *Engine) AssignUserRole(ctx context.Context, appID id.AppID, ur *rbac.UserRole) error

AssignUserRole assigns one of the app's roles to a user. A role owned by another app is refused as not found.

func (*Engine) AuthMiddleware ¶ added in v1.9.0

func (e *Engine) AuthMiddleware() forge.Middleware

AuthMiddleware returns the engine's fully-configured non-blocking authentication middleware (cookie bridge + JWT + strategies). Populates user context when a valid token is present, passes through otherwise. Applied globally by the extension.

It never returns nil. The real middleware is built by InitPlugins (which Start calls). Ask for it before that and you get a middleware that looks the real one up on every request, so you can mount it while you wire your router and it starts authenticating once the engine is initialized. Until then it passes requests through unauthenticated, which routes that need a signed-in user already refuse.

func (*Engine) AuthRegistry ¶ added in v1.9.0

func (e *Engine) AuthRegistry() auth.Registry

AuthRegistry returns the forge auth provider registry. Plugins can register their own auth providers and create middleware via registry.Middleware().

func (*Engine) Authorizer ¶ added in v1.9.0

func (e *Engine) Authorizer() bridge.Authorizer

Authorizer returns the authorization bridge (may be nil).

func (*Engine) BasePath ¶ added in v1.9.0

func (e *Engine) BasePath() string

BasePath returns the URL prefix for auth routes.

func (*Engine) Can ¶ added in v1.9.0

func (e *Engine) Can(
	ctx context.Context, subject principal.Ref, actors principal.Chain, action, resource string,
) (bool, error)

Can reports whether subject may perform action on resource, given that actors are acting on the subject's behalf.

With no actors this is a single Warden check and behaves exactly as HasPermission always has. With actors, every party must allow: the subject, and each hop of the chain. Delegation can only narrow. The first denial short-circuits, so a denied agent costs one check rather than the whole chain.

Impersonation does not reach here with a populated chain. Session.AuthzActors returns nil for it, because impersonating somebody is the request to evaluate as them.

func (*Engine) CeremonyStore ¶ added in v1.9.0

func (e *Engine) CeremonyStore() ceremony.Store

CeremonyStore returns the ceremony state store for short-lived auth ceremony sessions. Returns an in-memory store if none was configured.

func (*Engine) ChangePassword ¶ added in v1.9.0

func (e *Engine) ChangePassword(ctx context.Context, userID id.UserID, currentPassword, newPassword string) error

ChangePassword changes a user's password (requires current password).

func (*Engine) Chronicle ¶ added in v1.9.0

func (e *Engine) Chronicle() bridge.Chronicle

Chronicle returns the audit trail bridge (may be nil).

func (*Engine) ClientConfig ¶ added in v1.9.0

func (e *Engine) ClientConfig(ctx context.Context, appID id.AppID) *ClientConfigResponse

ClientConfig returns the merged client-facing configuration for an app. This aggregates plugin-level defaults with per-app overrides.

func (*Engine) CloneEnvironment ¶ added in v1.9.0

func (e *Engine) CloneEnvironment(ctx context.Context, req environment.CloneRequest) (*environment.CloneResult, error)

CloneEnvironment clones an environment's config and structure (roles, permissions, webhooks) into a new environment. User data is NOT cloned.

func (*Engine) Config ¶ added in v1.9.0

func (e *Engine) Config() Config

Config returns the engine configuration.

func (*Engine) ConsumeDummyHash ¶ added in v1.9.0

func (e *Engine) ConsumeDummyHash(password string) account.PasswordPolicy

ConsumeDummyHash runs the configured password hash over password and discards the result, so a code path that has no stored hash to compare against (an unknown identifier at sign-in, a duplicate address at sign-up) takes as long as one that does. Without it the response time tells a caller whether the identifier exists. It returns the policy it hashed with so a test can check the cost matches a real check.

func (*Engine) ConsumeDummyHashContext ¶ added in v1.9.0

func (e *Engine) ConsumeDummyHashContext(ctx context.Context, password string) account.PasswordPolicy

ConsumeDummyHashContext is ConsumeDummyHash for sign-up's duplicate-address path. It hashes under the same hash password span, with the same algorithm attribute, and records into the same histogram as a real sign-up's hash, so the duplicate looks like a fresh sign-up in a trace and on a metric as well as in the response time.

func (*Engine) ConsumeMFATicket ¶ added in v1.9.0

func (e *Engine) ConsumeMFATicket(ctx context.Context, ticket string) error

ConsumeMFATicket deletes a ticket so it cannot be replayed.

func (*Engine) CreateApp ¶ added in v1.9.0

func (e *Engine) CreateApp(ctx context.Context, a *app.App) error

CreateApp creates a new application with default environments and roles.

func (*Engine) CreateEnvironment ¶ added in v1.9.0

func (e *Engine) CreateEnvironment(ctx context.Context, env *environment.Environment) error

CreateEnvironment creates a new environment for an app.

func (*Engine) CreateRole ¶ added in v1.9.0

func (e *Engine) CreateRole(ctx context.Context, r *rbac.Role) error

CreateRole creates a new RBAC role.

func (*Engine) CreateServiceAccount ¶ added in v1.9.0

func (e *Engine) CreateServiceAccount(ctx context.Context, appID id.AppID, name, description string, scopes []string) (*serviceaccount.ServiceAccount, error)

CreateServiceAccount creates a new service account for an app.

func (*Engine) CreateServiceAccountAPIKey ¶ added in v1.9.0

func (e *Engine) CreateServiceAccountAPIKey(ctx context.Context, svcAcctID id.ServiceAccountID, name string, scopes []string, expiresAt *time.Time) (*apikey.APIKey, string, error)

CreateServiceAccountAPIKey mints an API key bound to a service account (not a user). Returns the persisted APIKey and the plaintext secret (only returned once — not stored).

func (*Engine) CreateServiceAccountInEnvironment ¶ added in v1.9.0

func (e *Engine) CreateServiceAccountInEnvironment(ctx context.Context, appID id.AppID, envID id.EnvironmentID, name, description string, scopes []string) (*serviceaccount.ServiceAccount, error)

CreateServiceAccountInEnvironment creates an account bound to a persisted environment in the app. Its API keys inherit that binding.

func (*Engine) CreateWebhook ¶ added in v1.9.0

func (e *Engine) CreateWebhook(ctx context.Context, w *webhook.Webhook) error

CreateWebhook registers w as a delivering Relay endpoint. The URL must be public and answer a signed test delivery; the events must be shaped like event types. On return w.Secret holds the signing secret, the only time it is shown; the row keeps its hash and the Relay endpoint id.

func (*Engine) DB ¶ added in v1.9.0

func (e *Engine) DB() *grove.DB

DB returns the database handle so plugins can create their own persistent stores. Returns nil if not set.

func (*Engine) DPoPBindingConfig ¶ added in v1.9.0

func (e *Engine) DPoPBindingConfig() middleware.SessionBindingConfig

DPoPBindingConfig returns the RFC 9449 enforcement wiring, for every auth path that resolves a token and therefore has to honour a binding. The auth middleware is one such path and the registered session auth provider is another; anyone assembling their own chain gets the same config from here rather than reassembling one that drifts.

The validator is always non-nil (initDPoP builds it unconditionally); the nonce signer is nil until a secret is derivable, which DPoPNonceRequiredForApp already accounts for by refusing to demand nonces it cannot mint.

func (*Engine) DPoPBindingForRequest ¶ added in v1.9.0

func (e *Engine) DPoPBindingForRequest(ctx forge.Context, appID id.AppID) (string, error)

DPoPBindingForRequest validates any DPoP proof on a first-party auth request and returns the thumbprint to bind the new session to. An empty string with a nil error means "no binding, and that is allowed here".

Mirrors the OAuth2 plugin's bindDPoP. There is no OAuth client in a first-party flow, so only the app's mode applies.

This lives on the Engine rather than in the api package because every issuing path needs it: the first-party handlers, and the magic-link, passkey, phone and MFA plugins, none of which can reach an unexported api method. Seven copies of proof validation is seven chances to get one of them subtly wrong.

func (*Engine) DPoPBindingUnavailable ¶ added in v1.9.0

func (e *Engine) DPoPBindingUnavailable(ctx context.Context, appID id.AppID) error

DPoPBindingUnavailable returns a refusal when appID mandates DPoP but the caller structurally cannot obtain a proof.

The IdP-redirect callbacks are the case: at a social or SSO callback the request is the identity provider redirecting the user agent, so the client holding the DPoP key is not the caller and has no opportunity to present anything. IssueSession's gate would refuse these anyway, but only after the authorization code had been redeemed and burned. Calling this first turns a consumed single-use artifact into a clean error.

The refusal is deliberate rather than an exemption: an app on required is stating a mandate, and a path that quietly issued unbound sessions would let anyone who can pick the sign-in method opt out of it. An operator who needs social or SSO on such an app lowers the app to optional, which is explicit, visible and audited.

func (*Engine) DPoPModeForApp ¶ added in v1.9.0

func (e *Engine) DPoPModeForApp(ctx context.Context, appID id.AppID) dpop.Mode

DPoPModeForApp resolves the effective mode for an app, before any per-client value is folded in with dpop.MaxMode.

func (*Engine) DPoPNonceRequiredForApp ¶ added in v1.9.0

func (e *Engine) DPoPNonceRequiredForApp(ctx context.Context, appID id.AppID) bool

DPoPNonceRequiredForApp reports whether an app demands a nonce.

It used to answer false whenever the signer was nil, on the reasoning that demanding a nonce we cannot mint locks every client out of the app. True, and it is also what an operator asked for. Answering false turned an explicit security control off and told nobody, which is the one outcome a security control must never have: one Warn line at startup, no enforcement, and a dashboard switch that reads as on.

So the misconfiguration fails closed. Nonces stay required, the requests that would have gone unprotected are refused instead, and the reason goes in the log at error level with the app named. Locking clients out is loud and fixable in one config change. Silently not enforcing is neither.

Nothing here fires unless somebody set dpop.nonce_required, which defaults to false.

func (*Engine) DPoPNonceSigner ¶ added in v1.9.0

func (e *Engine) DPoPNonceSigner() *dpop.NonceSigner

DPoPNonceSigner returns the nonce signer, or nil when no secret could be derived.

func (*Engine) DPoPValidator ¶ added in v1.9.0

func (e *Engine) DPoPValidator() *dpop.Validator

DPoPValidator returns the engine's proof validator. Never nil after construction.

func (*Engine) DefaultAppID ¶ added in v1.9.0

func (e *Engine) DefaultAppID() string

DefaultAppID returns the configured app ID string.

func (*Engine) DefaultTokenFormat ¶ added in v1.9.0

func (e *Engine) DefaultTokenFormat() tokenformat.Format

DefaultTokenFormat returns the default token format (may be nil).

func (*Engine) DeleteAccount ¶ added in v1.9.0

func (e *Engine) DeleteAccount(ctx context.Context, userID id.UserID) error

DeleteAccount performs a self-service account deletion (GDPR right to erasure). This soft-deletes the user, revokes all sessions, and fires cascade cleanup via plugin hooks.

func (*Engine) DeleteApp ¶ added in v1.9.0

func (e *Engine) DeleteApp(ctx context.Context, appID id.AppID) error

DeleteApp removes an application.

func (*Engine) DeleteDevice ¶ added in v1.9.0

func (e *Engine) DeleteDevice(ctx context.Context, deviceID id.DeviceID) error

DeleteDevice removes a device.

func (*Engine) DeleteEnvironment ¶ added in v1.9.0

func (e *Engine) DeleteEnvironment(ctx context.Context, envID id.EnvironmentID) error

DeleteEnvironment removes an environment.

func (*Engine) DeleteOrgBranding ¶ added in v1.9.0

func (e *Engine) DeleteOrgBranding(ctx context.Context, orgID id.OrgID) error

DeleteOrgBranding deletes the branding config for an organization.

func (*Engine) DeleteRole ¶ added in v1.9.0

func (e *Engine) DeleteRole(ctx context.Context, appID id.AppID, roleID id.RoleID) error

DeleteRole deletes an app's RBAC role and cascades to its permissions and assignments.

func (*Engine) DeleteServiceAccount ¶ added in v1.9.0

func (e *Engine) DeleteServiceAccount(ctx context.Context, svcID id.ServiceAccountID) error

DeleteServiceAccount deletes a service account by ID.

func (*Engine) DeleteSignupFormConfig ¶ added in v1.9.0

func (e *Engine) DeleteSignupFormConfig(ctx context.Context, appID id.AppID) error

DeleteSignupFormConfig deletes the signup form config for an app.

func (*Engine) DeleteWebhook ¶ added in v1.9.0

func (e *Engine) DeleteWebhook(ctx context.Context, webhookID id.WebhookID) error

DeleteWebhook removes the Relay endpoint, then the row. A row with no endpoint (not adopted yet) is simply removed.

func (*Engine) Dispatcher ¶ added in v1.9.0

func (e *Engine) Dispatcher() bridge.Dispatcher

Dispatcher returns the job queue bridge (may be nil).

func (*Engine) EmailVerificationRequired ¶ added in v1.9.0

func (e *Engine) EmailVerificationRequired(ctx context.Context, appID id.AppID) bool

EmailVerificationRequired reports whether an account in appID must verify its email before it gets a session. Sign-up, sign-in, IssueSession and the client config all ask this, so the UI and the server never disagree.

Resolution order (first match wins):

  1. Dynamic setting auth.require_email_verification, when it says true
  2. Per-app client config override (RequireEmailVerification)
  3. Environment setting (SkipEmailVerification, inverted, with type defaults merged)
  4. Default: no enforcement

func (*Engine) EnsureDefaultRole ¶ added in v1.9.0

func (e *Engine) EnsureDefaultRole(ctx context.Context, appID id.AppID, userID id.UserID)

EnsureDefaultRole assigns the default Warden role to a user if they don't already have one. For the platform app this is "platform_user"; for regular apps it is "user". Errors are silently ignored to avoid blocking user creation.

func (*Engine) EnsureMigrated ¶ added in v1.9.0

func (e *Engine) EnsureMigrated(ctx context.Context) error

EnsureMigrated runs store migrations (core + plugin groups) if not disabled. This is idempotent — safe to call multiple times. Use this to ensure tables exist before the engine is fully started (e.g. during extension init).

func (*Engine) ExchangeToken ¶ added in v1.9.0

func (e *Engine) ExchangeToken(ctx context.Context, req *ExchangeRequest) (*session.Session, error)

ExchangeToken mints a session in which the actor acts on the subject's behalf, against a grant that already exists.

It never creates authority. Without a live grant matching (app, actor, subject) this fails, which is what makes revocation meaningful.

Unlike per-request API-key auth, this does not run through the cached principal-auth gate (plugin_principalauth.go). Exchange is rare and mints a durable session rather than being called on every request, so the cache buys nothing here and its staleness would matter more than the extra lookup this costs instead.

func (*Engine) ExportUserData ¶ added in v1.9.0

func (e *Engine) ExportUserData(ctx context.Context, userID id.UserID) (*UserExport, error)

ExportUserData returns all data associated with a user for GDPR data portability.

func (*Engine) FailMFATicket ¶ added in v1.9.0

func (e *Engine) FailMFATicket(ctx context.Context, ticket string) (exhausted bool, err error)

FailMFATicket charges one wrong code against a ticket and reports whether that exhausted it. An exhausted ticket is deleted: the user must sign in again rather than keep guessing.

The rewritten entry keeps the ticket's original deadline. Re-Setting a full MFATicketTTL would let a caller hold a ticket open indefinitely by submitting wrong codes, extending the very window the counter bounds.

Errors are returned for the caller to log, not to surface: a failed write must not turn a wrong-code answer into a distinguishable 500, which would itself confirm the code was wrong.

func (*Engine) ForgotPassword ¶ added in v1.9.0

func (e *Engine) ForgotPassword(ctx context.Context, appID id.AppID, email string) (*account.PasswordReset, error)

func (*Engine) GetApp ¶ added in v1.9.0

func (e *Engine) GetApp(ctx context.Context, appID id.AppID) (*app.App, error)

GetApp retrieves an app by ID.

func (*Engine) GetAppBySlug ¶ added in v1.9.0

func (e *Engine) GetAppBySlug(ctx context.Context, slug string) (*app.App, error)

GetAppBySlug retrieves an app by its slug.

func (*Engine) GetDefaultEnvironment ¶ added in v1.9.0

func (e *Engine) GetDefaultEnvironment(ctx context.Context, appID id.AppID) (*environment.Environment, error)

GetDefaultEnvironment retrieves the default environment for an app.

func (*Engine) GetDevice ¶ added in v1.9.0

func (e *Engine) GetDevice(ctx context.Context, deviceID id.DeviceID) (*device.Device, error)

GetDevice returns a device by ID.

func (*Engine) GetEnvironment ¶ added in v1.9.0

func (e *Engine) GetEnvironment(ctx context.Context, envID id.EnvironmentID) (*environment.Environment, error)

GetEnvironment retrieves an environment by ID.

func (*Engine) GetEnvironmentBySlug ¶ added in v1.9.0

func (e *Engine) GetEnvironmentBySlug(ctx context.Context, appID id.AppID, slug string) (*environment.Environment, error)

GetEnvironmentBySlug retrieves an environment by app ID and slug.

func (*Engine) GetMe ¶ added in v1.9.0

func (e *Engine) GetMe(ctx context.Context, userID id.UserID) (*user.User, error)

GetMe returns the current user by ID.

func (*Engine) GetOrgBranding ¶ added in v1.9.0

func (e *Engine) GetOrgBranding(ctx context.Context, orgID id.OrgID) (*formconfig.BrandingConfig, error)

GetOrgBranding returns the branding config for an organization.

func (*Engine) GetRole ¶ added in v1.9.0

func (e *Engine) GetRole(ctx context.Context, appID id.AppID, roleID id.RoleID) (*rbac.Role, error)

GetRole returns a role by ID within an app. A role owned by another app is reported as not found.

func (*Engine) GetRoleBySlug ¶ added in v1.9.0

func (e *Engine) GetRoleBySlug(ctx context.Context, appID id.AppID, slug string) (*rbac.Role, error)

GetRoleBySlug returns a role by slug within an app.

func (*Engine) GetRoleChildren ¶ added in v1.9.0

func (e *Engine) GetRoleChildren(ctx context.Context, appID id.AppID, roleID id.RoleID) ([]*rbac.Role, error)

GetRoleChildren returns the direct child roles of one of the app's roles.

func (*Engine) GetRoleParent ¶ added in v1.9.0

func (e *Engine) GetRoleParent(ctx context.Context, appID id.AppID, r *rbac.Role) (*rbac.Role, error)

GetRoleParent returns the role r inherits from, found the way warden finds it: by r's parent slug, in r's own namespace within the app. GetRoleBySlug searches several namespaces and can land on another role with the same slug. It returns rbac.ErrRoleNotFound when r has no parent or warden reports the parent missing; any other failure comes back as an error, so a caller deciding a grant can refuse it.

func (*Engine) GetServiceAccount ¶ added in v1.9.0

func (e *Engine) GetServiceAccount(ctx context.Context, svcID id.ServiceAccountID) (*serviceaccount.ServiceAccount, error)

GetServiceAccount returns a service account by ID.

func (*Engine) GetSignupFormConfig ¶ added in v1.9.0

func (e *Engine) GetSignupFormConfig(ctx context.Context, appID id.AppID) (*formconfig.FormConfig, error)

GetSignupFormConfig returns the active signup form config for an app.

func (*Engine) GetUser ¶ added in v1.9.0

func (e *Engine) GetUser(ctx context.Context, userID id.UserID) (*user.User, error)

GetUser returns a user by ID. This method allows the Engine to satisfy narrow interfaces (e.g. notification plugin's userLookup) without exposing the full store.

func (*Engine) GetUserByEmail ¶ added in v1.9.0

func (e *Engine) GetUserByEmail(ctx context.Context, appID id.AppID, email string) (*user.User, error)

GetUserByEmail looks up a user by any of their email addresses (primary or secondary) within an app. Returns store.ErrNotFound when no match exists.

func (*Engine) GetWebhook ¶ added in v1.9.0

func (e *Engine) GetWebhook(ctx context.Context, webhookID id.WebhookID) (*webhook.Webhook, error)

GetWebhook returns a webhook by ID. The secret is never part of it.

func (*Engine) GrantDelegation ¶ added in v1.9.0

func (e *Engine) GrantDelegation(
	ctx context.Context, appID id.AppID, actor, subject principal.Ref,
	scopes []string, grantedBy principal.Ref, expiresAt *time.Time,
) (*principal.Delegation, error)

GrantDelegation records that actor may act for subject.

grantedBy is who consented and is recorded for audit. The caller is responsible for having checked that grantedBy is entitled to consent: for an ordinary delegation that means grantedBy is the subject or an admin over it. There is no HTTP route for this yet, so today "the caller" means whatever engine-embedding code invokes this method directly; the entitlement check belongs there until a route exists to enforce it.

func (*Engine) HasJWT ¶ added in v1.9.0

func (e *Engine) HasJWT() bool

HasJWT returns true if any JWT format is configured.

func (*Engine) HasPermission ¶ added in v1.9.0

func (e *Engine) HasPermission(ctx context.Context, userID id.UserID, action, resource string) (bool, error)

HasPermission checks whether a user has a specific permission.

Preserved as-is for plugin.PermissionChecker and for every caller that has a user ID and no chain: it is Can with an empty chain, so the walk of the role hierarchy and the inheritance behaviour are exactly what they were before Can existed. The decision and reason Can logs on denial come from checkOne, which now carries the full *warden.CheckResult for every hop rather than only the subject's, so nothing here is worse off for having moved.

func (*Engine) HasStrategies ¶ added in v1.9.0

func (e *Engine) HasStrategies() bool

HasStrategies returns true if any authentication strategies are registered.

func (*Engine) HasUsers ¶ added in v1.9.0

func (e *Engine) HasUsers(ctx context.Context) bool

HasUsers returns true if the platform app has at least one user.

func (*Engine) Health ¶ added in v1.9.0

func (e *Engine) Health(ctx context.Context) error

Health checks the health of the engine by pinging its store.

func (*Engine) Herald ¶ added in v1.9.0

func (e *Engine) Herald() bridge.Herald

Herald returns the Herald notification bridge (may be nil).

func (*Engine) Hooks ¶ added in v1.9.0

func (e *Engine) Hooks() *hook.Bus

Hooks returns the global event bus.

func (*Engine) Impersonate ¶ added in v1.9.0

func (e *Engine) Impersonate(ctx context.Context, adminID, targetID id.UserID, opts ...ImpersonateOption) (*user.User, *session.Session, error)

Impersonate creates a new session for the target user, marked as impersonated by the admin. The resulting session behaves as if the target user is signed in, but carries the impersonator's identity for audit purposes.

func (*Engine) InitPlugins ¶ added in v1.9.0

func (e *Engine) InitPlugins(ctx context.Context) error

InitPlugins builds the auth middleware, registers the session auth provider, and calls OnInit on all registered plugins. Idempotent — safe to call multiple times. The extension calls this before route registration so plugins can set up dependencies that RegisterRoutes relies on. A plugin whose OnInit fails aborts initialization: pluginsInitialized stays false so a caller that recovers can retry, and the error propagates rather than leaving the engine running with that plugin's control absent.

func (*Engine) IssueEmailVerification ¶ added in v1.9.0

func (e *Engine) IssueEmailVerification(ctx context.Context, userID id.UserID) error

IssueEmailVerification mints a fresh 6-digit OTP for the user's email and fires ActionEmailVerificationRequested carrying the code so the notification layer can deliver it. Safe to call repeatedly (e.g. resend).

func (*Engine) IssueSession ¶ added in v1.9.0

func (e *Engine) IssueSession(ctx context.Context, req *IssueSessionRequest) (*IssueSessionResult, error)

IssueSession is the centralized session-mint chokepoint. Every login path goes through this function; the MFARequired gate has exactly one implementation, here.

Returns (*IssueSessionResult, nil) on success. Returns (nil, *MFARequiredError) when the gate fires. Returns (nil, err) for any other failure.

Because every sign-in reaches it, this is also where a sign-in attempt is counted, under the normalized auth method. A call that completes an MFA challenge is not a new attempt (the MFA plugin counts the code check), so it gets the span but not the count.

func (*Engine) JWTFormats ¶ added in v1.9.0

func (e *Engine) JWTFormats() map[string]*tokenformat.JWT

JWTFormats returns all registered JWT formats (for JWKS endpoint).

func (*Engine) JWTSessionChecker ¶ added in v1.9.0

func (e *Engine) JWTSessionChecker(appIDStr, sessionIDStr string) (*session.Session, error)

JWTSessionChecker checks whether a JWT's session ID still exists in the store. This enables JWT revocation: revoked sessions are rejected even if the JWT signature is valid. The SettingJWTRequireActiveSession setting, resolved under the token's own app, controls whether this check is active; when it is off, nil, nil is returned to skip the store lookup and the binding checks. The setting defaults to on.

func (*Engine) JWTSessionContextChecker ¶ added in v1.9.0

func (e *Engine) JWTSessionContextChecker(ctx context.Context, appIDStr, sessionIDStr string) (*session.Session, error)

JWTSessionContextChecker is JWTSessionChecker for a caller that has the request's context. The lookup runs on context.WithoutCancel(ctx): it keeps the request's values, so its queries show up in the request's trace, and drops its cancellation and deadline, so it behaves exactly as it did on context.Background().

func (*Engine) KeyManager ¶ added in v1.9.0

func (e *Engine) KeyManager() bridge.KeyManager

KeyManager returns the key management bridge (may be nil).

func (*Engine) Keysmith ¶ added in v1.9.0

func (e *Engine) Keysmith() *keysmith.Engine

Keysmith returns the first-class key management engine (may be nil).

func (*Engine) Ledger ¶ added in v1.9.0

func (e *Engine) Ledger() bridge.Ledger

Ledger returns the billing/metering bridge (may be nil).

func (*Engine) LedgerEngine ¶ added in v1.9.0

func (e *Engine) LedgerEngine() *xledger.Ledger

LedgerEngine returns the first-class ledger billing engine (may be nil).

func (*Engine) LedgerStore ¶ added in v1.9.0

func (e *Engine) LedgerStore() ledgerstore.Store

LedgerStore returns the underlying ledger store for direct query access (e.g. plan/feature/subscription listing). Returns nil if no ledger engine was provided via WithLedgerEngine.

func (*Engine) LegacyPlaintextSecretReads ¶ added in v1.9.0

func (e *Engine) LegacyPlaintextSecretReads() uint64

LegacyPlaintextSecretReads reports how many stored secrets have been read back without an encryption envelope since this process started: rows written before at-rest encryption was deployed. When it stays at zero across a full cycle of the workload, every such row has been rewritten and Config.TokenEncryption.Strict can be turned on.

func (*Engine) ListAllDevices ¶ added in v1.9.0

func (e *Engine) ListAllDevices(ctx context.Context, limit int) ([]*device.Device, error)

ListAllDevices returns the most recent devices across all users, up to limit.

func (*Engine) ListAllSessions ¶ added in v1.9.0

func (e *Engine) ListAllSessions(ctx context.Context, limit int) ([]*session.Session, error)

ListAllSessions returns the most recent sessions across all users, up to limit.

func (*Engine) ListApps ¶ added in v1.9.0

func (e *Engine) ListApps(ctx context.Context) ([]*app.App, error)

ListApps returns all apps.

func (*Engine) ListAuthMethods ¶ added in v1.9.0

func (e *Engine) ListAuthMethods(ctx context.Context, userID id.UserID) ([]*plugin.AuthMethod, error)

ListAuthMethods aggregates linked auth methods from all plugins that implement AuthMethodContributor. Returns an empty slice if no plugins contribute methods.

func (*Engine) ListDelegationsForSubject ¶ added in v1.9.0

func (e *Engine) ListDelegationsForSubject(
	ctx context.Context, appID id.AppID, subject principal.Ref,
) ([]*principal.Delegation, error)

ListDelegationsForSubject returns what may act for subject, so a person can see and revoke the agents holding authority over their account.

func (*Engine) ListEnvironments ¶ added in v1.9.0

func (e *Engine) ListEnvironments(ctx context.Context, appID id.AppID) ([]*environment.Environment, error)

ListEnvironments returns all environments for an app.

func (*Engine) ListFormConfigs ¶ added in v1.9.0

func (e *Engine) ListFormConfigs(ctx context.Context, appID id.AppID) ([]*formconfig.FormConfig, error)

ListFormConfigs returns all form configs for an app.

func (*Engine) ListRolePermissions ¶ added in v1.9.0

func (e *Engine) ListRolePermissions(ctx context.Context, appID id.AppID, roleID id.RoleID) ([]*rbac.Permission, error)

ListRolePermissions returns all permissions for one of the app's roles.

func (*Engine) ListRoles ¶ added in v1.9.0

func (e *Engine) ListRoles(ctx context.Context, appID id.AppID) ([]*rbac.Role, error)

ListRoles returns all roles for an app.

func (*Engine) ListServiceAccounts ¶ added in v1.9.0

func (e *Engine) ListServiceAccounts(ctx context.Context, appID id.AppID, limit int) (*serviceaccount.List, error)

ListServiceAccounts returns service accounts for an app.

func (*Engine) ListSessions ¶ added in v1.9.0

func (e *Engine) ListSessions(ctx context.Context, userID id.UserID) ([]*session.Session, error)

ListSessions returns all sessions for a user.

func (*Engine) ListSessionsPage ¶ added in v1.9.0

func (e *Engine) ListSessionsPage(ctx context.Context, userID id.UserID, opts page.Opts) (page.Page[*session.Session], error)

ListSessionsPage is the bounded twin of ListSessions for the request path.

func (*Engine) ListUserDevices ¶ added in v1.9.0

func (e *Engine) ListUserDevices(ctx context.Context, userID id.UserID) ([]*device.Device, error)

ListUserDevices returns all devices for a user.

func (*Engine) ListUserRoleSlugs ¶ added in v1.9.0

func (e *Engine) ListUserRoleSlugs(ctx context.Context, userID id.UserID) ([]string, error)

ListUserRoleSlugs returns the slugs of all roles assigned to a user. This satisfies the middleware.RoleChecker interface (legacy; prefer PermissionChecker).

func (*Engine) ListUserRoles ¶ added in v1.9.0

func (e *Engine) ListUserRoles(ctx context.Context, userID id.UserID) ([]*rbac.Role, error)

ListUserRoles returns all roles assigned to a user within the platform app scope.

func (*Engine) ListUserRolesInApp ¶ added in v1.9.0

func (e *Engine) ListUserRolesInApp(ctx context.Context, appID id.AppID, userID id.UserID) ([]*rbac.Role, error)

ListUserRolesInApp returns the roles assigned to userID within the given appID. Use this when the caller's session app differs from the app the role assignments live in (e.g. cross-app admin tooling like TwinOS studio, where each workspace owns a separate Authsome App but the calling API key authenticates against the platform App).

func (*Engine) ListUsersWithRole ¶ added in v1.9.0

func (e *Engine) ListUsersWithRole(ctx context.Context, appID id.AppID, roleSlug string) ([]id.UserID, error)

ListUsersWithRole returns the user IDs of all subjects that hold the named role slug within the given app. It queries the warden assignments store directly because the rbac.Store interface exposes only a user→roles direction, not the reverse. Returns an empty slice (not an error) when no assignments exist.

func (*Engine) ListWebhooks ¶ added in v1.9.0

func (e *Engine) ListWebhooks(ctx context.Context, appID id.AppID) ([]*webhook.Webhook, error)

ListWebhooks returns all webhooks for an app, without secrets.

func (*Engine) LoadMFATicket ¶ added in v1.9.0

func (e *Engine) LoadMFATicket(ctx context.Context, ticket string) (*MFATicketPayload, error)

LoadMFATicket retrieves a ticket payload from the ceremony store without consuming it. Returns ceremony.ErrNotFound when the ticket is missing or expired.

func (*Engine) Lockout ¶ added in v1.9.0

func (e *Engine) Lockout() lockout.Tracker

Lockout returns the account lockout tracker (may be nil).

func (*Engine) Logger ¶ added in v1.9.0

func (e *Engine) Logger() log.Logger

Logger returns the engine logger.

func (*Engine) Mailer ¶ added in v1.9.0

func (e *Engine) Mailer() bridge.Mailer

Mailer returns the email bridge (may be nil). Each send through it is a send email span and an observation in the provider latency histogram, whichever Mailer was configured; the no-op mailer is handed back as is.

func (*Engine) Metrics ¶ added in v1.9.0

func (e *Engine) Metrics() Metrics

Metrics returns the current engine metrics.

func (*Engine) MetricsCollector ¶ added in v1.9.0

func (e *Engine) MetricsCollector() bridge.MetricsCollector

MetricsCollector returns the metrics collector bridge (may be nil).

func (*Engine) MintChildPrincipal ¶ added in v1.9.0

func (e *Engine) MintChildPrincipal(
	ctx context.Context, parentID id.ServiceAccountID, name string, scopes []string, ttl time.Duration,
) (*serviceaccount.ServiceAccount, *apikey.APIKey, string, error)

MintChildPrincipal creates a short-lived principal under a registered parent, with its own API key.

This is what makes per-task agents workable: one durable registration, N ephemeral instances, each with its own identity for attribution and its own credential to revoke. The two caps are what keep it from being an escalation: a child's scopes are a subset of its parent's, and a child's expiry never passes its parent's.

ttl is not required to be positive. A caller normally passes a positive duration, but a zero or negative one is accepted rather than rejected: it mints a child that is already expired, which is exactly what ReapExpiredPrincipals needs to be provable against without waiting out a real clock. The HTTP handler is where a positive ttl is actually enforced for real callers.

The secret is returned once and is not stored.

func (*Engine) NonceSecret ¶ added in v1.9.0

func (e *Engine) NonceSecret() []byte

NonceSecret returns the dashboard nonce signing secret.

It is derived from the first available HMAC JWT signing key (i.e. an entry in jwtFormats whose SigningKey is a []byte) by HMAC-SHA256 with the info string "authsome:dashboard:nonce-v1" — a single-step HKDF-Expand equivalent that domain-separates the nonce key from the JWT key without adding a new dependency.

If no HMAC JWT key is available (e.g. RSA/ES256 only, or no JWT at all), it falls back to the AUTHSOME_DASHBOARD_NONCE_SECRET environment variable. If that is also empty, it returns nil and the caller (the dashboard contributor) is expected to log a warning and either generate a random per-process secret or leave the signer uninitialised.

func (*Engine) PasswordHistory ¶ added in v1.9.0

func (e *Engine) PasswordHistory() account.PasswordHistoryStore

PasswordHistory returns the password history store (may be nil).

func (*Engine) PlatformAppID ¶ added in v1.9.0

func (e *Engine) PlatformAppID() id.AppID

PlatformAppID returns the platform app's ID.

func (*Engine) Plugin ¶ added in v1.9.0

func (e *Engine) Plugin(name string) plugin.Plugin

Plugin returns a registered plugin by name, or nil if not found.

func (*Engine) Plugins ¶ added in v1.9.0

func (e *Engine) Plugins() *plugin.Registry

Plugins returns the plugin registry.

func (*Engine) PrincipalAuthGate ¶ added in v1.9.0

func (e *Engine) PrincipalAuthGate() any

PrincipalAuthGate implements plugin.PrincipalAuthGateProvider. It returns any rather than a named type so this package does not have to import a plugin package to spell the return type, which would create an import cycle with plugins (such as apikey) that need the concrete *Engine type for other reasons. Consumers type-assert the result against their own narrow gate interface.

func (*Engine) PrincipalStore ¶ added in v1.9.0

func (e *Engine) PrincipalStore() principal.Store

PrincipalStore returns the principal and delegation store.

func (*Engine) ProtectedResourceMetadataURL ¶ added in v1.9.0

func (e *Engine) ProtectedResourceMetadataURL() string

ProtectedResourceMetadataURL returns the configured RFC 9728 metadata URL.

func (*Engine) RateLimitMiddleware ¶ added in v1.9.0

func (e *Engine) RateLimitMiddleware(limit int) forge.Middleware

RateLimitMiddleware returns the throttling middleware for limit requests per window, or nil when rate limiting is off or the limit is zero. Route groups that share one budget (SSO, SCIM) mount it as a group middleware.

func (*Engine) RateLimitMiddlewareConfig ¶ added in v1.9.0

func (e *Engine) RateLimitMiddlewareConfig(limit int) middleware.RateLimitConfig

RateLimitMiddlewareConfig builds the middleware configuration for limit requests per window: the configured window, the fail-open switch and a counter for limiter errors.

func (*Engine) RateLimitOptions ¶ added in v1.9.0

func (e *Engine) RateLimitOptions(limit int) []forge.RouteOption

RateLimitOptions returns the route options that throttle an endpoint to limit requests per window, keyed by client address, or nil when rate limiting is off or the limit is zero. Every route in the tree that carries a limit builds it here, so the fail-closed behaviour and the error metric are the same everywhere.

func (*Engine) RateLimiter ¶ added in v1.9.0

func (e *Engine) RateLimiter() ratelimit.Limiter

RateLimiter returns the rate limiter (may be nil).

func (*Engine) ReapExpiredPrincipals ¶ added in v1.9.0

func (e *Engine) ReapExpiredPrincipals(ctx context.Context, appID id.AppID) (int, error)

ReapExpiredPrincipals deletes lapsed ephemeral children and returns how many went.

Only children are reaped. A durable principal with an expiry set is one an operator chose to time-limit, and deleting it out from under them would destroy a registration they can see in the dashboard.

func (*Engine) RebindLedgerOnPlugins ¶ added in v1.9.0

func (e *Engine) RebindLedgerOnPlugins()

RebindLedgerOnPlugins pushes the engine's current ledger engine and store into any plugin that implements SetLedger / SetLedgerStore. Safe to call multiple times; no-op when the engine has no ledger.

func (*Engine) Refresh ¶ added in v1.9.0

func (e *Engine) Refresh(ctx context.Context, refreshToken string, opts ...RefreshOpts) (*session.Session, error)

func (*Engine) RefreshBySessionID ¶ added in v1.9.0

func (e *Engine) RefreshBySessionID(ctx context.Context, sessionID id.SessionID, opts ...RefreshOpts) (*session.Session, error)

RefreshBySessionID rotates the session with the given id into fresh tokens without the caller presenting either of its credentials.

This exists for hand-offs that already proved who is asking by other means: the SSO one-time code, which the browser redeems after the identity provider delivered it, names the session it stands for and nothing more. The store keeps only hashes, so the tokens minted at callback time cannot be read back; rotating the session gives the redeemer live tokens and retires the ones nobody ever received. Everything Refresh enforces after the lookup applies here too.

func (*Engine) RefreshBySessionToken ¶ added in v1.9.0

func (e *Engine) RefreshBySessionToken(ctx context.Context, sessionToken string, opts ...RefreshOpts) (*session.Session, error)

RefreshBySessionToken rotates the session behind an access token the caller already holds, without the caller presenting the refresh token.

This is the cookie path: a browser that authenticated with the session cookie has proven it holds the current access token, and the store keeps the refresh token only as a hash, so there is no plaintext to hand back and feed into Refresh. Everything Refresh enforces after the lookup applies here too: bans, agent sessions, expiry, the absolute lifetime, binding and DPoP. A session issued without a refresh token cannot be rotated this way.

func (*Engine) RefreshForClient ¶ added in v1.9.0

func (e *Engine) RefreshForClient(ctx context.Context, refreshToken, clientID string, opts ...RefreshOpts) (*session.Session, error)

RefreshForClient rotates a session the OAuth2 provider issued to clientID. It is the engine half of the token endpoint's refresh_token grant: the caller has authenticated the client already; here the token is checked for replay, matched to that client, and rotated with the same refusals as any refresh, including the DPoP proof check for a bound session.

func (*Engine) RegisterDevice ¶ added in v1.9.0

func (e *Engine) RegisterDevice(ctx context.Context, d *device.Device) (*device.Device, error)

RegisterDevice creates or updates a device using fingerprint-based upsert.

func (*Engine) Relay ¶ added in v1.9.0

func (e *Engine) Relay() bridge.EventRelay

Relay returns the event relay bridge (may be nil).

func (*Engine) RemovePermission ¶ added in v1.9.0

func (e *Engine) RemovePermission(ctx context.Context, appID id.AppID, permID id.PermissionID) error

RemovePermission removes one of the app's permissions.

func (*Engine) ResendEmailVerification ¶ added in v1.9.0

func (e *Engine) ResendEmailVerification(ctx context.Context, appID id.AppID, email string) error

ResendEmailVerification is the public, enumeration-safe entry point used by the dashboard "resend verification email" CTA and by POST /v1/verify-email/resend. It returns nil whether or not a matching user exists or is already verified — leaking either signal would let an unauthenticated probe enumerate registered addresses (the same trap closed on /v1/signup in Phase 2A).

Callers that legitimately need the lookup outcome (e.g. an authenticated user re-requesting their own verification email) should call SendEmailVerification directly with a hydrated user.

func (*Engine) ResetPassword ¶ added in v1.9.0

func (e *Engine) ResetPassword(ctx context.Context, token, newPassword string) error

ResetPassword resets a user's password using a reset token.

func (*Engine) ResolveAppByPublicKey ¶ added in v1.9.0

func (e *Engine) ResolveAppByPublicKey(ctx context.Context, publicKey string) (*app.App, error)

ResolveAppByPublicKey resolves a publishable key (pk_live_...) to an app. It first checks the apps table publishable_key column (fast, indexed), then falls back to searching Keysmith API key metadata for keys generated via the API key plugin.

func (*Engine) ResolvePrincipal ¶ added in v1.9.0

func (e *Engine) ResolvePrincipal(ctx context.Context, ref principal.Ref) (*principal.Principal, error)

ResolvePrincipal resolves any caller by ref.

func (*Engine) ResolvePrincipalByRef ¶ added in v1.9.0

func (e *Engine) ResolvePrincipalByRef(ctx context.Context, ref principal.Ref) (*principal.Principal, error)

ResolvePrincipalByRef adapts Engine.ResolvePrincipal to middleware.PrincipalResolver's no-context shape (for middleware).

func (*Engine) ResolveSessionByToken ¶ added in v1.9.0

func (e *Engine) ResolveSessionByToken(ctx context.Context, token string) (*session.Session, error)

ResolveSessionByToken resolves a session from its token (for middleware).

func (*Engine) ResolveUser ¶ added in v1.9.0

func (e *Engine) ResolveUser(ctx context.Context, userIDStr string) (*user.User, error)

ResolveUser resolves a user by ID string (for middleware). A banned user does not resolve: every credential path that turns an id into a user runs through here, so a ban takes effect on the next request, not the next sign-in.

func (*Engine) RevokeDelegation ¶ added in v1.9.0

func (e *Engine) RevokeDelegation(
	ctx context.Context, appID id.AppID, revokedBy principal.Ref, delID id.DelegationID,
) error

RevokeDelegation ends a grant.

appID scopes the lookup to the caller's own tenant: a delegation id from one app must not be revocable by a caller authenticated into another, so a mismatch reads as not-found rather than forbidden, the same way api/tenant_scope.go's assertAppScope treats a cross-tenant resource id.

revokedBy must be the grant's own subject or actor. There is no support yet for an admin revoking a grant on someone else's behalf: that would need a Warden permission decision, and this codebase has no established action/resource convention for delegation management to check against yet. Inventing one here would be a guess independent routes could later disagree with. Add that check at this call site, once one exists, rather than routing an admin path around this method.

func (*Engine) RevokeOtherUserSessions ¶ added in v1.9.0

func (e *Engine) RevokeOtherUserSessions(ctx context.Context, userID id.UserID, keep id.SessionID) error

RevokeOtherUserSessions ends every session of the user except keep. A credential change (password, second factor, passkey) calls it so a session an attacker already holds does not survive the change that was meant to lock them out. A nil keep revokes every session.

func (*Engine) RevokeSession ¶ added in v1.9.0

func (e *Engine) RevokeSession(ctx context.Context, sessionID id.SessionID) error

RevokeSession deletes a specific session.

func (*Engine) RevokeUserAccess ¶ added in v1.9.0

func (e *Engine) RevokeUserAccess(ctx context.Context, userID id.UserID) error

RevokeUserAccess ends every live credential a user holds: all of their sessions and every API key bound to them. Bans and deactivations call it so that removing a person's access is one operation, not a session sweep that forgets the keys.

func (*Engine) RotateWebhookSecret ¶ added in v1.9.0

func (e *Engine) RotateWebhookSecret(ctx context.Context, webhookID id.WebhookID) (string, error)

RotateWebhookSecret gives the webhook's Relay endpoint a new secret and returns it, the only time it is shown. Deliveries from this point are signed with the new secret; the receiver must switch before the old one stops verifying, which is at once.

func (*Engine) SMSSender ¶ added in v1.9.0

func (e *Engine) SMSSender() bridge.SMSSender

SMSSender returns the SMS bridge (may be nil), traced and measured the same way as Mailer.

func (*Engine) SaveOrgBranding ¶ added in v1.9.0

func (e *Engine) SaveOrgBranding(ctx context.Context, b *formconfig.BrandingConfig) error

SaveOrgBranding creates or updates the branding config for an organization.

func (*Engine) SaveSignupFormConfig ¶ added in v1.9.0

func (e *Engine) SaveSignupFormConfig(ctx context.Context, fc *formconfig.FormConfig) error

SaveSignupFormConfig creates or updates a signup form config. On update, the version is automatically incremented.

func (*Engine) SendEmailVerification ¶ added in v1.9.0

func (e *Engine) SendEmailVerification(ctx context.Context, u *user.User) (string, error)

SendEmailVerification mints a fresh email-verification token for a known user, persists it, and emits the auth.email_verification_requested hook so subscribers (notification plugin, custom mailers) can deliver the verification link. Caller-side code should use this instead of touching the verification store directly so the hook always fires.

Returns the new token so callers that bypass the hook (e.g. tests driving the engine in-process) can complete the flow inline.

func (*Engine) SessionConfigForApp ¶ added in v1.9.0

func (e *Engine) SessionConfigForApp(ctx context.Context, appID id.AppID, envIDs ...id.EnvironmentID) account.SessionConfig

SessionConfigForApp returns the resolved session config for an app, applying per-app and optional per-environment overrides on top of the global defaults. Plugins should use this instead of hardcoding their own session config.

func (*Engine) SetAuthRegistry ¶ added in v1.9.0

func (e *Engine) SetAuthRegistry(r auth.Registry)

SetAuthRegistry sets the forge auth provider registry. Called by the extension after obtaining the registry from the DI container.

func (*Engine) SetChronicle ¶ added in v1.9.0

func (e *Engine) SetChronicle(ch bridge.Chronicle)

SetChronicle replaces the chronicle implementation. Intended for tests; not safe for concurrent use after Start.

func (*Engine) SetDefaultEnvironment ¶ added in v1.9.0

func (e *Engine) SetDefaultEnvironment(ctx context.Context, appID id.AppID, envID id.EnvironmentID) error

SetDefaultEnvironment sets an environment as the default for its app.

func (*Engine) SetDummyHashObserver ¶ added in v1.9.0

func (e *Engine) SetDummyHashObserver(fn func(account.PasswordPolicy))

SetDummyHashObserver installs a test-only callback that fires on every dummy hash, so a test can prove the enumeration-resistant path paid the cost without timing it.

func (*Engine) SetLedgerEngine ¶ added in v1.9.0

func (e *Engine) SetLedgerEngine(l *xledger.Ledger)

SetLedgerEngine wires a ledger engine into the authsome engine after construction. This is the late-binding counterpart to WithLedgerEngine, intended for hosts that cannot resolve the ledger at engine-build time (typical when both authsome and ledger live in a DI container whose registration order is not guaranteed).

It also updates the bridge.Ledger adapter so hook-based consumers see a real ledger instead of the noop. Callers are still responsible for propagating the new ledger to plugins that captured their own reference during OnInit — see Engine.rebindLedgerOnPlugins.

func (*Engine) SetLockoutTracker ¶ added in v1.9.0

func (e *Engine) SetLockoutTracker(t lockout.Tracker)

SetLockoutTracker replaces the lockout tracker. A test seam only.

func (*Engine) SetRateLimiter ¶ added in v1.9.0

func (e *Engine) SetRateLimiter(rl ratelimit.Limiter)

SetRateLimiter replaces the rate limiter. A test seam only, like SetChronicle: tests relax the on-by-default limits without rebuilding the engine's configuration.

func (*Engine) Settings ¶ added in v1.9.0

func (e *Engine) Settings() *settings.Manager

Settings returns the dynamic settings manager (may be nil).

func (*Engine) SignIn ¶ added in v1.9.0

func (e *Engine) SignIn(ctx context.Context, req *account.SignInRequest) (_ *user.User, _ *session.Session, signInErr error)

SignIn authenticates a user and returns the user + session.

func (*Engine) SignOut ¶ added in v1.9.0

func (e *Engine) SignOut(ctx context.Context, sessionID id.SessionID) error

SignOut terminates a session.

func (*Engine) SignUp ¶ added in v1.9.0

func (e *Engine) SignUp(ctx context.Context, req *account.SignUpRequest) (*user.User, *session.Session, error)

SignUp creates a new user account and returns the user + session.

func (*Engine) Start ¶ added in v1.9.0

func (e *Engine) Start(ctx context.Context) error

Start initializes the engine, runs migrations, and starts plugins.

func (*Engine) Stop ¶ added in v1.9.0

func (e *Engine) Stop(ctx context.Context) error

Stop gracefully shuts down the engine and all plugins.

func (*Engine) StopImpersonation ¶ added in v1.9.0

func (e *Engine) StopImpersonation(ctx context.Context, sessionID id.SessionID) error

StopImpersonation terminates an impersonation session.

func (*Engine) Store ¶ added in v1.9.0

func (e *Engine) Store() store.Store

Store returns the persistence backend.

func (*Engine) Strategies ¶ added in v1.9.0

func (e *Engine) Strategies() *strategy.Registry

Strategies returns the strategy registry.

func (*Engine) SweepRetention ¶ added in v1.9.0

func (e *Engine) SweepRetention(ctx context.Context) (int64, error)

SweepRetention removes rows that have outlived their retention window: expired sessions, verifications, password resets and revocation records from the store, expired ceremony state, and whatever each plugin that implements plugin.RetentionSweeper keeps. It returns how many rows went.

One replica sweeps at a time: the sweep first takes a lease in the store for half the interval and returns zero, without error, when another replica holds it. Each kind is removed in batches of Retention.BatchSize until a batch comes back short. A failure in one kind does not stop the others; the errors are joined and returned together.

A sweep that holds the lease runs in a sweep retention span: a root span when the background sweeper calls this, a child when you call it under a span of your own. Losing the lease starts no span.

func (*Engine) SwitchActiveOrg ¶ added in v1.9.0

func (e *Engine) SwitchActiveOrg(ctx context.Context, sessionID id.SessionID, newOrgID id.OrgID) (*session.Session, error)

SwitchActiveOrg sets the active organization on an existing session. The session's user must be a member of newOrgID — verified via the organization plugin. When the user is not a member the org-plugin returns ErrOrgNotMember (or equivalent) and we propagate.

Returns the updated session so callers (HTTP handlers) can re-issue the cookie / token with the new OrgID claim if appropriate.

When newOrgID is empty the active org is cleared (legitimate use: "leave the current org and stay logged out of any org").

func (*Engine) Telemetry ¶ added in v1.9.0

func (e *Engine) Telemetry() *telemetry.Metrics

Telemetry returns the engine's metric instruments, for authsome's own plugins to record into. It is nil when the engine was built without WithMetricsSink, and every method on it is safe to call on nil.

func (*Engine) TokenEncryptor ¶ added in v1.9.0

func (e *Engine) TokenEncryptor() bridge.Encryptor

TokenEncryptor returns the at-rest Encryptor used for sensitive opaque payloads such as third-party OAuth access/refresh tokens. Always non-nil after NewEngine: a boot with no key configured fails rather than falling back to plaintext, except under `go test`. Plugins that persist provider tokens should wrap their store using this encryptor (see plugins/social.NewEncryptedStore).

func (*Engine) TokenFormatForApp ¶ added in v1.9.0

func (e *Engine) TokenFormatForApp(appID string) tokenformat.Format

TokenFormatForApp returns the token format for a specific app. Resolution order: per-app JWT format → default format → opaque.

func (*Engine) TrustDevice ¶ added in v1.9.0

func (e *Engine) TrustDevice(ctx context.Context, deviceID id.DeviceID) (*device.Device, error)

TrustDevice marks a device as trusted.

func (*Engine) UnassignUserRole ¶ added in v1.9.0

func (e *Engine) UnassignUserRole(ctx context.Context, appID id.AppID, userID id.UserID, roleID id.RoleID) error

UnassignUserRole removes one of the app's roles from a user.

func (*Engine) UnlinkAuthMethod ¶ added in v1.9.0

func (e *Engine) UnlinkAuthMethod(ctx context.Context, userID id.UserID, provider string) error

UnlinkAuthMethod removes an auth method from a user. It returns an error if the method is the last one (cannot unlink all methods) or the plugin does not support unlinking.

func (*Engine) UpdateApp ¶ added in v1.9.0

func (e *Engine) UpdateApp(ctx context.Context, a *app.App) error

UpdateApp updates an existing application.

func (*Engine) UpdateEnvironment ¶ added in v1.9.0

func (e *Engine) UpdateEnvironment(ctx context.Context, env *environment.Environment) error

UpdateEnvironment updates an existing environment.

func (*Engine) UpdateMe ¶ added in v1.9.0

func (e *Engine) UpdateMe(ctx context.Context, u *user.User) error

UpdateMe updates the current user.

func (*Engine) UpdateRole ¶ added in v1.9.0

func (e *Engine) UpdateRole(ctx context.Context, r *rbac.Role) error

UpdateRole updates an existing RBAC role.

func (*Engine) UpdateWebhook ¶ added in v1.9.0

func (e *Engine) UpdateWebhook(ctx context.Context, w *webhook.Webhook) error

UpdateWebhook mirrors w's URL, events and active state to its Relay endpoint and saves the row. A changed URL is checked and test-delivered the way a new one is; the secret is unchanged (see RotateWebhookSecret).

func (*Engine) ValidateJWT ¶ added in v1.9.0

func (e *Engine) ValidateJWT(token string) (*tokenformat.TokenClaims, error)

ValidateJWT tries all registered JWT formats to validate a token. This implements middleware.JWTValidator.

func (*Engine) Vault ¶ added in v1.9.0

func (e *Engine) Vault() bridge.Vault

Vault returns the secrets/feature-flag/config bridge (may be nil).

func (*Engine) VerifyEmail ¶ added in v1.9.0

func (e *Engine) VerifyEmail(ctx context.Context, token string) error

VerifyEmail verifies a user's email using a verification token.

func (*Engine) VerifyEmailCode ¶ added in v1.9.0

func (e *Engine) VerifyEmailCode(ctx context.Context, userID id.UserID, code string) error

VerifyEmailCode verifies a user's email using the 6-digit OTP code they were emailed. It enforces expiry and a maximum number of attempts, and uses a constant-time comparison to avoid leaking the code via timing.

func (*Engine) Warden ¶ added in v1.9.0

func (e *Engine) Warden() *warden.Engine

Warden returns the first-class authorization engine (may be nil).

type ExchangeRequest ¶ added in v1.9.0

type ExchangeRequest struct {
	AppID            id.AppID
	Actor            principal.Ref
	RequestedSubject principal.Ref
	Scopes           []string
	IPAddress        string
	UserAgent        string
	// CredentialID identifies the credential the actor authenticated with, so
	// the risk verdict caches against it.
	CredentialID string

	// CallerActors is the actor chain already stamped on the session or
	// credential presenting this request, if any. Non-empty means Actor is
	// not presenting its own root credential: it already holds a session
	// minted by a prior exchange or by impersonation. See the refusal this
	// guards in ExchangeToken.
	CallerActors principal.Chain
	// CallerDelegationID is the grant the caller's own session was minted
	// against, if any. Checked alongside CallerActors so a session that
	// somehow carries a delegation id without a populated chain is still
	// caught.
	CallerDelegationID id.DelegationID
}

ExchangeRequest asks for a session in which Actor acts for RequestedSubject.

type ID ¶ added in v1.9.0

type ID = id.ID

ID is the primary identifier type for all AuthSome entities.

type ImpersonateOption ¶ added in v1.9.0

type ImpersonateOption func(*impersonateOpts)

ImpersonateOption configures Engine.Impersonate. Variadic so the existing three-argument call shape keeps compiling.

func WithImpersonationDPoPBinding ¶ added in v1.9.0

func WithImpersonationDPoPBinding(jkt string) ImpersonateOption

WithImpersonationDPoPBinding binds the impersonation session to a client-held key.

Impersonation mints outside IssueSession, so it needs the mandate applied explicitly. It is worth applying: an impersonation session acts as another user, which makes it the one session you would least want exempt from proof-of-possession on an app that requires it. The admin console asking for the impersonation is an ordinary HTTP client and can present a proof like any other.

type IssueSessionRequest ¶ added in v1.9.0

type IssueSessionRequest struct {
	User       *user.User
	AppID      id.AppID
	EnvID      id.EnvironmentID
	AuthMethod string
	IPAddress  string
	UserAgent  string

	// MFAJustVerified bypasses the MFARequired gate. Set this only
	// from the MFA challenge handler immediately after a code has
	// been validated against a ticket; bypassing without that
	// pairing is account hijack.
	MFAJustVerified bool

	// SessionTTL and RefreshTTL let the calling auth method shorten the
	// session it mints below the app's configured lifetime — a magic-link or
	// SSO session need not last as long as one from an interactive password
	// login. Zero means "use the app's configured value".
	//
	// These may only shorten. A plugin asking for longer than the app allows
	// is ignored, so a per-method setting can never be used to escape the
	// lifetime an operator set centrally.
	SessionTTL time.Duration
	RefreshTTL time.Duration

	// DPoPJKT binds the issued session to a client-held key (RFC 9449).
	// Empty issues an ordinary unbound session.
	DPoPJKT string
}

IssueSessionRequest is the input to Engine.IssueSession. Every login path (password, social, magiclink, sso, phone, post-MFA-verify) populates one of these and hands it to the engine; the engine interposes the MFARequired gate before minting a session.

type IssueSessionResult ¶ added in v1.9.0

type IssueSessionResult struct {
	User    *user.User
	Session *session.Session
}

IssueSessionResult is the gate's success output. On the MFA-needed path the gate returns (nil, *MFARequiredError) instead.

type LockoutConfig ¶ added in v1.9.0

type LockoutConfig struct {
	// MaxAttempts is the number of failed attempts before lockout (default: 5).
	MaxAttempts int `json:"max_attempts"`

	// LockoutDurationSeconds is the lockout duration in seconds (default: 900 = 15 min).
	LockoutDurationSeconds int `json:"lockout_duration_seconds"`

	// ResetAfterSeconds resets the failure count after this many seconds of no failures (default: 3600 = 1h).
	ResetAfterSeconds int `json:"reset_after_seconds"`

	// Enabled enables account lockout (default: true).
	Enabled bool `json:"enabled"`
}

LockoutConfig configures account lockout after failed authentication attempts.

func (LockoutConfig) LockoutDuration ¶ added in v1.9.0

func (c LockoutConfig) LockoutDuration() time.Duration

LockoutDuration returns the lockout duration as a time.Duration.

func (LockoutConfig) ResetAfter ¶ added in v1.9.0

func (c LockoutConfig) ResetAfter() time.Duration

ResetAfter returns the failure count reset period as a time.Duration.

type MFARequiredError ¶ added in v1.9.0

type MFARequiredError struct {
	Ticket           string
	AvailableMethods []string
}

MFARequiredError carries the ticket and available methods so the HTTP layer can render the 403 body without a second store lookup. Wraps account.ErrMFARequired so existing errors.Is checks keep working.

func (*MFARequiredError) Error ¶ added in v1.9.0

func (e *MFARequiredError) Error() string

Error returns the underlying sentinel's message.

func (*MFARequiredError) ResponseBody ¶ added in v1.9.0

func (e *MFARequiredError) ResponseBody() any

ResponseBody returns the JSON envelope the API returns to clients. Mirrors codedHTTPError in api/helpers.go so plugins don't need to import the api package to render the same shape.

func (*MFARequiredError) StatusCode ¶ added in v1.9.0

func (e *MFARequiredError) StatusCode() int

StatusCode lets the forge HTTP layer treat this error directly as a 403 without an explicit mapError call from every plugin handler. Plugin callbacks that bubble *MFARequiredError up unchanged still produce the canonical mfa_required envelope.

func (*MFARequiredError) Unwrap ¶ added in v1.9.0

func (e *MFARequiredError) Unwrap() error

Unwrap exposes the sentinel for errors.Is checks.

type MFATicketPayload ¶ added in v1.9.0

type MFATicketPayload struct {
	UserID     id.UserID
	AppID      id.AppID
	EnvID      id.EnvironmentID
	AuthMethod string
	IPAddress  string
	UserAgent  string
	IssuedAt   time.Time

	// DPoPJKT is the thumbprint proved at sign-in, carried across the
	// ceremony so the session minted after the second factor keeps the
	// binding the first factor established.
	DPoPJKT string
}

MFATicketPayload is the publicly exposed shape of a loaded ticket. It mirrors the on-disk form but uses typed IDs so callers can use them directly with engine APIs.

type Metrics ¶ added in v1.9.0

type Metrics struct {
	PluginsLoaded int `json:"plugins_loaded"`
	Strategies    int `json:"strategies"`
}

Metrics returns observable metrics for the engine.

type Option ¶

type Option func(*Engine)

Option configures the AuthSome engine.

func WithAppID ¶ added in v1.9.0

func WithAppID(appID string) Option

WithAppID sets the application identity used in forge.Scope.

func WithAppSessionConfig ¶ added in v1.9.0

func WithAppSessionConfig(cfg *appsessionconfig.Config) Option

WithAppSessionConfig registers a per-app session configuration override. The config is seeded into the store during engine Start and overrides the global session config for sessions created under the specified app.

func WithAuthorizer ¶ added in v1.9.0

func WithAuthorizer(a bridge.Authorizer) Option

WithAuthorizer sets the authorization bridge.

func WithBasePath ¶

func WithBasePath(path string) Option

WithBasePath sets the URL prefix for all auth routes.

func WithBootstrap ¶ added in v1.9.0

func WithBootstrap(opts ...BootstrapOption) Option

WithBootstrap enables automatic platform app bootstrap with optional customization.

func WithCeremonyStore ¶ added in v1.9.0

func WithCeremonyStore(s ceremony.Store) Option

WithCeremonyStore sets the ceremony state store for short-lived auth ceremony sessions (passkey, social OAuth, SSO, MFA SMS challenges). When not set, plugins fall back to an in-memory store.

func WithChronicle ¶ added in v1.9.0

func WithChronicle(c bridge.Chronicle) Option

WithChronicle sets the audit trail bridge.

func WithConfig ¶ added in v1.9.0

func WithConfig(cfg Config) Option

WithConfig sets the engine configuration.

func WithDB ¶ added in v1.9.0

func WithDB(db *grove.DB) Option

WithDB stores the grove database handle so plugins can create their own persistent stores via Engine.DB().

func WithDPoPReplayCache ¶ added in v1.9.0

func WithDPoPReplayCache(c dpop.ReplayCache) Option

WithDPoPReplayCache overrides the DPoP proof replay cache.

The default is a bounded in-process cache, which means replay detection does not span instances. Supply dpop.NewCeremonyReplayCache with a distributed ceremony store to close that gap, accepting one store round trip per authenticated request in exchange.

func WithDebug ¶ added in v1.9.0

func WithDebug(debug bool) Option

WithDebug enables verbose debug logging.

func WithDefaultTokenFormat ¶ added in v1.9.0

func WithDefaultTokenFormat(f tokenformat.Format) Option

WithDefaultTokenFormat sets the default token format for access tokens. When not set, opaque tokens (64-char hex) are used.

func WithDisableMigrate ¶ added in v1.9.0

func WithDisableMigrate() Option

WithDisableMigrate prevents automatic migration on Start.

func WithDisableRoutes ¶ added in v1.9.0

func WithDisableRoutes() Option

WithDisableRoutes prevents automatic route registration.

func WithDispatcher ¶ added in v1.9.0

func WithDispatcher(d bridge.Dispatcher) Option

WithDispatcher sets the background job queue bridge.

func WithDriverName ¶ added in v1.9.0

func WithDriverName(name string) Option

WithDriverName sets the grove driver name for plugin migration discovery.

func WithEventRelay ¶ added in v1.9.0

func WithEventRelay(r bridge.EventRelay) Option

WithEventRelay sets the webhook/event relay bridge.

func WithHerald ¶ added in v1.9.0

func WithHerald(h bridge.Herald) Option

WithHerald sets the unified notification system bridge. When configured, Herald replaces the separate Mailer and SMSSender bridges for notification delivery, providing multi-channel support with templates, scoped configuration, and user preference management.

func WithJWTFormat ¶ added in v1.9.0

func WithJWTFormat(appID string, jwtFmt *tokenformat.JWT) Option

WithJWTFormat registers a JWT token format for a specific app. Access tokens for this app will be signed JWTs instead of opaque tokens.

func WithKeyManager ¶ added in v1.9.0

func WithKeyManager(km bridge.KeyManager) Option

WithKeyManager sets the key management bridge.

func WithKeysmith ¶ added in v1.9.0

func WithKeysmith(ks *keysmith.Engine) Option

WithKeysmith sets the first-class Keysmith key management engine. When set, API key operations delegate to Keysmith (gaining rate limiting, policy enforcement, key rotation with grace periods, scope management, usage tracking, and multi-tenant support). This also sets the bridge.KeyManager for backward compatibility.

func WithLedger ¶ added in v1.9.0

func WithLedger(l bridge.Ledger) Option

WithLedger sets the billing/metering bridge.

func WithLedgerEngine ¶ added in v1.9.0

func WithLedgerEngine(l *xledger.Ledger) Option

WithLedgerEngine sets the first-class Ledger billing engine. When set, the subscription plugin can access the ledger store directly for list operations (plans, features, subscriptions). This also sets the bridge.Ledger adapter for backward compatibility.

func WithLockoutConfig ¶ added in v1.9.0

func WithLockoutConfig(cfg LockoutConfig) Option

WithLockoutConfig sets the lockout configuration.

func WithLockoutTracker ¶ added in v1.9.0

func WithLockoutTracker(t lockout.Tracker) Option

WithLockoutTracker sets the account lockout tracker.

func WithLogger ¶ added in v1.9.0

func WithLogger(logger log.Logger) Option

WithLogger sets the structured logger.

func WithMailer ¶ added in v1.9.0

func WithMailer(m bridge.Mailer) Option

WithMailer sets the transactional email bridge.

func WithMetrics ¶ added in v1.9.0

func WithMetrics(m bridge.MetricsCollector) Option

WithMetrics sets a bridge.MetricsCollector. The engine hands it every hook-bus event through RecordEvent and its operational signals (audit.record.failed, auth.lockout, control.degraded, and so on) through IncrementCounter. The tenant argument it receives is the app ID, which is unbounded: don't turn it into a metric label.

In a Forge app you don't need this. The extension passes the app's registry to WithMetricsSink, which records authsome's own metric families; see that option.

func WithMetricsSink ¶ added in v1.9.0

func WithMetricsSink(m gometrics.Metrics) Option

WithMetricsSink records authsome's metric families (sign-in attempts, MFA checks, refreshes, password hashing time, API key and OAuth2 token requests, third-party call latency, retention and the degraded-control counters) into m. A Forge app's registry, fapp.Metrics(), is one; the extension passes it in for you. Nil turns the families off.

Every family carries bounded labels only. None is labelled by app, tenant, user, provider name or route.

func WithPasswordHistory ¶ added in v1.9.0

func WithPasswordHistory(s account.PasswordHistoryStore) Option

WithPasswordHistory sets the password history store for preventing password reuse. Works in conjunction with PasswordConfig.HistoryCount.

func WithPlugin ¶ added in v1.9.0

func WithPlugin(p plugin.Plugin) Option

WithPlugin registers a plugin with the engine.

func WithPrincipalAuthTTL ¶ added in v1.9.0

func WithPrincipalAuthTTL(d time.Duration) Option

WithPrincipalAuthTTL sets how long a machine caller's allow verdict is cached, keyed on credential and source IP together. Defaults to five minutes. Denials are never cached regardless of this setting: see principalAuthGate.Authorize.

func WithProtectedResourceMetadataURL ¶ added in v1.9.0

func WithProtectedResourceMetadataURL(url string) Option

WithProtectedResourceMetadataURL sets the RFC 9728 metadata URL advertised in the WWW-Authenticate header on a 401. Leave it unset to emit no hint. It should be the origin-root path the oauth2provider plugin serves, for example https://auth.example.com/.well-known/oauth-protected-resource.

func WithRateLimiter ¶ added in v1.9.0

func WithRateLimiter(rl ratelimit.Limiter) Option

WithRateLimiter sets the rate limiter for brute-force protection.

func WithSMSSender ¶ added in v1.9.0

func WithSMSSender(s bridge.SMSSender) Option

WithSMSSender sets the SMS sending bridge for MFA verification codes.

func WithStore ¶ added in v1.9.0

func WithStore(s store.Store) Option

WithStore sets the persistence backend.

func WithStrategy ¶ added in v1.9.0

func WithStrategy(s strategy.Strategy, priority int) Option

WithStrategy registers an authentication strategy with the given priority. Lower priority values are evaluated first.

func WithTokenEncryptor ¶ added in v1.9.0

func WithTokenEncryptor(enc bridge.Encryptor) Option

WithTokenEncryptor sets the at-rest Encryptor used for sensitive opaque payloads (third-party OAuth access/refresh tokens, etc). When not set, the engine reads AUTHSOME_TOKEN_ENCRYPTION_KEY from the environment; if that is also unset, it falls back to a NoopEncryptor with a warning.

func WithVault ¶ added in v1.9.0

func WithVault(v bridge.Vault) Option

WithVault sets the secrets, feature flags, and configuration bridge.

func WithWarden ¶ added in v1.9.0

func WithWarden(w *warden.Engine) Option

WithWarden sets the required Warden authorization engine. All RBAC operations (roles, permissions, assignments, and permission checks) delegate to Warden's full RBAC+ReBAC+ABAC evaluation. Warden is required — NewEngine will return an error if this option is not provided. This also sets the bridge.Authorizer for backward compatibility.

type PasswordConfig ¶ added in v1.9.0

type PasswordConfig struct {
	// MinLength is the minimum password length (default: 12).
	MinLength int `json:"min_length"`

	// RequireUppercase requires at least one uppercase letter.
	RequireUppercase bool `json:"require_uppercase"`

	// RequireLowercase requires at least one lowercase letter.
	RequireLowercase bool `json:"require_lowercase"`

	// RequireDigit requires at least one digit.
	RequireDigit bool `json:"require_digit"`

	// RequireSpecial requires at least one special character.
	RequireSpecial bool `json:"require_special"`

	// BcryptCost is the bcrypt cost factor (default: 12).
	BcryptCost int `json:"bcrypt_cost"`

	// Algorithm is the password hashing algorithm ("bcrypt" or "argon2id").
	// Default: "bcrypt". When changed, existing hashes are auto-rehashed on
	// next successful login.
	Algorithm string `json:"algorithm"`

	// Argon2 holds parameters for Argon2id hashing (used when Algorithm = "argon2id").
	Argon2 Argon2Config `json:"argon2"`

	// CheckBreached enables HaveIBeenPwned breach checking on signup and
	// password change. Default: false.
	CheckBreached bool `json:"check_breached"`

	// HistoryCount is the number of previous passwords to remember. When set,
	// users cannot reuse any of their last N passwords. 0 = disabled.
	HistoryCount int `json:"history_count"`

	// MaxAgeDays forces password rotation after this many days. When a user
	// signs in with an expired password, the engine returns ErrPasswordExpired.
	// 0 = no expiry.
	MaxAgeDays int `json:"max_age_days"`
}

PasswordConfig configures password validation.

type Prefix ¶ added in v1.9.0

type Prefix = id.Prefix

Prefix identifies the entity type encoded in a TypeID.

type RateLimitConfig ¶

type RateLimitConfig struct {
	// SignInLimit is the max sign-in attempts per window (default: 5).
	SignInLimit int `json:"signin_limit"`

	// SignUpLimit is the max sign-up attempts per window (default: 3).
	SignUpLimit int `json:"signup_limit"`

	// RefreshLimit is the max token refresh attempts per window (default: 10).
	RefreshLimit int `json:"refresh_limit"`

	// IntrospectLimit is the max token introspection attempts per window (default: 20).
	IntrospectLimit int `json:"introspect_limit"`

	// ForgotPasswordLimit is the max forgot-password attempts per window (default: 3).
	ForgotPasswordLimit int `json:"forgot_password_limit"`

	// MFAChallengeLimit is the max MFA challenge attempts per window (default: 5).
	MFAChallengeLimit int `json:"mfa_challenge_limit"`

	// VerifyEmailLimit is the max email-verification (code/token submit)
	// attempts per window (default: 10). Defense-in-depth on top of the
	// per-user OTP attempt cap.
	VerifyEmailLimit int `json:"verify_email_limit"`

	// ResendVerificationLimit is the max resend-verification requests per
	// window (default: 3). Caps how fast fresh codes can be minted.
	ResendVerificationLimit int `json:"resend_verification_limit"`

	// SSFPushLimit is the max Shared Signals push deliveries accepted per
	// window per client (default: 60). Defence in depth only: rate limiting
	// is off unless Enabled is set, so the real controls on this endpoint are
	// the secret path, the bearer token, the signature, and the per-stream
	// circuit breaker.
	SSFPushLimit int `json:"ssf_push_limit"`

	// ResetPasswordLimit caps reset-password submissions per window (default: 5).
	ResetPasswordLimit int `json:"reset_password_limit"`

	// ChangePasswordLimit caps change-password submissions per window (default: 5).
	ChangePasswordLimit int `json:"change_password_limit"`

	// OAuthTokenLimit caps OAuth2 token, revoke and device-authorize
	// requests per window (default: 30).
	OAuthTokenLimit int `json:"oauth_token_limit"`

	// OAuthAuthorizeLimit caps OAuth2 authorize requests per window (default: 30).
	OAuthAuthorizeLimit int `json:"oauth_authorize_limit"`

	// PasskeyLimit caps passkey login and registration ceremonies per window (default: 10).
	PasskeyLimit int `json:"passkey_limit"`

	// SSOLimit caps public SSO requests per window (default: 20).
	SSOLimit int `json:"sso_limit"`

	// SCIMLimit caps SCIM requests per window (default: 60).
	SCIMLimit int `json:"scim_limit"`

	// WaitlistJoinLimit caps waitlist joins per window (default: 5).
	WaitlistJoinLimit int `json:"waitlist_join_limit"`

	// APIKeyFailureLimit caps failed API-key authentications per client
	// address per window (default: 20).
	APIKeyFailureLimit int `json:"api_key_failure_limit"`

	// WindowSeconds is the sliding window duration in seconds (default: 60).
	WindowSeconds int `json:"window_seconds"`

	// Enabled enables rate limiting (default: true).
	Enabled bool `json:"enabled"`

	// FailOpen lets requests through when the limiter itself fails
	// (default: false). Off, a limiter outage answers 503: an attacker who
	// can break the limiter must not get an unlimited window out of it.
	FailOpen bool `json:"fail_open"`
}

RateLimitConfig configures per-endpoint rate limits.

func PluginLimiter ¶ added in v1.9.0

func PluginLimiter(engine any) (rl ratelimit.Limiter, cfg RateLimitConfig, ok bool)

PluginLimiter hands a plugin the engine's limiter and rate-limit config. ok is false when the engine is not the concrete *Engine, the limiter is absent or rate limiting is off.

func (RateLimitConfig) Window ¶ added in v1.9.0

func (c RateLimitConfig) Window() time.Duration

Window returns the rate limit window as a time.Duration.

type RefreshOpts ¶ added in v1.9.0

type RefreshOpts struct {
	// IPAddress is the client IP to validate against the session's stored IP.
	IPAddress string
	// UserAgent is the client User-Agent to validate against the session's stored UA.
	UserAgent string

	// DPoPProof is the raw DPoP header from the refresh request. Required when
	// the session being refreshed is bound; ignored when it is not.
	DPoPProof string
	// Method and RequestURL describe the refresh request, for htm and htu.
	Method     string
	RequestURL string

	// KeepRefreshToken rotates only the access token. Auto-refresh sets it
	// when the refresh token is not exposed to the client: rotating a token
	// the client will never receive would strand the one it holds, and its
	// next explicit refresh would read as a replay.
	KeepRefreshToken bool
}

RefreshOpts carries optional client context for binding validation during refresh.

type RetentionConfig ¶ added in v1.9.0

type RetentionConfig struct {
	// SessionsDays keeps sessions this long after both their tokens expired
	// (default: 30).
	SessionsDays int `json:"sessions_days"`

	// VerificationsDays keeps verification codes this long after expiry
	// (default: 7).
	VerificationsDays int `json:"verifications_days"`

	// PasswordResetsDays keeps password reset tokens this long after expiry
	// (default: 7).
	PasswordResetsDays int `json:"password_resets_days"`

	// RevokedRefreshTokensDays keeps refresh-token revocation records this
	// long after revocation (default: 90). It must cover the refresh token
	// lifetime, or a replayed token could outlive its own revocation record.
	RevokedRefreshTokensDays int `json:"revoked_refresh_tokens_days"`

	// DeviceCodesDays keeps OAuth2 device codes this long after expiry
	// (default: 1).
	DeviceCodesDays int `json:"device_codes_days"`

	// AuthCodesDays keeps OAuth2 authorization codes this long after expiry
	// (default: 1).
	AuthCodesDays int `json:"auth_codes_days"`

	// BatchSize is how many rows one delete statement removes (default:
	// 1000). The sweeper repeats until a batch comes back short.
	BatchSize int `json:"batch_size"`
}

RetentionConfig sets how long rows that have outlived their purpose are kept before the retention sweeper removes them. Each value is a number of days counted from the row's expiry (or revocation); zero means the default and a negative value keeps rows of that kind forever.

func (RetentionConfig) Cutoff ¶ added in v1.9.0

func (c RetentionConfig) Cutoff(kind string, now time.Time) time.Time

Cutoff returns the instant before which rows of kind are removed, or the zero time when that kind is never swept.

func (RetentionConfig) Days ¶ added in v1.9.0

func (c RetentionConfig) Days(kind string) time.Duration

Days returns the retention window for one kind (a store.Retention* name) as a duration. Zero takes the kind's default; a negative value returns zero, meaning that kind is never swept.

type SessionConfig ¶

type SessionConfig struct {
	// TokenTTL is the lifetime of session tokens (default: 1h).
	TokenTTL time.Duration `json:"token_ttl"`

	// RefreshTokenTTL is the lifetime of refresh tokens (default: 30d).
	RefreshTokenTTL time.Duration `json:"refresh_token_ttl"`

	// MaxActiveSessions is the maximum number of concurrent sessions per user (0 = unlimited).
	MaxActiveSessions int `json:"max_active_sessions"`

	// RotateRefreshToken controls whether refresh operations issue a new
	// refresh token (invalidating the old one). Default: true.
	// When false, the same refresh token is reused until it expires.
	RotateRefreshToken *bool `json:"rotate_refresh_token"`

	// BindToIP rejects requests when the client IP differs from the
	// IP recorded at session creation. Default: false.
	BindToIP bool `json:"bind_to_ip"`

	// BindToDevice rejects requests when the User-Agent differs from
	// the one recorded at session creation. Default: false.
	BindToDevice bool `json:"bind_to_device"`

	// CleanupInterval is the interval at which expired sessions are
	// automatically purged. 0 = disabled.
	CleanupInterval time.Duration `json:"cleanup_interval"`
}

SessionConfig configures session behavior.

func (SessionConfig) ShouldRotateRefreshToken ¶ added in v1.9.0

func (c SessionConfig) ShouldRotateRefreshToken() bool

ShouldRotateRefreshToken returns whether refresh token rotation is enabled. Defaults to true when not explicitly configured.

type TokenEncryptionConfig ¶ added in v1.9.0

type TokenEncryptionConfig struct {
	// Keys is a comma-separated list of "kid:hex" pairs, hex being 64
	// characters (32 bytes). The first pair seals new values; every pair
	// decrypts, which is how a key is rotated: add the new one in front,
	// rewrite rows, then drop the old one. Falls back to the environment
	// when empty.
	Keys string `json:"keys"`
	// Strict refuses to decrypt a stored value with no envelope. Leave it
	// off until every row written before encryption was deployed has been
	// rewritten; a plaintext read after that is a fault, not a leftover.
	Strict bool `json:"strict"`
}

TokenEncryptionConfig configures at-rest encryption of provider tokens, MFA secrets and SSO connection secrets.

type UserExport ¶ added in v1.9.0

type UserExport struct {
	User     *user.User         `json:"user"`
	Sessions []*session.Session `json:"sessions"`
	Devices  []*device.Device   `json:"devices"`
	Extra    map[string]any     `json:"extra,omitempty"`
}

UserExport is the complete data export for a user (GDPR data portability).

type WebhookConfig ¶

type WebhookConfig struct {
	// AllowInsecureURLs permits http URLs and non-public addresses, for
	// development against a receiver on the same machine. Never set it in
	// production: it is the only thing standing between a webhook URL and
	// the network the service runs in.
	AllowInsecureURLs bool `json:"allow_insecure_urls"`

	// VerifyTimeout bounds the test delivery made when a webhook is
	// created or its URL changes (default: 10s).
	VerifyTimeout time.Duration `json:"verify_timeout"`
}

WebhookConfig governs webhook registration.

Directories ¶

Path Synopsis
_examples
forge command
Example: AuthSome as a Forge extension with all optional bridges.
Example: AuthSome as a Forge extension with all optional bridges.
standalone command
Example: standalone AuthSome server with memory store and password + social plugins.
Example: standalone AuthSome server with memory store and password + social plugins.
Package account defines the account lifecycle domain (signup, signin, verification, password reset).
Package account defines the account lifecycle domain (signup, signin, verification, password reset).
Package api provides Forge-style HTTP handlers for the AuthSome engine.
Package api provides Forge-style HTTP handlers for the AuthSome engine.
Package apikey defines the API key domain entity and its store interface.
Package apikey defines the API key domain entity and its store interface.
Package apitypes holds the request and response types that have no fields of their own and are shared across the whole HTTP surface.
Package apitypes holds the request and response types that have no fields of their own and are shared across the whole HTTP surface.
Package app defines the multi-app (tenant) domain entity and its store interface.
Package app defines the multi-app (tenant) domain entity and its store interface.
Package appclientconfig defines per-app client-facing configuration overrides.
Package appclientconfig defines per-app client-facing configuration overrides.
Package appsessionconfig defines per-app session configuration overrides.
Package appsessionconfig defines per-app session configuration overrides.
Package authprovider implements forge auth providers for authsome's authentication methods (session, JWT, API key).
Package authprovider implements forge auth providers for authsome's authentication methods (session, JWT, API key).
Package authz provides authorization primitives for authsome, including resource type schemas and a centralized Warden-backed authorization checker.
Package authz provides authorization primitives for authsome, including resource type schemas and a centralized Warden-backed authorization checker.
bootstrap
warden
Package wardenseed embeds authsome's default warden DSL programs and provides loaders + appliers that materialise them against a warden engine at bootstrap time.
Package wardenseed embeds authsome's default warden DSL programs and provides loaders + appliers that materialise them against a warden engine at bootstrap time.
Package bridge defines local interfaces for optional forgery extension integration.
Package bridge defines local interfaces for optional forgery extension integration.
chronicleadapter
Package chronicleadapter bridges AuthSome audit events to the Chronicle extension.
Package chronicleadapter bridges AuthSome audit events to the Chronicle extension.
dispatchadapter
Package dispatchadapter adapts the dispatch extension's engine to the authsome bridge.Dispatcher interface.
Package dispatchadapter adapts the dispatch extension's engine to the authsome bridge.Dispatcher interface.
heraldadapter
Package heraldadapter bridges AuthSome notification requests to the Herald extension.
Package heraldadapter bridges AuthSome notification requests to the Herald extension.
keysmithadapter
Package keysmithadapter bridges AuthSome key management to the Keysmith extension.
Package keysmithadapter bridges AuthSome key management to the Keysmith extension.
ledgeradapter
Package ledgeradapter adapts the ledger extension to the authsome bridge.Ledger interface.
Package ledgeradapter adapts the ledger extension to the authsome bridge.Ledger interface.
maileradapter
Package maileradapter provides bridge.Mailer implementations for transactional email delivery (Resend HTTP API and standard SMTP).
Package maileradapter provides bridge.Mailer implementations for transactional email delivery (Resend HTTP API and standard SMTP).
metricsadapter
Package metricsadapter implements bridge.MetricsCollector over a go-utils metrics registry, such as a Forge app's.
Package metricsadapter implements bridge.MetricsCollector over a go-utils metrics registry, such as a Forge app's.
relayadapter
Package relayadapter bridges AuthSome webhook events to the Relay extension.
Package relayadapter bridges AuthSome webhook events to the Relay extension.
smsadapter
Package smsadapter provides SMS sender implementations.
Package smsadapter provides SMS sender implementations.
vaultadapter
Package vaultadapter adapts the vault extension's services to the authsome bridge.Vault interface.
Package vaultadapter adapts the vault extension's services to the authsome bridge.Vault interface.
wardenadapter
Package wardenadapter bridges AuthSome authorization to the Warden extension.
Package wardenadapter bridges AuthSome authorization to the Warden extension.
Package captcha provides a small pluggable captcha verifier interface and a Cloudflare Turnstile implementation.
Package captcha provides a small pluggable captcha verifier interface and a Cloudflare Turnstile implementation.
Package ceremony provides a store interface for short-lived ceremony sessions used by authentication plugins (passkey, social, SSO, MFA).
Package ceremony provides a store interface for short-lived ceremony sessions used by authentication plugins (passkey, social, SSO, MFA).
clients
go module
Package device defines the device tracking domain entity and its store interface.
Package device defines the device tracking domain entity and its store interface.
Package dpop implements RFC 9449 Demonstrating Proof of Possession.
Package dpop implements RFC 9449 Demonstrating Proof of Possession.
Package environment defines the environment domain entity and its store interface.
Package environment defines the environment domain entity and its store interface.
Package extension adapts AuthSome as a Forge extension.
Package extension adapts AuthSome as a Forge extension.
contract
Package contract wires authsome into the Forge dashboard's contract path.
Package contract wires authsome into the Forge dashboard's contract path.
Package formconfig defines dynamic form configuration types for authsome.
Package formconfig defines dynamic form configuration types for authsome.
Package hook provides a global event bus that fires on every engine action.
Package hook provides a global event bus that fires on every engine action.
Package id defines TypeID-based identity types for all AuthSome entities.
Package id defines TypeID-based identity types for all AuthSome entities.
internal
boundedmap
Package boundedmap is a map with a capacity: once full, putting a new key evicts the least recently used one.
Package boundedmap is a map with a capacity: once full, putting a new key evicts the least recently used one.
browserbind
Package browserbind ties a browser-driven OAuth or SSO ceremony to the browser that started it.
Package browserbind ties a browser-driven OAuth or SSO ceremony to the browser that started it.
doclint
Package doclint holds the repository's self-checks: tests that read the tree rather than the code, such as whether an environment file is tracked or whether the documentation names identifiers that exist.
Package doclint holds the repository's self-checks: tests that read the tree rather than the code, such as whether an environment file is tracked or whether the documentation names identifiers that exist.
dpoptest
Package dpoptest mints DPoP proofs for tests.
Package dpoptest mints DPoP proofs for tests.
ipmask
Package ipmask reduces a client address to its network so a record can keep where a request came from without keeping who.
Package ipmask reduces a client address to its network so a record can keep where a request came from without keeping who.
jwkutil
Package jwkutil converts between JSON Web Keys and Go public keys, and computes RFC 7638 thumbprints.
Package jwkutil converts between JSON Web Keys and Go public keys, and computes RFC 7638 thumbprints.
mask
Package mask shortens personal identifiers for log lines.
Package mask shortens personal identifiers for log lines.
resourceuri
Package resourceuri holds the RFC 8707 section 2 syntax rule for a resource indicator, in one place both the OAuth2 provider and the core settings can reach.
Package resourceuri holds the RFC 8707 section 2 syntax rule for a resource indicator, in one place both the OAuth2 provider and the core settings can reach.
safeurl
Package safeurl decides whether an operator-supplied URL may be called from inside the service.
Package safeurl decides whether an operator-supplied URL may be called from inside the service.
secutil
Package secutil provides shared test helpers for authsome security work.
Package secutil provides shared test helpers for authsome security work.
sqliteguard
Package sqliteguard holds a static check over the SQLite store code.
Package sqliteguard holds a static check over the SQLite store code.
sqliteschema
Package sqliteschema provides the SQLite table-rebuild procedure used by migrations that must change a column's declared type.
Package sqliteschema provides the SQLite table-rebuild procedure used by migrations that must change a column's declared type.
telemetry
Package telemetry holds authsome's tracing and metric plumbing: the tracer every authsome span is started on, the helpers that mark a span failed without ever putting error text on it, the bounded values metric labels take, and the instruments recorded through a go-utils metrics sink.
Package telemetry holds authsome's tracing and metric plumbing: the tracer every authsome span is started on, the helpers that mark a span failed without ever putting error text on it, the bounded values metric labels take, and the instruments recorded through a go-utils metrics sink.
telemetry/telemetrytest
Package telemetrytest holds the helpers authsome's telemetry tests share: a real Forge metrics registry with its Prometheus scrape, and a span recorder installed as the global tracer provider.
Package telemetrytest holds the helpers authsome's telemetry tests share: a real Forge metrics registry with its Prometheus scrape, and a span recorder installed as the global tracer provider.
Package lockout provides account lockout tracking for AuthSome.
Package lockout provides account lockout tracking for AuthSome.
Package middleware provides HTTP middleware for authentication and context resolution.
Package middleware provides HTTP middleware for authentication and context resolution.
Package notification defines the notification domain entity and its store interface.
Package notification defines the notification domain entity and its store interface.
Package organization defines the organization domain entity and its store interface.
Package organization defines the organization domain entity and its store interface.
Package page holds the request and answer shape of a bounded list.
Package page holds the request and answer shape of a bounded list.
Package plugin: authz.go — shared route-group guards for plugins.
Package plugin: authz.go — shared route-group guards for plugins.
plugins
agentauth
Package agentauth: see doc.go for the package doc comment and the host integration contract.
Package agentauth: see doc.go for the package doc comment and the host integration contract.
anomaly
contract.go: Wires the anomaly plugin into the forge-dashboard contract surface via plugin.ContractContributor.
contract.go: Wires the anomaly plugin into the forge-dashboard contract surface via plugin.ContractContributor.
anomaly/contract
Package contract wires the anomaly plugin's deep-link settings page into the forge-dashboard contract registry.
Package contract wires the anomaly plugin's deep-link settings page into the forge-dashboard contract registry.
apikey
contract.go: Wires the apikey plugin into the forge-dashboard contract surface via plugin.ContractContributor.
contract.go: Wires the apikey plugin into the forge-dashboard contract surface via plugin.ContractContributor.
apikey/contract
Package contract wires the apikey plugin's intent surface into the forge-dashboard contract registry.
Package contract wires the apikey plugin's intent surface into the forge-dashboard contract registry.
consent
contract.go: Wires the consent plugin into the forge-dashboard contract surface via plugin.ContractContributor.
contract.go: Wires the consent plugin into the forge-dashboard contract surface via plugin.ContractContributor.
consent/consenttest
Package consenttest provides a backend-agnostic conformance suite for the consent.Store interface.
Package consenttest provides a backend-agnostic conformance suite for the consent.Store interface.
consent/contract
Package contract is the consent plugin's forge-dashboard contract surface.
Package contract is the consent plugin's forge-dashboard contract surface.
deviceverify
contract.go: Wires the deviceverify plugin into the forge-dashboard contract surface via plugin.ContractContributor.
contract.go: Wires the deviceverify plugin into the forge-dashboard contract surface via plugin.ContractContributor.
deviceverify/contract
Package contract wires the deviceverify plugin's deep-link settings page into the forge-dashboard contract registry.
Package contract wires the deviceverify plugin's deep-link settings page into the forge-dashboard contract registry.
email
contract.go: Wires the email plugin into the forge-dashboard contract surface.
contract.go: Wires the email plugin into the forge-dashboard contract surface.
email/contract
Package contract wires the email plugin's deep-link settings page into the forge-dashboard contract registry.
Package contract wires the email plugin's deep-link settings page into the forge-dashboard contract registry.
geofence
contract.go: Wires the geofence plugin into the forge-dashboard contract surface via plugin.ContractContributor.
contract.go: Wires the geofence plugin into the forge-dashboard contract surface via plugin.ContractContributor.
geofence/contract
Package contract wires the geofence plugin's deep-link settings page into the forge-dashboard contract registry.
Package contract wires the geofence plugin's deep-link settings page into the forge-dashboard contract registry.
geoip
contract.go: Wires the geoip plugin into the forge-dashboard contract surface via plugin.ContractContributor.
contract.go: Wires the geoip plugin into the forge-dashboard contract surface via plugin.ContractContributor.
geoip/contract
Package contract wires the geoip plugin's deep-link settings page into the forge-dashboard contract registry.
Package contract wires the geoip plugin's deep-link settings page into the forge-dashboard contract registry.
impossibletravel
contract.go: Wires the impossibletravel plugin into the forge-dashboard contract surface via plugin.ContractContributor.
contract.go: Wires the impossibletravel plugin into the forge-dashboard contract surface via plugin.ContractContributor.
impossibletravel/contract
Package contract wires the impossibletravel plugin's deep-link settings page into the forge-dashboard contract registry.
Package contract wires the impossibletravel plugin's deep-link settings page into the forge-dashboard contract registry.
ipreputation
contract.go: Wires the ipreputation plugin into the forge-dashboard contract surface via plugin.ContractContributor.
contract.go: Wires the ipreputation plugin into the forge-dashboard contract surface via plugin.ContractContributor.
ipreputation/contract
Package contract wires the ipreputation plugin's deep-link settings page into the forge-dashboard contract registry.
Package contract wires the ipreputation plugin's deep-link settings page into the forge-dashboard contract registry.
magiclink
contract.go: Wires the magiclink plugin into the forge-dashboard contract surface via plugin.ContractContributor.
contract.go: Wires the magiclink plugin into the forge-dashboard contract surface via plugin.ContractContributor.
magiclink/contract
Package contract wires the magiclink plugin's deep-link settings page into the forge-dashboard contract registry.
Package contract wires the magiclink plugin's deep-link settings page into the forge-dashboard contract registry.
mfa
contract.go: Wires the mfa plugin into the forge-dashboard contract surface via plugin.ContractContributor.
contract.go: Wires the mfa plugin into the forge-dashboard contract surface via plugin.ContractContributor.
mfa/contract
Package contract wires the mfa plugin's deep-link settings page into the forge-dashboard contract registry.
Package contract wires the mfa plugin's deep-link settings page into the forge-dashboard contract registry.
mfa/mfatest
Package mfatest provides a backend-agnostic conformance suite for the mfa.Store interface.
Package mfatest provides a backend-agnostic conformance suite for the mfa.Store interface.
notification
Package notification provides a Herald-backed notification plugin for AuthSome.
Package notification provides a Herald-backed notification plugin for AuthSome.
notification/contract
Package contract wires the notification plugin's deep-link settings page into the forge-dashboard contract registry.
Package contract wires the notification plugin's deep-link settings page into the forge-dashboard contract registry.
oauth2provider
admin_clients.go: the admin surface for editing an OAuth2 client that already exists.
admin_clients.go: the admin surface for editing an OAuth2 client that already exists.
oauth2provider/contract
Package contract wires the oauth2provider plugin's deep-link settings page into the forge-dashboard contract registry.
Package contract wires the oauth2provider plugin's deep-link settings page into the forge-dashboard contract registry.
oauth2provider/oauth2test
Package oauth2test provides a backend-agnostic conformance suite for the oauth2provider.Store interface.
Package oauth2test provides a backend-agnostic conformance suite for the oauth2provider.Store interface.
organization
contract.go: Wires the organization plugin into the forge-dashboard contract surface via plugin.ContractContributor.
contract.go: Wires the organization plugin into the forge-dashboard contract surface via plugin.ContractContributor.
organization/contract
Package contract wires the organization plugin's intent surface into the forge-dashboard contract registry.
Package contract wires the organization plugin's intent surface into the forge-dashboard contract registry.
passkey
contract.go: Wires the passkey plugin into the forge-dashboard contract surface via plugin.ContractContributor.
contract.go: Wires the passkey plugin into the forge-dashboard contract surface via plugin.ContractContributor.
passkey/contract
Package contract wires the passkey plugin's deep-link settings page into the forge-dashboard contract registry.
Package contract wires the passkey plugin's deep-link settings page into the forge-dashboard contract registry.
passkey/passkeytest
Package passkeytest provides a backend-agnostic conformance suite for the passkey.Store interface.
Package passkeytest provides a backend-agnostic conformance suite for the passkey.Store interface.
password
contract.go: Wires the password plugin into the forge-dashboard contract surface.
contract.go: Wires the password plugin into the forge-dashboard contract surface.
password/contract
Package contract registers the password plugin's forge-dashboard contract surface: three intents (password.settings, password.policy, password.settingsUpdate) backed by the settings.Manager, plus a standalone /auth/password page that extends the auth contributor's /settings page with a password-policy panel.
Package contract registers the password plugin's forge-dashboard contract surface: three intents (password.settings, password.policy, password.settingsUpdate) backed by the settings.Manager, plus a standalone /auth/password page that extends the auth contributor's /settings page with a password-policy panel.
phone
contract.go: Wires the phone plugin into the forge-dashboard contract surface via plugin.ContractContributor.
contract.go: Wires the phone plugin into the forge-dashboard contract surface via plugin.ContractContributor.
phone/contract
Package contract wires the phone plugin's deep-link settings page into the forge-dashboard contract registry.
Package contract wires the phone plugin's deep-link settings page into the forge-dashboard contract registry.
retention
Package retention mirrors authsome auth activity into a CRM.
Package retention mirrors authsome auth activity into a CRM.
riskengine
contract.go: Wires the riskengine plugin into the forge-dashboard contract surface via plugin.ContractContributor.
contract.go: Wires the riskengine plugin into the forge-dashboard contract surface via plugin.ContractContributor.
riskengine/contract
Package contract wires the riskengine plugin's deep-link settings page into the forge-dashboard contract registry.
Package contract wires the riskengine plugin's deep-link settings page into the forge-dashboard contract registry.
scim
contract.go: Wires the scim plugin into the forge-dashboard contract surface via plugin.ContractContributor.
contract.go: Wires the scim plugin into the forge-dashboard contract surface via plugin.ContractContributor.
scim/contract
Package contract wires the scim plugin's deep-link settings page into the forge-dashboard contract registry.
Package contract wires the scim plugin's deep-link settings page into the forge-dashboard contract registry.
scim/scimtest
Package scimtest is the store contract every SCIM store implementation must satisfy.
Package scimtest is the store contract every SCIM store implementation must satisfy.
sharedsignals
Package sharedsignals implements the OpenID Shared Signals Framework and CAEP.
Package sharedsignals implements the OpenID Shared Signals Framework and CAEP.
sharedsignals/caep
Package caep implements the OpenID CAEP event types and the RFC 9493 subject identifiers they carry.
Package caep implements the OpenID CAEP event types and the RFC 9493 subject identifiers they carry.
sharedsignals/jwksclient
Package jwksclient fetches and caches JSON Web Key Sets for inbound token verification.
Package jwksclient fetches and caches JSON Web Key Sets for inbound token verification.
sharedsignals/setjwt
Package setjwt parses and validates RFC 8417 Security Event Tokens.
Package setjwt parses and validates RFC 8417 Security Event Tokens.
sharedsignals/ssftest
Package ssftest provides a backend-agnostic conformance suite for the sharedsignals.Store interface.
Package ssftest provides a backend-agnostic conformance suite for the sharedsignals.Store interface.
social
contract.go: Wires the social plugin into the forge-dashboard contract surface via plugin.ContractContributor.
contract.go: Wires the social plugin into the forge-dashboard contract surface via plugin.ContractContributor.
social/contract
Package contract wires the social plugin's deep-link settings page into the forge-dashboard contract registry.
Package contract wires the social plugin's deep-link settings page into the forge-dashboard contract registry.
social/socialtest
Package socialtest provides a backend-agnostic conformance suite for the social.Store interface.
Package socialtest provides a backend-agnostic conformance suite for the social.Store interface.
sso
contract.go: Wires the sso plugin into the forge-dashboard contract surface via plugin.ContractContributor.
contract.go: Wires the sso plugin into the forge-dashboard contract surface via plugin.ContractContributor.
sso/contract
Package contract wires the sso plugin's deep-link settings page into the forge-dashboard contract registry.
Package contract wires the sso plugin's deep-link settings page into the forge-dashboard contract registry.
sso/ssotest
Package ssotest provides a backend-agnostic conformance suite for the sso.Store interface.
Package ssotest provides a backend-agnostic conformance suite for the sso.Store interface.
subscription
contract.go: Wires the subscription plugin into the forge-dashboard contract surface via plugin.ContractContributor.
contract.go: Wires the subscription plugin into the forge-dashboard contract surface via plugin.ContractContributor.
subscription/contract
Package contract wires the subscription plugin's intent surface into the forge-dashboard contract registry.
Package contract wires the subscription plugin's intent surface into the forge-dashboard contract registry.
vpndetect
contract.go: Wires the vpndetect plugin into the forge-dashboard contract surface via plugin.ContractContributor.
contract.go: Wires the vpndetect plugin into the forge-dashboard contract surface via plugin.ContractContributor.
vpndetect/contract
Package contract wires the vpndetect plugin's deep-link settings page into the forge-dashboard contract registry.
Package contract wires the vpndetect plugin's deep-link settings page into the forge-dashboard contract registry.
waitlist
contract.go: Wires the waitlist plugin into the forge-dashboard contract surface via plugin.ContractContributor.
contract.go: Wires the waitlist plugin into the forge-dashboard contract surface via plugin.ContractContributor.
waitlist/contract
Package contract is the waitlist plugin's forge-dashboard contract surface.
Package contract is the waitlist plugin's forge-dashboard contract surface.
waitlist/waitlisttest
Package waitlisttest provides a backend-agnostic conformance suite for the waitlist.Store interface.
Package waitlisttest provides a backend-agnostic conformance suite for the waitlist.Store interface.
Package principal defines the caller abstraction shared by human users, AI agents and workloads.
Package principal defines the caller abstraction shared by human users, AI agents and workloads.
Package ratelimit provides rate limiting for AuthSome endpoints.
Package ratelimit provides rate limiting for AuthSome endpoints.
Package rbac provides role-based access control for AuthSome.
Package rbac provides role-based access control for AuthSome.
sdk
go
sdkgen
cmd command
Command authsome-sdkgen generates typed client SDKs from AuthSome API metadata.
Command authsome-sdkgen generates typed client SDKs from AuthSome API metadata.
cmd/specgen command
Command specgen dumps the AuthSome OpenAPI spec by booting the engine with all known plugins, registering their routes on a Forge router, and serializing the dynamically-generated spec to JSON.
Command specgen dumps the AuthSome OpenAPI spec by booting the engine with all known plugins, registering their routes on a Forge router, and serializing the dynamically-generated spec to JSON.
dart
Package dart generates a typed Dart client SDK from an AuthSome OpenAPI spec.
Package dart generates a typed Dart client SDK from an AuthSome OpenAPI spec.
golang
Package golang generates a typed Go client SDK from an AuthSome OpenAPI spec.
Package golang generates a typed Go client SDK from an AuthSome OpenAPI spec.
openapi
Package openapi generates OpenAPI 3.1 specifications from AuthSome engine metadata.
Package openapi generates OpenAPI 3.1 specifications from AuthSome engine metadata.
typescript
Package typescript generates a typed TypeScript client SDK from an AuthSome OpenAPI spec.
Package typescript generates a typed TypeScript client SDK from an AuthSome OpenAPI spec.
Package serviceaccount defines the service account domain entity and its store interface.
Package serviceaccount defines the service account domain entity and its store interface.
Package session defines the session domain entity and its store interface.
Package session defines the session domain entity and its store interface.
Package settings provides a dynamic, plugin-extensible configuration system for AuthSome.
Package settings provides a dynamic, plugin-extensible configuration system for AuthSome.
Package store defines the aggregate persistence interface.
Package store defines the aggregate persistence interface.
memory
Package memory provides an in-memory implementation of store.Store for testing.
Package memory provides an in-memory implementation of store.Store for testing.
mongo
Package mongo implements the AuthSome store interface using MongoDB via Grove ORM.
Package mongo implements the AuthSome store interface using MongoDB via Grove ORM.
postgres
Package postgres implements the AuthSome store interface using grove ORM (PostgreSQL).
Package postgres implements the AuthSome store interface using grove ORM (PostgreSQL).
sqlite
Package sqlite implements the AuthSome store interface using grove ORM (SQLite).
Package sqlite implements the AuthSome store interface using grove ORM (SQLite).
storetest
Package storetest provides a backend-agnostic conformance suite for the store.Store interface.
Package storetest provides a backend-agnostic conformance suite for the store.Store interface.
Package strategy defines the authentication strategy system.
Package strategy defines the authentication strategy system.
Package testutil provides a ready-to-use HTTP test server backed by a real AuthSome engine with in-memory stores and all standard plugins.
Package testutil provides a ready-to-use HTTP test server backed by a real AuthSome engine with in-memory stores and all standard plugins.
Package tokenformat provides pluggable token generation strategies.
Package tokenformat provides pluggable token generation strategies.
Package user defines the user domain entity and its store interface.
Package user defines the user domain entity and its store interface.
Package webhook defines the webhook domain entity and its store interface.
Package webhook defines the webhook domain entity and its store interface.

Jump to

Keyboard shortcuts

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