embedded

package
v0.134.0 Latest Latest
Warning

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

Go to latest
Published: Sep 24, 2026 License: MIT Imports: 63 Imported by: 0

Documentation

Overview

Package embedded is the AuthKit engine: the concrete *engine a host constructs with New and holds directly, and the authkit/authhttp transport mounts. Data types hosts exchange with it live in the root authkit package; configuration, dependencies, sender/provider interfaces and engine-only types live here.

Index

Constants

View Source
const (
	DefaultDelegatedTTLFloor   = time.Minute
	DefaultDelegatedTTLDefault = 15 * time.Minute
	DefaultDelegatedTTLCeiling = time.Hour
)

Delegated mint defaults (#261). Applied to unset DelegatedConfig fields before the floor <= default <= ceiling boot check.

View Source
const (
	ApplicationTierRegistered = authkit.ApplicationTierRegistered
	ApplicationTierApproved   = authkit.ApplicationTierApproved

	ApplicationTrustRootManual = authkit.ApplicationTrustRootManual
	ApplicationTrustRootDomain = authkit.ApplicationTrustRootDomain
	ApplicationTrustRootUser   = authkit.ApplicationTrustRootUser

	ApplicationWellKnownPath = authkit.ApplicationWellKnownPath
)

Re-exported tier/trust-root constants and document types.

View Source
const (
	PasswordlessModeCode = "code"
	PasswordlessModeLink = "link"
	PasswordlessModeBoth = "both"

	PasswordlessChannelEmail = "email"
	PasswordlessChannelSMS   = "sms"
)
View Source
const (
	RegistrationVerificationNone     = authkit.RegistrationVerificationNone
	RegistrationVerificationOptional = authkit.RegistrationVerificationOptional
	RegistrationVerificationRequired = authkit.RegistrationVerificationRequired
)
View Source
const (
	RegistrationModeOpen       = authkit.RegistrationModeOpen
	RegistrationModeInviteOnly = authkit.RegistrationModeInviteOnly
	RegistrationModeClosed     = authkit.RegistrationModeClosed
)
View Source
const (
	AdminUserStatusActive  = authkit.AdminUserStatusActive
	AdminUserStatusBanned  = authkit.AdminUserStatusBanned
	AdminUserStatusDeleted = authkit.AdminUserStatusDeleted
	AdminUserStatusAny     = authkit.AdminUserStatusAny
)
View Source
const (
	AdminUserSortCreatedAt = authkit.AdminUserSortCreatedAt
	AdminUserSortLastLogin = authkit.AdminUserSortLastLogin
	AdminUserSortUsername  = authkit.AdminUserSortUsername
	AdminUserSortEmail     = authkit.AdminUserSortEmail
)
View Source
const (
	ImportUnverifiedSolanaLinkInserted = authkit.ImportUnverifiedSolanaLinkInserted
	ImportUnverifiedSolanaLinkSkipped  = authkit.ImportUnverifiedSolanaLinkSkipped
	ImportUnverifiedSolanaLinkRejected = authkit.ImportUnverifiedSolanaLinkRejected
)
View Source
const (
	// ImportStatusInserted: the user row was created.
	ImportStatusInserted = authkit.ImportStatusInserted
	// ImportStatusSkipped: a matching user already existed (by username/email/
	// phone), or the row duplicated an earlier row in the same batch. Skipped
	// rows are left untouched — bulk import is insert-or-skip, never overwrite,
	// so a re-run is idempotent and never clobbers data a user changed after
	// import.
	ImportStatusSkipped = authkit.ImportStatusSkipped
	// ImportStatusRejected: the row failed validation/normalization (bad email,
	// username, phone) and was not imported.
	ImportStatusRejected = authkit.ImportStatusRejected
)
View Source
const (
	// ServiceJWTTokenUse + DefaultServiceJWTLifetime are defined in authkit
	// (core-free) and re-exported here.
	ServiceJWTTokenUse = authkit.ServiceJWTTokenUse
	// ServiceJWTType is the JOSE typ header AuthKit stamps on minted service JWTs.
	ServiceJWTType            = "service+jwt"
	DefaultServiceJWTLifetime = authkit.DefaultServiceJWTLifetime
)
View Source
const (
	TwoFactorDisabled = authkit.TwoFactorDisabled
	TwoFactorOptional = authkit.TwoFactorOptional
	TwoFactorRequired = authkit.TwoFactorRequired
)
View Source
const (
	TwoFactorEmail = authkit.TwoFactorEmail
	TwoFactorSMS   = authkit.TwoFactorSMS
	TwoFactorTOTP  = authkit.TwoFactorTOTP
)
View Source
const (
	RootPersona   = authkit.RootPersona
	OwnerRoleName = authkit.OwnerRole
)
View Source
const (
	// Operator dashboard visibility.
	PermRootResourcesRead = "root:resources:read" // read root/admin resources

	// Identity / account directory.
	PermRootUsersBan     = "root:users:ban"     // ban / unban an account
	PermRootUsersRecover = "root:users:recover" // revoke every session of an account
	PermRootUsersDelete  = "root:users:delete"  // soft-delete an account
	// PermRootUsersInvite authorizes minting a STANDALONE account-registration
	// invite (#147): inviting someone to create an account, independent of any
	// permission-group invite. owner holds it via root:*; hosts may grant it to a
	// bounded operator role so non-owner staff can invite new accounts.
	PermRootUsersInvite = "root:users:invite" // invite someone to create an account

	// Operator management of roles/credentials.
	PermRootRolesManage       = "root:roles:manage"       // define/inspect platform-operator roles
	PermRootCredentialsManage = "root:credentials:manage" // manage/revoke machine credentials as an operator
)
View Source
const (
	SubjectKindUser      = authkit.SubjectKindUser
	SubjectKindRemoteApp = authkit.SubjectKindRemoteApp
)

SubjectKindUser / SubjectKindRemoteApplication select the concrete group-role table used for a principal.

View Source
const (
	RemoteAppModeJWKS   = authkit.RemoteAppModeJWKS
	RemoteAppModeStatic = authkit.RemoteAppModeStatic
)

Remote-application trust modes (#74). A remote_application is a federation PRINCIPAL whose credential is a key, with exactly one trust source:

jwks   — keys fetched + refreshed from JWKSURI; rotation is publishing a new
         kid at the same URL.
static — authorized_keys-style human-managed PEM list for principals without
         a JWKS endpoint; manual rotation by design.

Remote-application trust modes are defined in authkit (core-free) and re-exported here.

View Source
const (
	AssuranceLevelPassword = "urn:authkit:loa:1"
	AssuranceLevelMFA      = "urn:authkit:loa:2"
)
View Source
const (
	SolanaSNSStatusPending  = "pending"
	SolanaSNSStatusResolved = "resolved"
	SolanaSNSStatusNotFound = "not_found"
	SolanaSNSStatusError    = "error"
	SolanaSNSStatusStale    = "stale"
)
View Source
const DefaultBootstrapManifestPath = "/etc/authkit/bootstrap.yaml"
View Source
const DelegatedAccessTokenType = jwtkit.DelegatedAccessTokenType

DelegatedAccessTokenType is the canonical JOSE `typ` header value for a delegated access token.

View Source
const HashAlgoLegacyResetRequired = "legacy-reset-required"

HashAlgoLegacyResetRequired marks user_passwords rows migrated from legacy systems whose stored hashes can never verify (DES crypt, md5-crypt, corrupted values). The raw legacy hash is preserved in password_hash for forensics only; the sole way forward for these accounts is a password reset.

View Source
const RemoteApplicationAccessTokenType = jwtkit.RemoteApplicationAccessTokenType

RemoteApplicationAccessTokenType is the JOSE `typ` for a remote application access token.

View Source
const SensitiveActionFreshAuthWindow = 15 * time.Minute
View Source
const SolanaProviderSlug = "solana"

SolanaProviderSlug is the provider slug used for Solana wallets.

Variables

View Source
var (
	ErrApplicationRegistrationDisabled = authkit.ErrApplicationRegistrationDisabled
	ErrApplicationDomainInvalid        = authkit.ErrApplicationDomainInvalid
	ErrApplicationDomainConflict       = authkit.ErrApplicationDomainConflict
	ErrApplicationDocumentFetchFailed  = authkit.ErrApplicationDocumentFetchFailed
	ErrApplicationDocumentInvalid      = authkit.ErrApplicationDocumentInvalid
	ErrApplicationSlugConflict         = authkit.ErrApplicationSlugConflict
	ErrApplicationIssuerConflict       = authkit.ErrApplicationIssuerConflict
)

Re-exported sentinels (defined in authkit, core-free).

View Source
var (
	ErrAccountExistsLinkRequired    = authkit.ErrAccountExistsLinkRequired
	ErrProviderLinkFailed           = authkit.ErrProviderLinkFailed
	ErrUserCreationFailed           = authkit.ErrUserCreationFailed
	ErrProviderAlreadyLinked        = authkit.ErrProviderAlreadyLinked
	ErrProviderChangeRequiresUnlink = authkit.ErrProviderChangeRequiresUnlink
)
View Source
var (
	ErrInvalidCredentials          = authkit.ErrInvalidCredentials
	ErrEmailVerificationSendFailed = authkit.ErrEmailVerificationSendFailed
	ErrPhoneVerificationSendFailed = authkit.ErrPhoneVerificationSendFailed
)

Password-login errors shared with the transport.

View Source
var (
	ErrPasskeyNotFound                 = authkit.ErrPasskeyNotFound
	ErrPasskeyUserVerificationRequired = authkit.ErrPasskeyUserVerificationRequired
	ErrPasskeyCloneDetected            = authkit.ErrPasskeyCloneDetected
)
View Source
var (
	ErrSIWSChallengeNotFound      = authkit.ErrSIWSChallengeNotFound
	ErrSIWSChallengeExpired       = authkit.ErrSIWSChallengeExpired
	ErrSIWSChallengeMismatch      = authkit.ErrSIWSChallengeMismatch
	ErrSIWSAddressMismatch        = authkit.ErrSIWSAddressMismatch
	ErrSIWSDomainInvalid          = authkit.ErrSIWSDomainInvalid
	ErrSIWSTimestampInvalid       = authkit.ErrSIWSTimestampInvalid
	ErrSIWSSignatureInvalid       = authkit.ErrSIWSSignatureInvalid
	ErrWalletAlreadyLinked        = authkit.ErrWalletAlreadyLinked
	ErrWalletChangeRequiresUnlink = authkit.ErrWalletChangeRequiresUnlink
)

SIWS sentinel aliases (root authkit sentinels) so the SIWS verification path returns typed errors the HTTP layer maps with errors.Is — see http/solana_siws.go.

View Source
var (
	ErrInvalidTwoFAMethod       = authkit.ErrInvalidTwoFAMethod
	ErrPhoneNumberRequired      = authkit.ErrPhoneNumberRequired
	ErrPhoneNumberMustBeE164    = authkit.ErrPhoneNumberMustBeE164
	ErrInvalidCode              = authkit.ErrInvalidCode
	ErrTwoFACodeExpired         = authkit.ErrTwoFACodeExpired
	ErrPhoneTwoFAUnavailable    = authkit.ErrPhoneTwoFAUnavailable
	ErrTwoFASetupCodeSendFailed = authkit.ErrTwoFASetupCodeSendFailed
	ErrTwoFAEnableFailed        = authkit.ErrTwoFAEnableFailed
	ErrTwoFAFactorExists        = authkit.ErrTwoFAFactorExists
)
View Source
var (
	ErrInvalidAccessToken = authkit.ErrInvalidAccessToken
	ErrAccessTokenRevoked = authkit.ErrAccessTokenRevoked
	ErrAccessTokenExpired = authkit.ErrAccessTokenExpired
)

Token sentinel errors are defined in authkit and re-exported here for backward compatibility (so core.X callers and errors.Is checks are unaffected).

View Source
var (
	APIKeyMarker    = authkit.APIKeyMarker
	HasAPIKeyPrefix = authkit.HasAPIKeyPrefix
	FormatAPIKey    = authkit.FormatAPIKey
	ParseAPIKey     = authkit.ParseAPIKey
)

API-key marker/parse/format helpers are defined in authkit (core-free) and re-exported here for backward compatibility.

View Source
var (
	ErrInvalidBootstrapManifest  = authkit.ErrInvalidBootstrapManifest
	ErrBootstrapDatabaseNotEmpty = authkit.ErrBootstrapDatabaseNotEmpty
)
View Source
var (
	// ErrInviteLinkNotFound indicates no invite link matched the code/lookup.
	ErrInviteLinkNotFound = authkit.ErrInviteLinkNotFound
	// ErrInviteLinkExpired indicates the link's expires_at has passed.
	ErrInviteLinkExpired = authkit.ErrInviteLinkExpired
	// ErrInviteLinkRevoked indicates the link was revoked by a manager.
	ErrInviteLinkRevoked = authkit.ErrInviteLinkRevoked
	// ErrExternalInvitesDisabled indicates invite links are off because the
	// deployment's registration mode does not permit invited self-registration.
	ErrExternalInvitesDisabled = authkit.ErrExternalInvitesDisabled
)
View Source
var (
	// ErrInvalidServiceJWT is defined in authkit and re-exported here.
	ErrInvalidServiceJWT = authkit.ErrInvalidServiceJWT
	ErrMissingSigner     = authkit.ErrMissingSigner
)
View Source
var (
	// ErrInsufficientRoleAuthority: the actor lacks `<persona>:members:manage` in
	// the group, so it may not change role assignments there at all.
	ErrInsufficientRoleAuthority = authkit.ErrInsufficientRoleAuthority
	// ErrRoleAssignmentEscalation: the target role confers a permission the actor
	// does not itself hold — assigning (or revoking) it would be privilege
	// escalation.
	ErrRoleAssignmentEscalation = authkit.ErrRoleAssignmentEscalation
	// ErrAccountAuthorityEscalation: the target account holds a root grant the
	// actor does not — banning, deleting or revoking it would be escalation (#286).
	ErrAccountAuthorityEscalation = authkit.ErrAccountAuthorityEscalation
	// ErrRoleNotAssignable: the named role is not valid for the group's persona
	// (neither a catalog role nor, for custom-role personas, a defined custom role).
	ErrRoleNotAssignable = authkit.ErrRoleNotAssignable
)
View Source
var (
	ErrEmailDeliveryFailed = authkit.ErrEmailDeliveryFailed
	ErrSMSDeliveryFailed   = authkit.ErrSMSDeliveryFailed
)
View Source
var (
	// ErrGroupSlugReserved: the slug is on the persona's reserved list and the
	// caller does not hold the escalation role.
	ErrGroupSlugReserved = authkit.ErrGroupSlugReserved
	// ErrGroupCreationRefused: the host admission seam refused the creation.
	ErrGroupCreationRefused = authkit.ErrGroupCreationRefused
)

Sentinel aliases (#263).

View Source
var (
	// ErrUserBanned indicates the account is blocked from authenticating.
	ErrUserBanned = authkit.ErrUserBanned
	// ErrPasswordResetRequired indicates the account's stored password hash is
	// flagged HashAlgoLegacyResetRequired: no plaintext can ever verify against
	// it, so the user must complete a password reset before password auth (login,
	// step-up, change-password) can succeed. HTTP layers map this to the stable
	// code "password_reset_required".
	ErrPasswordResetRequired = authkit.ErrPasswordResetRequired
	// ErrUserNotFound indicates a user does not exist (or is not visible).
	ErrUserNotFound = authkit.ErrUserNotFound
	// ErrInvalidUntil indicates a time-limited operation has a non-future expiry.
	ErrInvalidUntil = authkit.ErrInvalidUntil
	// ErrEmailAlreadyVerified indicates an email verification request targeted an already-verified email.
	ErrEmailAlreadyVerified = authkit.ErrEmailAlreadyVerified
	// ErrPhoneAlreadyVerified indicates a phone verification request targeted an already-verified phone.
	ErrPhoneAlreadyVerified = authkit.ErrPhoneAlreadyVerified
	// ErrPendingRegistrationNotFound indicates a registration resend request did not match a pending registration.
	ErrPendingRegistrationNotFound = authkit.ErrPendingRegistrationNotFound
	// ErrRegistrationDisabled indicates a public user-creation path was attempted
	// while native-user registration is bootstrap-only. Existing-user
	// authentication is unaffected; only NEW account creation through
	// public/auto-registration is blocked.
	ErrRegistrationDisabled = authkit.ErrRegistrationDisabled
	// ErrVerificationLinkExpired indicates a verification link/token no longer has a pending verification record.
	ErrVerificationLinkExpired = authkit.ErrVerificationLinkExpired
	ErrEmailInUse              = authkit.ErrEmailInUse
	ErrPhoneInUse              = authkit.ErrPhoneInUse
	ErrUsernameInUse           = authkit.ErrUsernameInUse
	ErrEmailSenderUnavailable  = authkit.ErrEmailSenderUnavailable
	ErrSMSSenderUnavailable    = authkit.ErrSMSSenderUnavailable
	ErrPasswordlessDisabled    = authkit.ErrPasswordlessDisabled
	ErrDeviceKeysDisabled      = authkit.ErrDeviceKeysDisabled
)
View Source
var (
	// ErrRemoteApplicationIssuerConflict indicates the issuer belongs to another group.
	ErrRemoteApplicationIssuerConflict = authkit.ErrRemoteApplicationIssuerConflict
	// ErrRemoteApplicationNotFound indicates no remote_application matched.
	ErrRemoteApplicationNotFound = authkit.ErrRemoteApplicationNotFound
	// ErrInvalidRemoteApplication is defined in authkit and re-exported here.
	ErrInvalidRemoteApplication = authkit.ErrInvalidRemoteApplication
	// ErrReservedIssuer indicates an attempt to register a remote_application
	// under the platform's own issuer string. The platform issuer is the local,
	// first-party signing identity; allowing a federated remote_application to
	// claim it would overwrite the trusted local issuer entry (key-swap / auth
	// DoS — see AK-AUTH-01).
	ErrReservedIssuer = authkit.ErrReservedIssuer
)
View Source
var Err2FAMethodUnavailable = authkit.ErrTwoFAMethodUnavailable

Err2FAMethodUnavailable is returned by 2FA enroll/challenge operations when the method is disabled by policy or its delivery dependency is missing.

View Source
var ErrAccountRegistrationInviteNotFound = authkit.ErrAccountRegistrationInviteNotFound
View Source
var ErrCannotRemoveLastAdminRole = authkit.ErrCannotRemoveLastAdminRole

ErrCannotRemoveLastAdminRole is returned by the permission-group last-owner guard (refuseOwnerLoss) and mapped to a stable HTTP code by the admin adapter. Aliased from the root package so core can return it unqualified.

View Source
var ErrEntitlementFilterUnavailable = authkit.ErrEntitlementFilterUnavailable

ErrEntitlementFilterUnavailable is returned by AdminListUsers/AdminCountUsers when an Entitlement filter is requested but no EntitlementFilterProvider is configured — fail loud rather than silently return everyone.

View Source
var ErrGroupNotFound = authkit.ErrGroupNotFound

ErrGroupNotFound is returned when a (persona, instance_slug) or id resolves to no live permission-group.

View Source
var ErrGroupSlugApplicationManaged = authkit.ErrGroupSlugApplicationManaged

ErrGroupSlugApplicationManaged: the group's slug mirrors a domain-rooted application's slug (= its proven domain); it renames only through the application repoint flow, never directly.

View Source
var ErrGroupSlugTaken = authkit.ErrGroupSlugTaken

ErrGroupSlugTaken: the requested instance slug is held live by another group or reserved to one until the deadline recorded when its owner renamed.

View Source
var ErrNotGroupMember = authkit.ErrNotGroupMember

ErrNotGroupMember is returned when a remote_application holds no role in its controlling permission-group.

View Source
var ErrOwnerSlugTaken = authkit.ErrOwnerSlugTaken

ErrOwnerSlugTaken is retained as a stable sentinel for identity-policy error mapping. Under the permission-group model usernames are unique on their own (the owner-slug reservation plane was removed); kept so dependents' errors.Is checks keep compiling.

View Source
var ErrRenameRateLimited = authkit.ErrRenameRateLimited

ErrRenameRateLimited is returned when a username rename is attempted before the configured rename interval has elapsed.

View Source
var ErrStepUpRequired = authkit.ErrStepUpRequired
View Source
var ErrTwoFAEnrollmentRequired = authkit.ErrTwoFAEnrollmentRequired
View Source
var ErrTwoFARequired = authkit.E(authkit.CodeTwoFARequired)
View Source
var ErrUserReferenced = authkit.ErrUserReferenced

ErrUserReferenced: a host table references the user without ON DELETE CASCADE, so the hard delete was rolled back in full.

Functions

func ApplyMigrations added in v0.109.0

func ApplyMigrations(ctx context.Context, pool *pgxpool.Pool, schema string, options ...MigrationOptions) error

ApplyMigrations applies AuthKit's PostgreSQL migrations to a privileged pool. It also initializes managed River unless RiverFromHost is declared. Runtime New and Start never run DDL; runtime credentials can be separately restricted.

AuthKit owns its migration source and migratekit runner. The host supplies the database pool and the schema name, then constructs the Runtime after this function returns successfully. The schema is created by migratekit; callers must not create it separately. An empty schema selects AuthKit's default "profiles" schema.

func IntrinsicRootPermissions

func IntrinsicRootPermissions() []string

IntrinsicRootPermissions returns the authkit-built-in root: permission set (every deployment ships these). Apps add their own root: moderation perms on top via the root persona's roles.

func MaskDestination added in v0.98.0

func MaskDestination(value string) string

MaskDestination hides all but the last five characters of a code destination (email or phone) for display as a verification id.

func MintDelegatedAccessToken

func MintDelegatedAccessToken(ctx context.Context, signer jwtkit.Signer, p DelegatedAccessParams) (string, error)

MintDelegatedAccessToken signs a canonical delegated access token with an explicit signer. It stamps the `typ=delegated-access+jwt` JOSE header, writes the canonical `delegated_sub`/`permissions`/`attributes` claims, and NEVER sets `sub` — the sub-XOR-delegated_sub invariant is enforced by construction. Receiving services authorize by issuer/resource-account trust plus `permissions`. A top-level `roles` claim is never minted; delegated-subject role UUIDs, when carried, ride under `attributes.roles` (see the Roles param).

Hosts embedding core.Service should prefer (*engine).MintDelegatedAccessToken so they never construct their own signer or read the PEM.

func MintRemoteApplicationAccessToken

func MintRemoteApplicationAccessToken(ctx context.Context, signer jwtkit.Signer, p RemoteApplicationAccessParams) (string, error)

MintRemoteApplicationAccessToken signs a remote application access token with an explicit signer. It stamps the `typ=remote-application-access+jwt` header and writes NO `sub`/`delegated_sub` — identity is the validated `iss` and authority is STORED, resolved at verify. A non-nil p.Permissions is written as the `permissions` claim: a down-scoping request the verifier intersects with the stored ceiling (#76 amendment); never a widening.

func NormalizeEmail

func NormalizeEmail(email string) string

func NormalizePhone

func NormalizePhone(phone string) string

func NormalizePreferredLanguage

func NormalizePreferredLanguage(language string) (string, error)

func NormalizeRemoteAppTrustSource

func NormalizeRemoteAppTrustSource(jwksURI string, mode string, keys []RemoteAppKey, policy TrustSourcePolicy) (string, error)

func PermCredentialsManage

func PermCredentialsManage(p authkit.Persona) authkit.Perm

func PermCredentialsRead

func PermCredentialsRead(p authkit.Persona) authkit.Perm

func PermMembersManage

func PermMembersManage(p authkit.Persona) authkit.Perm

Built-in per-persona group-management permissions (authkit-provisioned in every persona's catalog). All are 3-segment <persona>:<area>:<action>. The owner role (=<persona>:*) covers them all; an app may grant them to other roles.

func PermMembersRead

func PermMembersRead(p authkit.Persona) authkit.Perm

func PermRolesManage

func PermRolesManage(p authkit.Persona) authkit.Perm

func PermRolesRead

func PermRolesRead(p authkit.Persona) authkit.Perm

func PermSettingsManage added in v0.88.0

func PermSettingsManage(p authkit.Persona) authkit.Perm

PermSettingsManage gates the group's own settings surface (#264): slug rename and display-name changes. Held by the owner via `<persona>:*`; grant it to other roles deliberately.

func PermSettingsRead added in v0.93.0

func PermSettingsRead(p authkit.Persona) authkit.Perm

PermSettingsRead gates reading the group's own identity descriptor (#269): GET /<persona>/:instance_slug — id, slug, display name. The read symmetric of PermSettingsManage; held by the owner via `<persona>:*`.

func RandB64 added in v0.98.0

func RandB64(n int) string

RandB64 returns n random bytes as unpadded URL-safe base64: the one token generator for every one-time secret the engine and the HTTP layer mint.

func SecretEqual added in v0.98.0

func SecretEqual[T ~string | ~[]byte](a, b T) bool

SecretEqual compares two secrets — or their digests — without short-circuiting on the first differing byte; differing lengths compare unequal. It exists so whether a secret comparison is constant-time is never a per-call-site judgement: one-time-code digests, the API-key secret and the OAuth state cookie all compare through it.

func StepUpMethods added in v0.98.0

func StepUpMethods(hasPassword bool, settings *TwoFactorSettings, providerSlugs []string, supportsStepUp func(string) bool) []string

StepUpMethods lists how the user can re-authenticate for a sensitive action: password, an enabled second factor, and every linked provider that supports step-up (de-duplicated, sorted). Pure over already-loaded inputs.

func StepUpTwoFactorOptions added in v0.98.0

func StepUpTwoFactorOptions(settings *TwoFactorSettings, emailDestination string) *authkit.StepUpTwoFactorOptions

StepUpTwoFactorOptions lists the second factors a step-up can use, with the code destination masked. Nil when 2FA is not enabled.

func ValidTwoFactorStepUpMethod added in v0.98.0

func ValidTwoFactorStepUpMethod(method string) bool

ValidTwoFactorStepUpMethod reports whether method can satisfy a step-up.

func ValidateEmail

func ValidateEmail(email string) error

func ValidateGrantPattern

func ValidateGrantPattern(g string) error

ValidateGrantPattern checks a GRANT token (what a role holds). Grants may be concrete perms OR namespace-anchored globs, but NEVER a bare `*`:

<persona>:<resource>:<action>   a concrete perm
<persona>:<resource>:*          all actions on a resource
<persona>:*                     the whole persona namespace (the owner grant)

The persona segment is always a literal — a bare `*` or `*`-persona is rejected, which is what makes reach != capability structural (a `merchant:*` grant can never name a `root:`/`customer:` perm). Mirrors authkit.Perm.Matches semantics but is STRICTER: it forbids mid-glob forms like `persona:*:action`.

func ValidatePermission

func ValidatePermission(p string) error

ValidatePermission checks a CONCRETE catalog permission: EXACTLY three lowercase segments `<persona>:<resource>:<action>` (e.g. `merchant:catalog:update`, `root:users:ban`). Two-part (`repo:update`) and four-part perms are rejected — a two-part perm must grow a resource (`repo:contents:update`); a persona may use a `:self:` resource for "the thing itself" actions (`endpoint:self:invoke`).

func ValidatePhone

func ValidatePhone(phone string) error

func ValidationErrorCode

func ValidationErrorCode(err error) authkit.Code

ValidationErrorCode returns the identity-policy code err carries, or "" when err is not a validation failure.

func WithAccountRegistrationInviteToken added in v0.98.0

func WithAccountRegistrationInviteToken(ctx context.Context, token string) context.Context

func WithResolvedGroup added in v0.98.0

func WithResolvedGroup(ctx context.Context, instance authkit.GroupInstance, reference string) context.Context

WithResolvedGroup binds the address already resolved by an HTTP request to its immutable target. It confers no permission: the caller must still authorize. Only this exact persona/reference matches. Parent and other-target lookups keep normal resolution. Every use rechecks target liveness and never falls back to the name if the captured group has been deleted.

func WithSessionRevokeReason

func WithSessionRevokeReason(ctx context.Context, reason SessionRevokeReason) context.Context

WithSessionRevokeReason annotates ctx so revoke paths can emit a structured reason to the auth logger.

Types

type APIKey added in v0.98.0

type APIKey = authkit.APIKey

APIKey is the non-secret metadata view of an API key. The secret is never stored or returned after creation. Role is the single group role the key holds; Permissions is that role's RESOLVED effective permission set (a convenience projection — the role is the source of truth, edit it to change the key).

type APIKeyMintOptions added in v0.98.0

type APIKeyMintOptions = authkit.APIKeyMintOptions

APIKeyMintOptions is the API-key mint request. The key references exactly ONE role (Role) that must be valid for the owning group's persona catalog (or a group custom role); its permissions are resolved from that role at use time.

type APIKeysConfig

type APIKeysConfig struct {
	// Prefix is the issuing application's brand prefix for generated API keys
	// (single value per deployment). Empty defaults to the bare `st_` marker.
	// Must be lowercase alphanumeric, 1-16 chars.
	Prefix string
	// MaxTTL caps how far in the future a minted API key may expire. 0 (default)
	// means no cap (keys may be non-expiring); when set, a requested expiry
	// beyond now+MaxTTL (incl. no-expiry) is capped at mint time.
	MaxTTL time.Duration
}

APIKeysConfig configures opaque permission-group-owned machine credentials.

type AccountRecoveryConfirmation added in v0.124.0

type AccountRecoveryConfirmation struct {
	Token     string    `json:"token"`
	ExpiresAt time.Time `json:"expires_at"`
	PurgeAt   time.Time `json:"purge_at"`
}

AccountRecoveryConfirmation is an opaque proof, never an access token or session. Confirmation restores this deletion generation without signing in.

type AccountRegistrationInvite added in v0.98.0

type AccountRegistrationInvite = authkit.AccountRegistrationInvite

type AccountRegistrationInviteCreated added in v0.98.0

type AccountRegistrationInviteCreated = authkit.AccountRegistrationInviteCreated

type AdminListUsersResult added in v0.98.0

type AdminListUsersResult = authkit.AdminListUsersResult

AdminListUsersResult contains paginated user list with total count

type AdminUser added in v0.98.0

type AdminUser = authkit.AdminUser

type AdminUserListOptions added in v0.98.0

type AdminUserListOptions = authkit.AdminUserListOptions

AdminUserListOptions is the admin dashboard user-directory query. It carries no host product knowledge: Role is the root_role query param, a singleton-root permission-group role slug. Status/Sort are closed enums. Entitlement filtering delegates to the billing provider, never a cross-schema join.

type AdminUserSort added in v0.98.0

type AdminUserSort = authkit.AdminUserSort

AdminUserSort selects the directory ordering column.

type AdminUserStatus added in v0.98.0

type AdminUserStatus = authkit.AdminUserStatus

AdminUserStatus filters the directory by account state.

type ApplicationDocument added in v0.98.0

type ApplicationDocument = authkit.ApplicationDocument

type ApplicationsConfig added in v0.88.0

type ApplicationsConfig struct {
	// SelfRegistration enables the POST /applications/register surface (and
	// the signed rotate/repoint routes). Off by default.
	SelfRegistration bool
	// AllowPrivateNetworkJWKS permits http and private/loopback addresses for
	// every remote-application fetch — jwks_uri values, application documents
	// and their domain proofs — and turns off the SSRF guard on the verifier's
	// JWKS client. Local federation rigs only; the default (false) refuses
	// anything that is not a public https endpoint.
	AllowPrivateNetworkJWKS bool
	// OrgPersona is the declared RBAC persona under which each self-registered
	// application's SERVICE-OWNED org is created (instance_slug = the
	// application slug; the application principal is seeded as its owner).
	// Required when SelfRegistration is set; must be a declared non-root
	// persona whose Parent is the root persona.
	OrgPersona authkit.Persona
}

ApplicationsConfig configures application self-registration (#264).

The trust root is domain control (or an owning user account) — never the keypair alone: registration fetches https://<domain>/.well-known/authkit/application.json server-side, and that fetch IS the domain-control proof. Re-registration of the same domain re-proves the root and adopts the document's current keys (the boot-time self-heal and the rotation-from-root path).

type AuthSessionEvent

type AuthSessionEvent struct {
	OccurredAt time.Time
	Issuer     string
	UserID     string
	SessionID  string
	Event      SessionEventType
	Method     *string
	Reason     *string
	IPAddr     *string
	UserAgent  *string
}

AuthSessionEvent is a best-effort, append-only session lifecycle record stored in Postgres (session_events, #245) and retained per Config.SessionEventRetention. issuer/user_id/session_id/event are required; method is typically set for SessionEventCreated and reason for SessionEventRevoked.

type BootstrapManifest added in v0.98.0

type BootstrapManifest = authkit.BootstrapManifest

BootstrapManifest is AuthKit's first-class closed-deployment seed manifest. It creates/updates initial users and assigns root roles after the host app has already configured AuthKit's RBAC schema in code.

Operator authority is a role assignment in the singleton root group. A user's `root_role` seeds one root-group role ASSIGNMENT: "owner" (the built-in apex, root:*, present by default on every group) is seeded SEED-IF-ABSENT via the genesis path; any other name must be a catalog role of the root persona (declared in core.Config, e.g. an app's bounded "admin").

func LoadBootstrapManifestFile

func LoadBootstrapManifestFile(path string) (BootstrapManifest, error)

func ParseBootstrapManifestYAML

func ParseBootstrapManifestYAML(raw []byte) (BootstrapManifest, error)

type BootstrapManifestRemoteApplication added in v0.98.0

type BootstrapManifestRemoteApplication = authkit.BootstrapManifestRemoteApplication

type BootstrapManifestResult added in v0.98.0

type BootstrapManifestResult = authkit.BootstrapManifestResult

type BootstrapManifestUser added in v0.98.0

type BootstrapManifestUser = authkit.BootstrapManifestUser

type BootstrapReconcileOptions added in v0.98.0

type BootstrapReconcileOptions = authkit.BootstrapReconcileOptions

type BootstrapUserPassword added in v0.98.0

type BootstrapUserPassword = authkit.BootstrapUserPassword

type Config

type Config struct {
	// HTTP configures the optional runtime-owned HTTP surface during New.
	// Pass authhttp.Config. Nil keeps the runtime headless; hosts that must
	// provision first may call ConfigureHTTP before obtaining routes instead.
	HTTP HTTPConfiguration

	// River configures mandatory PostgreSQL cleanup; in-memory TTL stays local.
	River RiverConfig

	// Naming is the shared user/group rename policy, normalized at construction.
	Naming authkit.NamingConfig

	// Token is the JWT issuing/verification contract and session limits.
	Token TokenConfig
	// Frontend describes host-owned frontend routes used for absolute-URL and
	// full-page OIDC callback construction.
	Frontend FrontendConfig
	// Registration controls verification policy and public self-registration.
	Registration RegistrationConfig
	// Password is the length policy every password write enforces; zero fields
	// default to 8..128 characters. Published by GET {api}/capabilities.
	Password password.Policy
	// Username bounds username length; zero fields default to 4..30. The
	// character rule is fixed (authkit.UsernamePattern). Published by
	// GET {api}/capabilities.
	Username authkit.UsernamePolicy
	// Keys controls signing-key resolution (or verify-only mode).
	Keys KeysConfig
	// Ephemeral governs the short-lived state backend (2FA codes, pending
	// registrations, reset tokens, rate-limit counters).
	Ephemeral EphemeralConfig
	// Identity declares external OAuth2/OIDC identity providers.
	Identity IdentityConfig
	// APIKeys configures opaque permission-group-owned machine credentials.
	APIKeys APIKeysConfig
	// TwoFactor configures optional MFA features.
	TwoFactor TwoFactorConfig
	// Passkeys configures WebAuthn/FIDO2 passkey ceremonies.
	Passkeys PasskeyConfig
	// DeviceKeys enables the refreshless native-client device-key surface
	// (#278). Off by default: enrollment is an email-code login, so hosts opt
	// in explicitly before RouteDeviceKeys is mounted or the engine issues
	// enrollment/login challenges (#293).
	DeviceKeys DeviceKeysConfig
	// RBAC declares the app's permission-group personas (#111): containment
	// schema plus per-persona role catalogs. Empty yields root-only.
	RBAC []PersonaDef

	// Applications configures application self-registration (#264): domain-
	// proven remote applications with service-owned orgs. Zero value = disabled
	// (the manual/bootstrap registration paths are unaffected).
	Applications ApplicationsConfig

	// Delegated configures the delegated-token mint route (#261): the audience
	// allowlist and the TTL floor/default/ceiling. Zero value = the route is
	// not mounted. An internally inconsistent triple refuses at construction —
	// never a silent clamp of the configuration itself (request-time TTLs ARE
	// clamped into the validated bounds).
	Delegated DelegatedConfig

	// Documents configures the published signed-document surface (#260):
	// which remote-application reader slugs may fetch published documents from
	// GET|HEAD /.well-known/authkit/documents/{digest}. Publication is never
	// public — an empty list with a mounted documents surface refuses at
	// construction (authhttp.New), fail-closed like the publisher itself.
	Documents DocumentsConfig

	// Schema is the Postgres schema AuthKit's tables live in. Empty defaults to
	// "profiles" (the historical hard-coded name). Set it when multiple apps
	// embed AuthKit against the same database and must not share auth tables
	// (authkit issue 69). The name must match ^[a-z_][a-z0-9_]*$ (max 63 bytes);
	// NewFromConfig rejects anything else. Hosts must call ApplyMigrations with
	// the same schema before constructing the Runtime.
	Schema string

	// SolanaNetwork is the SIWS chain selector ("mainnet"/"testnet"/"devnet").
	// Empty defaults to mainnet. Solana Name Runtime (SNS)
	// resolution is AuthKit-owned: it uses the built-in keyless resolver, with a
	// fixed 3s lookup timeout and 24h cache TTL. There is no host override.
	SolanaNetwork string

	// SessionEventRetention is how long session-event history rows
	// (sign-ins/revocations, incl. IP + user-agent — personal data) are kept
	// before CleanupExpiredAuthState prunes them. 0 (unset) defaults to 365
	// days — the deliberate ceiling; any negative value keeps events forever.
	SessionEventRetention time.Duration
	// contains filtered or unexported fields
}

Config is the host-provided configuration for an AuthKit Runtime. Fields are grouped by concern into typed sub-structs (#108). It carries DATA/POLICY only; runtime dependencies (Postgres, Redis, senders) are Deps.

type ContactChange added in v0.98.0

type ContactChange struct {
	// Field is "email" or "phone".
	Field string
	// NewValue is the replacement address as stored.
	NewValue string
}

ContactChange is delivered to the PREVIOUS address after a recovery identifier (email or phone) was replaced, so a hijacked change is visible to the account's real owner.

type CreateAccountRegistrationInviteRequest added in v0.98.0

type CreateAccountRegistrationInviteRequest = authkit.CreateAccountRegistrationInviteRequest

type CreateGroupInviteLinkRequest added in v0.98.0

type CreateGroupInviteLinkRequest = authkit.CreateGroupInviteLinkRequest

CreateGroupInviteLinkRequest mints an invite link for the group addressed by (Persona, InstanceSlug) granting Role. ExpiresIn overrides the default lifetime.

type CreateInstanceResult added in v0.98.0

type CreateInstanceResult struct {
	// GroupID is the new (or idempotently returned) instance's uuid (#269). It
	// is populated on BOTH outcomes: the idempotent re-run is the bootstrap
	// path, so an id only on Created=true would leave the re-runner with
	// nothing. Empty only on error.
	GroupID      string
	InstanceSlug string
	Created      bool
}

CreateInstanceResult reports a generated-creation outcome. Created is false when the slug already existed and the caller is a member (idempotent return).

type CreatePermissionGroupRequest added in v0.98.0

type CreatePermissionGroupRequest = authkit.CreatePermissionGroupRequest

CreatePermissionGroupRequest creates a permission group. Parent is addressed by (ParentPersona, ParentInstanceSlug); for a single-allowed-parent persona ParentPersona may be omitted. OwnerSubjectID, when set, is seeded with the owner role.

type CustomRoleResolver

type CustomRoleResolver func(groupID string, role authkit.Role) ([]string, bool)

CustomRoleResolver returns the grant tokens of a per-group custom role, or (nil, false) if no such custom role exists. Consulted only for personas whose CustomRoles is set; pass nil when the deployment defines no custom roles.

type DelegatedAccessParams added in v0.98.0

type DelegatedAccessParams = authkit.DelegatedAccessParams

DelegatedAccessParams describes a delegated access token to mint.

A delegated access token is AuthKit's standard primitive for resource-service federation: one AuthKit issuer signs a short-lived JWT for an external delegated subject, and a resource service accepts it after issuer/JWKS/ audience validation. The token represents a delegated subject (DelegatedSubject) acting under the resource account that the VALIDATED `iss` resolves to in the receiver's issuer registry. It NEVER carries a normal `sub` — no local account is implied in the receiving service.

type DelegatedConfig added in v0.91.0

type DelegatedConfig struct {
	// AllowDPoP allows browser-key binding. The authorizer must handle requests
	// with ConfirmationJWKThumbprintSHA256 set and DelegateCertificate nil.
	AllowDPoP bool `json:"allow_dpop" yaml:"allow_dpop"`
	// Audiences is the allowlist. Requested audiences must be a subset; an
	// empty request receives the full list. Empty = the route is disabled.
	Audiences []string
	// TTLFloor/TTLDefault/TTLCeiling bound the minted token TTL. Unset fields
	// default to 60s / 15m / 1h. After defaulting, the triple must satisfy
	// 0 < floor <= default <= ceiling or construction refuses (#231 house
	// style: an impossible configuration never boots).
	TTLFloor   time.Duration
	TTLDefault time.Duration
	TTLCeiling time.Duration
}

DelegatedConfig configures the delegated-token mint route (#261/#277, POST /delegated/token under the API prefix). All four knobs are DATA: the mint mechanics (audience-subset clamp, TTL clamp, certificate binding, document stamping, KID reconciliation) live in AuthKit; the host contributes the required delegation authorizer (WithDelegatedAuthorization) and optional document providers (authhttp.WithDocuments).

type DelegationAuthorizer added in v0.98.0

type DelegationAuthorizer = authkit.DelegationAuthorizer

DelegationAuthorizer is the HOST-INJECTED seam of the delegated-token mint route (#261/#277): AuthKit validates the request's cryptographic facts, the host decides the exact permissions/attributes/documents to sign. Required whenever the route is mounted; an error refuses the mint.

type DelegationGrant added in v0.98.0

type DelegationGrant = authkit.DelegationGrant

DelegationAuthorizer is the HOST-INJECTED seam of the delegated-token mint route (#261/#277): AuthKit validates the request's cryptographic facts, the host decides the exact permissions/attributes/documents to sign. Required whenever the route is mounted; an error refuses the mint.

type DelegationRequest added in v0.98.0

type DelegationRequest = authkit.DelegationRequest

DelegationAuthorizer is the HOST-INJECTED seam of the delegated-token mint route (#261/#277): AuthKit validates the request's cryptographic facts, the host decides the exact permissions/attributes/documents to sign. Required whenever the route is mounted; an error refuses the mint.

type DeletePermissionGroupOptions added in v0.98.0

type DeletePermissionGroupOptions = authkit.DeletePermissionGroupOptions

DeletePermissionGroupOptions controls the delete-time naming rule (#264).

type Deps added in v0.98.0

type Deps struct {
	// River is nil for managed maintenance, or RiverFromHost for a shared fleet.
	River *RiverOwnership

	// Postgres is the durable store. Required by every host-facing constructor.
	Postgres *pgxpool.Pool
	// Redis backs the ephemeral store, namespaced by Ephemeral.KeyPrefix
	// (#307). Nil selects the per-process memory store, which construction
	// refuses unless Config.Ephemeral.AllowMemory is set (#305).
	// Redis-compatible servers must provide atomic Lua execution (EVAL/EVALSHA)
	// for conditional proof claims and counters, as well as atomic GETDEL.
	Redis *redis.Client
	// EphemeralStore is a host-supplied store; mutually exclusive with Redis.
	EphemeralStore EphemeralStore
	Email          EmailSender
	SMS            SMSSender
	Entitlements   EntitlementsProvider
	// Deletion hooks run durably through River, never inside the request's
	// transaction. Soft deletion must preserve recoverable host data; hard
	// deletion is finalization work after 30 days and before identity purge.
	// Hooks must be idempotent and honor context cancellation. Nil means no
	// application work for that stage. OnRestore undoes reversible soft work.
	OnSoftDelete func(context.Context, authkit.UserDeletion) error
	OnHardDelete func(context.Context, authkit.UserDeletion) error
	OnRestore    func(context.Context, authkit.UserDeletion) error
	// DelegatedAuthorization is the host's delegation authorizer for the
	// delegated-token mint route (#261/#277); its grant is the complete
	// authority AuthKit signs. Required when Delegated.Audiences is set.
	DelegatedAuthorization DelegationAuthorizer
	// ApplicationAdmission is consulted before any application
	// self-registration fetch (#264): a non-nil error refuses the attempt.
	ApplicationAdmission func(ctx context.Context, domain string) error
	// InstanceAdmission is consulted before any generated persona-instance
	// creation (#263) with the normalized slug; a non-nil error refuses.
	InstanceAdmission func(ctx context.Context, group authkit.GroupRef, subject string) error
	// NameAdmission is the host's side-effect-free namespace policy for
	// creation and rename.
	NameAdmission func(context.Context, authkit.NameAdmissionRequest) error
	// SolanaSNSResolver replaces the SNS primary-name resolver used after a
	// verified Solana link.
	SolanaSNSResolver SolanaSNSResolver
	// OutboundHTTP overrides the client for application-document and JWKS
	// fetches (#264); nil builds the timeout-bounded, redirect-refusing,
	// SSRF-guarded default.
	OutboundHTTP *http.Client
	// Clock replaces the engine clock for TTL and grace-window decisions.
	Clock func() time.Time
}

Deps are the runtime dependencies a Runtime is built with. Config carries data and policy; everything that reaches outside the process is here.

type DeviceKey added in v0.98.0

type DeviceKey struct {
	ID         string     `json:"id"`
	Label      string     `json:"label,omitempty"`
	CreatedAt  time.Time  `json:"created_at"`
	LastUsedAt *time.Time `json:"last_used_at,omitempty"`
	RevokedAt  *time.Time `json:"revoked_at,omitempty"`
}

DeviceKey is the public projection of one native-client credential.

type DeviceKeyAuthResult added in v0.98.0

type DeviceKeyAuthResult struct {
	AccessToken string
	ExpiresAt   time.Time
	DeviceKey   DeviceKey
}

type DeviceKeyChallenge added in v0.98.0

type DeviceKeyChallenge struct {
	ID        string
	Challenge string
	ExpiresAt time.Time
}

type DeviceKeyNotice added in v0.98.0

type DeviceKeyNotice struct {
	Label     string
	CreatedAt time.Time
}

DeviceKeyNotice describes a native-client device key just enrolled on an EXISTING account (#293), so a key added through a compromised mailbox is visible to the account's real owner.

type DeviceKeySecondFactorRequired added in v0.98.0

type DeviceKeySecondFactorRequired struct{ Method string }

DeviceKeySecondFactorRequired is returned by FinishDeviceKeyEnrollment when the email code and key proof are valid but the account has a usable second factor that was not presented (#293). The ceremony stays live for a retry carrying the code; for SMS/email factors the code has just been sent.

func (*DeviceKeySecondFactorRequired) Error added in v0.98.0

type DeviceKeysConfig added in v0.98.0

type DeviceKeysConfig struct {
	// Enabled mounts RouteDeviceKeys and lets the engine run enrollment and
	// login ceremonies.
	Enabled bool
}

DeviceKeysConfig controls the native-client device-key surface (#278).

type DocumentReader added in v0.98.0

type DocumentReader struct {
	ID     string
	Domain string
	Issuer string
}

DocumentReader pins one reader by exactly one identity:

  • ID: the application's uuid.
  • Domain: the proven domain of a domain-rooted (self-registered) application.
  • Issuer: the issuer of a manually registered application the platform itself holds under the root group (bootstrap manifest / root credentials manager). A tenant-registered application never matches by issuer.

type DocumentsConfig added in v0.91.0

type DocumentsConfig struct {
	// Readers are the remote applications allowed to fetch published
	// documents. Authorization is config, not a host callback, and it keys on
	// an identity nobody else can claim — never on the slug, which is a
	// claimable handle. Empty + a mounted documents surface is a construction
	// error, never a public route.
	Readers []DocumentReader
	// AllowRegisteredTier admits readers still at the registered tier
	// (self-registered, not yet approved by an admin). Default: approved only.
	AllowRegisteredTier bool
}

DocumentsConfig configures reader authorization for the published signed-document surface (#260, #296).

type EmailSender

type EmailSender interface {
	SendVerification(ctx context.Context, email, username string, msg VerificationMessage) error
	SendPasswordResetLink(ctx context.Context, email, username, resetURL string) error
	SendAccountRegistrationInvite(ctx context.Context, email, inviteURL string) error
	SendLoginCode(ctx context.Context, email, username, code string) error
	SendWelcome(ctx context.Context, email, username string) error
	// SendContactChanged goes to the address that was just REPLACED.
	SendContactChanged(ctx context.Context, email, username string, change ContactChange) error
	// SendDeviceKeyEnrolled tells the account's address that a new device key
	// can now sign in as it.
	SendDeviceKeyEnrolled(ctx context.Context, email, username string, notice DeviceKeyNotice) error
}

EmailSender sends verification/login/reset/notice emails.

type EntitlementFilterProvider

type EntitlementFilterProvider interface {
	ListSubjectsWithEntitlement(ctx context.Context, entitlement string) ([]string, error)
}

EntitlementFilterProvider is the REVERSE of EntitlementsProvider: given an entitlement key, it returns the subject ids that currently hold it. AuthKit owns the user DIRECTORY; the billing system (OpenRails) owns "who is entitled", so filtering the directory BY entitlement delegates here instead of joining across schemas. Subject ids ARE user ids (UUID-only payable identity). Detected by type assertion on the entitlements provider; when absent, AdminListUsers with an Entitlement filter fails with ErrEntitlementFilterUnavailable so the misconfiguration is loud rather than silently returning everyone.

type EntitlementsProvider

type EntitlementsProvider interface {
	ListEntitlements(ctx context.Context, userIDs []string) (map[string][]string, error)
}

EntitlementsProvider returns the names of users' currently active application entitlements (e.g., billing tiers). Names are the ONLY shape AuthKit consumes. Token.EntitlementAllowlist selects which names may appear in access tokens; admin user views receive the full result. Providers return active grants only; expired/revoked entitlements are the provider's concern, not AuthKit's.

BATCH-NATIVE (#221, operation-shape rule #219): one call answers many users — the map is keyed by user id and unknown/entitlement-less ids are simply absent. A single-user read is the batch with a one-element slice. (This replaces the former single-user signature plus the optional BatchEntitlementsProvider type-assertion upgrade.)

type EphemeralConfig added in v0.98.0

type EphemeralConfig struct {
	// AllowMemory permits the in-memory ephemeral store and rate limiter
	// (single-instance deployments and local development only).
	AllowMemory bool
	// KeyPrefix namespaces every Redis key this deployment writes (ephemeral
	// store, OIDC/SIWS caches, rate-limit counters) so several AuthKit
	// deployments can share one Redis database (#307). Empty derives
	// "authkit:<schema>:"; a trailing ':' is added when missing. Must match
	// ^[a-z0-9_.:-]{1,64}$.
	KeyPrefix string
}

KeysConfig controls signing-key resolution. AuthKit reads NO environment variables here (#231): key material and the dev opt-in come from the host's explicit configuration; binaries (cmd/authkit-server) read env once at their own boundary and set these fields. EphemeralConfig governs the ephemeral (short-lived state) backend. The in-memory store and rate limiter are per-process: in a multi-replica deployment they give per-replica 2FA codes, pending registrations and N-times rate limits, so construction FAILS without Redis unless AllowMemory is set. Like KeysConfig.AllowEphemeralDevKeys, the opt-in is an explicit field.

type EphemeralStore

type EphemeralStore interface {
	Get(ctx context.Context, key string) ([]byte, bool, error)
	Set(ctx context.Context, key string, value []byte, ttl time.Duration) error
	Del(ctx context.Context, key string) error
	// Consume atomically returns AND deletes a key in a single operation, so the
	// value is delivered to AT MOST ONE caller even under concurrent reads
	// (Redis GETDEL / a single locked get-delete). Single-use credentials whose
	// KEY is the secret — a WebAuthn/passkey challenge, a password-reset token —
	// MUST be read via Consume, never Get+Del: a non-atomic read-then-delete lets
	// two concurrent requests both observe the value before either deletes it,
	// defeating the single-use guarantee (replay). Missing key => (nil, false, nil).
	Consume(ctx context.Context, key string) ([]byte, bool, error)
	// CompareAndConsume deletes a live key only while it still holds expected.
	// OTP/link representations use one canonical record; an old reader can
	// neither win twice nor consume a newer issuance. Missing/mismatch => false.
	CompareAndConsume(ctx context.Context, key string, expected []byte) (bool, error)
	// Incr atomically increments the integer at key and returns the new value,
	// creating it as 1 with ttl when absent (the TTL is set once, not on every
	// increment). Attempt caps MUST use it: a Get+Set counter lets K concurrent
	// wrong guesses all read the same n and the cap never fires (#306).
	Incr(ctx context.Context, key string, ttl time.Duration) (int64, error)
}

EphemeralStore is a minimal key-value interface used for short-lived auth state. Implementations should honor TTL on Set and treat missing keys as (found=false, err=nil).

type ExternalIdentity added in v0.98.0

type ExternalIdentity struct {
	Provider          string // provider slug (the configured name)
	Issuer            string
	Subject           string
	Email             string
	EmailVerified     bool
	PreferredUsername string
	DisplayName       string
}

ExternalIdentity is a provider-verified identity.

type ExternalLinkAuthorization added in v0.100.0

type ExternalLinkAuthorization struct {
	UserID          string
	SessionID       string
	AuthenticatedAt time.Time
}

ExternalLinkAuthorization records the fresh session that initiated linking. It is carried only in server-side browser state, never accepted from a callback.

type ExternalLoginInput added in v0.98.0

type ExternalLoginInput struct {
	Identity ExternalIdentity
	// Link authorizes a provider mutation only; it never creates a session.
	Link               *ExternalLinkAuthorization
	AccountInviteToken string
	Event              string // session-created audit event, e.g. "oidc_login"
	UserAgent          string
	IP                 string
}

ExternalLoginInput is an external-identity login or link attempt.

type FactorEnrollmentMode added in v0.98.0

type FactorEnrollmentMode string

FactorEnrollmentMode distinguishes restricted enrollment grants from authenticated factor management.

const (
	// FirstFactorOnly permits a restricted grant to enroll only when no factor exists.
	FirstFactorOnly FactorEnrollmentMode = "first_factor_only"
	// AllowAdditionalFactors permits fresh authenticated users to add a new method.
	AllowAdditionalFactors FactorEnrollmentMode = "allow_additional_factors"
)

type FrontendConfig

type FrontendConfig struct {
	// BaseURL, if set, is used for building absolute URLs (e.g. password
	// reset/verify links). If empty and Token.Issuer is a well-formed URL,
	// NewFromConfig defaults it to the issuer.
	BaseURL string
	// OIDCReturnPath is the host SPA landing route AuthKit redirects to after it
	// finishes an OIDC/social login flow (the browser is sent to
	// BaseURL + OIDCReturnPath with the login result). This is NOT the backend
	// OAuth/OIDC provider callback URL — AuthKit owns that. Empty defaults to
	// "/login/callback".
	OIDCReturnPath string
	// VerifyPath is the host-owned frontend route that receives scanner-safe
	// verification link landings. Empty defaults to "/verify".
	VerifyPath string
	// PasswordResetPath is the host-owned frontend route that receives
	// scanner-safe password reset link landings. Empty defaults to "/reset".
	PasswordResetPath string
	// PasswordlessPath is the host-owned frontend route that receives
	// passwordless login magic links. Empty defaults to "/passwordless".
	PasswordlessPath string
	// InvitePath is the host-owned frontend route that receives permission-group
	// invite links (`?code=…`); the SPA reads the code and POSTs it to the redeem
	// endpoint. Empty defaults to "/accept-invite". (#134)
	InvitePath string
}

FrontendConfig describes host-owned frontend routes.

type GeneratedRoute

type GeneratedRoute struct {
	Persona authkit.Persona
	Method  string
	Path    string // e.g. /merchant/:instance_slug/members
	Perm    authkit.Perm
}

GeneratedRoute is one auto-generated management endpoint: addressed by the RESOURCE's own id (:instance_slug), gated by Perm (a concrete <persona>:<res>:<act>).

type GroupAssignment

type GroupAssignment struct {
	Persona           authkit.Persona
	PermissionGroupID string // opaque group id; used ONLY to scope custom-role lookups
	Role              authkit.Role
}

GroupAssignment is a subject's SINGLE role assignment within ONE permission-group (#247: one role per subject per group is a hard rule — no per-group role unions), tagged with that group's persona. The slice order is irrelevant — the union ACROSS groups is additive and order-independent.

type GroupDirectory added in v0.98.0

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

GroupDirectory reads immutable group identities and current/active alias names. It uses AuthKit's existing store; it carries no signer, issuer or session state.

func NewGroupDirectory added in v0.98.0

func NewGroupDirectory(pool *pgxpool.Pool, schema string) (*GroupDirectory, error)

NewGroupDirectory creates a read-only view of an already migrated schema. Empty schema selects the default profiles namespace. Construction does not query, migrate, write or start workers. Hosts remain responsible for authorizing any subsequent action.

func (*GroupDirectory) Close added in v0.105.0

func (d *GroupDirectory) Close()

Close releases the directory's schema-bound pool. The caller's pool passed to NewGroupDirectory remains host-owned.

func (*GroupDirectory) GroupInstanceByID added in v0.98.0

func (d *GroupDirectory) GroupInstanceByID(ctx context.Context, id string) (authkit.GroupInstance, error)

func (*GroupDirectory) GroupInstanceForSlug added in v0.98.0

func (d *GroupDirectory) GroupInstanceForSlug(ctx context.Context, group authkit.GroupRef) (authkit.GroupInstance, error)

func (*GroupDirectory) SearchGroupInstances added in v0.98.0

func (d *GroupDirectory) SearchGroupInstances(ctx context.Context, persona authkit.Persona, query, afterSlug, afterID string, limit int) ([]authkit.GroupInstance, error)

SearchGroupInstances returns canonical slugs containing query (case insensitive, literal substring), ordered by (slug,id). Empty cursor starts the search; later pages use the last row's slug/id. Limit defaults to50 and is capped at200.

type GroupInstance added in v0.98.0

type GroupInstance = authkit.GroupInstance

GroupInstance is a persona instance's own identity (#269).

type GroupInviteLink = authkit.GroupInviteLink

GroupInviteLink is the non-secret view of an invite link (never carries the code or its hash).

type GroupInviteLinkCreated added in v0.98.0

type GroupInviteLinkCreated = authkit.GroupInviteLinkCreated

GroupInviteLinkCreated is the mint result: the plaintext Code (shown ONCE) and the ready-to-send URL.

type GroupMember added in v0.98.0

type GroupMember = authkit.GroupMember

GroupMember is one role-assignment in a group (roster listing).

type GroupSchema

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

GroupSchema is the validated, immutable set of declared group personas — the containment schema + catalogs + management Construct via NewGroupSchema, which validates everything once.

func BuildSchema

func BuildSchema(appTypes ...PersonaDef) (*GroupSchema, error)

BuildSchema assembles the deployment's GroupSchema from authkit's intrinsic root persona plus the app's declared personas, and validates the whole. Root declarations are additive: apps may pass root roles, catalog entries, or capabilities without redefining authkit's intrinsic root. Non-root persona names remain globally unique and are rejected by NewGroupSchema.

func NewGroupSchema

func NewGroupSchema(types ...PersonaDef) (*GroupSchema, error)

NewGroupSchema validates an app's declared personas and returns the schema, or an error describing the first problem. It enforces: a single root persona (named RootPersona, parentless); every persona has an `owner` role == `<persona>:*`; every role grant is a valid pattern in the persona's own namespace; and parent edges reference declared personas and form an acyclic tree rooted at root.

func (*GroupSchema) Can added in v0.98.0

func (s *GroupSchema) Can(assignments []GroupAssignment, custom CustomRoleResolver, perm authkit.Perm) bool

Can reports whether the subject (via its assignments across a target group's parent chain) holds a grant covering perm. ALLOW if any granted token covers perm under authkit's namespace-anchored glob semantics (a bare `*` never matches). Additive walk-up union; the caller constructs the exact perm to check (e.g. for a resource of persona RT acted on from an ancestor of persona LT, the perm is `LT:RT:<action>` — the two-persona rule, decision #5).

func (*GroupSchema) CreationDef added in v0.98.0

func (s *GroupSchema) CreationDef(persona authkit.Persona) (InstanceCreationDef, bool)

CreationDef returns a persona's generated-creation config (#263); ok is false for unknown personas.

func (*GroupSchema) CreationEnabled added in v0.98.0

func (s *GroupSchema) CreationEnabled(persona authkit.Persona) bool

CreationEnabled reports whether the persona has the generated creation route.

func (*GroupSchema) GeneratedRoutes added in v0.98.0

func (s *GroupSchema) GeneratedRoutes() []GeneratedRoute

GeneratedRoutes returns the full management surface implied by the schema's per-persona definition. The HTTP layer mounts exactly these; disabled capabilities are simply absent (→ 404). Reads gate on <area>:read; mutations on the matching <area>:manage built-in.

func (*GroupSchema) GrantableUniverse added in v0.98.0

func (s *GroupSchema) GrantableUniverse(persona authkit.Persona) ([]string, bool)

func (*GroupSchema) IsRoot added in v0.98.0

func (s *GroupSchema) IsRoot(name authkit.Persona) bool

IsRoot reports whether name is the root persona.

func (*GroupSchema) Persona added in v0.98.0

func (s *GroupSchema) Persona(name authkit.Persona) (PersonaDef, bool)

Persona returns a declared persona's effective definition.

func (*GroupSchema) Personas added in v0.98.0

func (s *GroupSchema) Personas() []authkit.Persona

Personas returns the declared persona names, sorted.

func (*GroupSchema) ResolveGrants added in v0.98.0

func (s *GroupSchema) ResolveGrants(assignments []GroupAssignment, custom CustomRoleResolver) []string

ResolveGrants computes the additive, de-duplicated UNION of grant tokens a subject holds across the given assignments. For each (persona, role): a catalog role contributes the persona's catalog grants; otherwise, if the persona allows custom roles, the per-group custom role's grants are used. Unknown personas and unknown roles contribute NOTHING (fail-closed). Every returned token is a grant pattern already validated at schema-construction time.

func (*GroupSchema) Role added in v0.98.0

func (s *GroupSchema) Role(persona authkit.Persona, role authkit.Role) (RoleDef, bool)

Role returns a single role from a persona's catalog.

func (*GroupSchema) Roles added in v0.98.0

func (s *GroupSchema) Roles(persona authkit.Persona) ([]RoleDef, bool)

Roles returns a persona's effective roles (app-declared + seeded owner).

func (*GroupSchema) ValidateParent added in v0.98.0

func (s *GroupSchema) ValidateParent(childPersona, parentPersona authkit.Persona) error

ValidateParent enforces the containment schema at INSTANCE-create time: a proposed (childPersona, parentPersona) edge. root is parentless; every non-root group needs the parent persona declared by the child persona's Parent — so e.g. `root -> repo` is structurally impossible, not merely discouraged.

type HTTPBackend added in v0.117.0

type HTTPBackend interface {
	authkit.Client
	verify.Enricher
	AssignGroupRoleFromClaims(ctx context.Context, claims verify.Claims, group authkit.GroupRef, subject authkit.Subject, role authkit.Role) error
	RemoveGroupSubjectFromClaims(ctx context.Context, claims verify.Claims, group authkit.GroupRef, subject authkit.Subject) error
	AdminRevokeAccountSessionsAs(ctx context.Context, actorUserID, userID string) (authkit.AccountSessionRevocation, error)
	AssignRemoteApplicationRoleAs(ctx context.Context, actorUserID string, group authkit.GroupRef, appSlug string, role authkit.Role) error
	BeginDeviceKeyEnrollment(ctx context.Context, email, publicKey, label string) (DeviceKeyChallenge, error)
	BeginDeviceKeyLogin(ctx context.Context, deviceKeyID string) (DeviceKeyChallenge, error)
	BeginPasskeyLogin(ctx context.Context) (*protocol.CredentialAssertion, error)
	BeginPasskeyRegistration(ctx context.Context, userID string) (*protocol.CredentialCreation, error)
	BeginTwoFactorEnrollment(ctx context.Context, userID string, enrollmentToken bool, sessionID string) (TwoFactorEnrollmentScope, error)
	ChangePassword(ctx context.Context, userID, current, new string, keepSessionID *string) error
	CheckPendingRegistrationConflict(ctx context.Context, email, username string) (bool, bool, error)
	CheckPhoneRegistrationConflict(ctx context.Context, phone, username string) (bool, bool, error)
	CheckSMSHealth(ctx context.Context) error
	CheckUserPassword(ctx context.Context, userID, pass string) error
	ClaimDPoPProof(ctx context.Context, key string, ttl time.Duration) (bool, error)
	CompleteExternalLogin(ctx context.Context, in ExternalLoginInput) (LoginOutcome, error)
	CompleteLoginChallenge(ctx context.Context, in LoginChallengeInput) (LoginOutcome, error)
	Config() Config
	ConfirmPasswordReset(ctx context.Context, token, newPassword string) (string, error)
	ConfirmVerification(ctx context.Context, in VerificationInput) (LoginOutcome, error)
	ContinueRefreshMFA(ctx context.Context, userID, sessionID string) (LoginOutcome, error)
	CreateAccountRegistrationInvite(ctx context.Context, req CreateAccountRegistrationInviteRequest) (AccountRegistrationInviteCreated, error)
	CreateInstanceForSubject(ctx context.Context, group authkit.GroupRef, displayName, ownerUserID string) (CreateInstanceResult, error)
	DefineGroupCustomRole(ctx context.Context, actorUserID string, group authkit.GroupRef, def authkit.CustomRoleDef) error
	DelegationAuthorizer() DelegationAuthorizer
	DeleteGroupCustomRole(ctx context.Context, actorUserID string, group authkit.GroupRef, role authkit.Role) error
	DeletePasskey(ctx context.Context, userID, id string) error
	DeletePendingPhoneRegistrationByPhone(ctx context.Context, phone string) error
	DeletePendingRegistrationByEmail(ctx context.Context, email string) error
	DeleteRemoteApplication(ctx context.Context, issuer string) error
	Disable2FAFactorWithRemovedRoles(ctx context.Context, userID, factorID string) ([]RemovedMFARoleAssignment, error)
	Disable2FAWithRemovedRoles(ctx context.Context, userID string) ([]RemovedMFARoleAssignment, error)
	EnrollTwoFactor(ctx context.Context, in TwoFactorEnrollInput) (TwoFactorEnrollOutcome, error)
	EphemeralRedisClient() *redis.Client
	ExchangeRefreshToken(ctx context.Context, refreshToken string, ua string, ip net.IP) (idToken string, expiresAt time.Time, newRefresh string, err error)
	FinishDeviceKeyEnrollment(ctx context.Context, enrollmentID, code, signature, secondFactor string) (DeviceKeyAuthResult, error)
	FinishDeviceKeyLogin(ctx context.Context, challengeID, signature string) (DeviceKeyAuthResult, error)
	FinishPasskeyLogin(ctx context.Context, response []byte, userAgent string, ip net.IP) (LoginOutcome, error)
	ConfirmAccountRecovery(ctx context.Context, token string) error
	FinishPasskeyRegistration(ctx context.Context, userID string, response []byte) (Passkey, error)
	GenerateSIWSChallenge(ctx context.Context, cache siws.ChallengeCache, domain, address, username string) (siws.SignInInput, error)
	Get2FASettings(ctx context.Context, userID string) (*TwoFactorSettings, error)
	GetPendingPhoneRegistrationByPhone(ctx context.Context, phone string) (*PendingRegistration, error)
	GetPendingRegistrationByEmail(ctx context.Context, email string) (*PendingRegistration, error)
	GetPreferredLanguage(ctx context.Context, userID string) (PreferredLanguage, error)
	GetProviderLinkByIssuer(ctx context.Context, issuer, subject string) (string, *string, error)
	GetRemoteApplicationBySlug(ctx context.Context, slug string) (*RemoteApplication, error)
	GroupNamingState(ctx context.Context, id string) (authkit.NamingState, error)
	HasEmailSender() bool
	HasPassword(ctx context.Context, userID string) (bool, error)
	HasProviderLink(ctx context.Context, userID, issuer, providerSlug string) (bool, error)
	JWKS() jwtkit.JWKS
	LinkSolanaWallet(ctx context.Context, cache siws.ChallengeCache, userID string, output siws.SignInOutput) error
	ListDeviceKeys(ctx context.Context, userID, currentID string) ([]DeviceKey, error)
	ListPasskeys(ctx context.Context, userID string) ([]Passkey, error)
	ListRemoteApplicationsForGroup(ctx context.Context, group authkit.GroupRef) ([]RemoteApplication, error)
	ListSessionEvents(ctx context.Context, userID string, eventTypes ...SessionEventType) ([]AuthSessionEvent, error)
	ListUserSessions(ctx context.Context, userID string) ([]Session, error)
	LogSessionFailed(ctx context.Context, userID string, sessionID string, reason *string, ip *string, ua *string)
	MarkSessionAuthenticated(ctx context.Context, userID, sessionID string) error
	MarkSessionAuthenticatedWithMethods(ctx context.Context, userID, sessionID string, authMethods []string) error
	MintDelegatedAccessToken(ctx context.Context, p DelegatedAccessParams) (string, error)
	NamingPolicy() authkit.NamingPolicy
	PasskeysEnabled() bool
	PasswordLogin(ctx context.Context, in PasswordLoginInput) (LoginOutcome, error)
	PasswordlessLogin(ctx context.Context, in PasswordlessLoginInput) (LoginOutcome, error)
	PermissionGroupSchema() *GroupSchema
	Postgres() *pgxpool.Pool
	ProviderSlugs(ctx context.Context, userID string) ([]string, error)
	PublicKeysByKID() map[string]crypto.PublicKey
	PublicNativeUserRegistrationEnabled() bool
	RecordFailedDeviceKeyEnrollment(ctx context.Context, enrollmentID string)
	RedeemGroupInviteLink(ctx context.Context, code, redeemerUserID string) (RedeemGroupInviteLinkResult, error)
	RedisKeyPrefix() string
	RegenerateBackupCodes(ctx context.Context, userID string) ([]string, error)
	Register(ctx context.Context, in RegisterInput) (RegisterOutcome, error)
	RegisterApplicationFromDomain(ctx context.Context, domain string) (*RegisteredApplication, error)
	RegistrationVerificationEnabled() bool
	RenamePasskey(ctx context.Context, userID, id, label string) error
	RequestEmailChange(ctx context.Context, userID, newEmail string) error
	RequestEmailVerification(ctx context.Context, email string, ttl time.Duration) error
	RequestPasswordReset(ctx context.Context, email string, ttl time.Duration, ip *string, ua *string) error
	RequestPhoneChange(ctx context.Context, userID, newPhone string) error
	RequestPhonePasswordReset(ctx context.Context, phone string, ttl time.Duration, ip *string, ua *string) error
	RequestPhoneVerification(ctx context.Context, phone string, ttl time.Duration) error
	Require2FAForStepUpMethod(ctx context.Context, userID, sessionID, method string) (destination, selectedMethod string, factor TwoFactorFactor, err error)
	ResendLoginChallenge(ctx context.Context, userID, nonce, factorID string) (*TwoFactorChallenge, error)
	ResendRegistration(ctx context.Context, identifier string) (bool, error)
	RevokeDeviceKey(ctx context.Context, userID, currentID, targetID string) error
	RevokeIssuerSessions(ctx context.Context, userID string, keepSessionID *string) error
	RevokeOtherDeviceKeys(ctx context.Context, userID, currentID string) error
	RevokeSessionByIDForUser(ctx context.Context, userID, sessionID string) error
	SMSAvailable() bool
	SMSHealthy() bool
	Schema() string
	SendWelcome(ctx context.Context, userID string)
	SessionFreshness(ctx context.Context, userID, sessionID string, now time.Time) (SessionFreshness, error)
	SetPasswordAfterFreshAuth(ctx context.Context, userID, new string, keepSessionID *string) error
	SetPreferredLanguage(ctx context.Context, userID, language string) error
	SoftDeleteUser(ctx context.Context, id string) error
	SoftDeleteUserAs(ctx context.Context, actorUserID, userID string) error
	RestoreUserAs(ctx context.Context, actorUserID, userID string) error
	StartPasswordless(ctx context.Context, req PasswordlessStartRequest) (PasswordlessStartResult, error)
	TwoFactorAllowedMethods() []string
	TwoFactorEnabled() bool
	UnlinkProviderUnlessLast(ctx context.Context, userID, provider string) (bool, error)
	UpdateGroupInstanceAs(ctx context.Context, actorUserID, groupID string, update authkit.GroupInstanceUpdate) (authkit.GroupInstance, error)
	UserNamingState(ctx context.Context, id string) (authkit.NamingState, error)
	UserProfile(ctx context.Context, in ProfileInput) (authkit.UserProfile, error)
	ValidatePassword(value string, identifiers ...string) error
	ValidateUsername(username string) error
	ValidateUsernameForRegistration(ctx context.Context, username string) (string, error)
	ValidateVerificationConfiguration() error
	Verify2FAStepUpMethodCode(ctx context.Context, userID, sessionID, method, code string) (bool, error)
	VerifyBackupCode(ctx context.Context, userID, backupCode string) (bool, error)
	VerifyPendingPassword(ctx context.Context, email, pass string) bool
	VerifyPendingPhonePassword(ctx context.Context, phone, pass string) bool
	VerifySIWSAndLogin(ctx context.Context, cache siws.ChallengeCache, output siws.SignInOutput, extra map[string]any) (LoginOutcome, error)
}

HTTPBackend is a local transport construction capability supplied only to HTTPConfiguration.BuildHTTP. It is not the portable application Client, and Runtime deliberately provides no accessor for it.

type HTTPConfiguration added in v0.115.0

type HTTPConfiguration interface {
	BuildHTTP(HTTPBackend) (HTTPSurface, error)
}

HTTPConfiguration constructs a local runtime's HTTP surface. authhttp.Config implements this small construction boundary without an embedded/authhttp package cycle. It is trusted host configuration, never a Client operation.

type HTTPRoute added in v0.115.0

type HTTPRoute struct {
	Method  string
	Path    string
	Handler http.Handler
}

HTTPRoute is one fully anchored configured registration. Handler preserves AuthKit's canonical authentication and request-path processing.

type HTTPSurface added in v0.115.0

type HTTPSurface interface {
	Routes() []HTTPRoute
	Verifier() *verify.Verifier
	Close()
}

HTTPSurface is the runtime-owned result of local HTTP configuration.

type IdentityConfig

type IdentityConfig struct {
	// Providers are the external identity providers: authprovider.Google/
	// Apple/Discord/GitHub for the built-ins, authprovider.OIDC/OAuth2 for any
	// other IdP. Each provider owns its quirks and carries its own Name.
	Providers []authprovider.Provider
}

IdentityConfig declares external OAuth2/OIDC identity providers.

type ImportUnverifiedSolanaLinkInput added in v0.98.0

type ImportUnverifiedSolanaLinkInput = authkit.ImportUnverifiedSolanaLinkInput

type ImportUnverifiedSolanaLinkResult added in v0.98.0

type ImportUnverifiedSolanaLinkResult = authkit.ImportUnverifiedSolanaLinkResult

type ImportUnverifiedSolanaLinkStatus added in v0.98.0

type ImportUnverifiedSolanaLinkStatus = authkit.ImportUnverifiedSolanaLinkStatus

type ImportUnverifiedSolanaLinksResult added in v0.98.0

type ImportUnverifiedSolanaLinksResult = authkit.ImportUnverifiedSolanaLinksResult

type ImportUserInput added in v0.98.0

type ImportUserInput = authkit.ImportUserInput

type ImportUserResult added in v0.98.0

type ImportUserResult = authkit.ImportUserResult

ImportUserResult is the outcome for one input row, addressed by its original index in the input slice.

type ImportUserStatus added in v0.98.0

type ImportUserStatus = authkit.ImportUserStatus

ImportUserStatus is the per-row outcome of ImportUsers.

type ImportUsersResult added in v0.98.0

type ImportUsersResult = authkit.ImportUsersResult

ImportUsersResult aggregates the per-row outcomes plus rollup counts.

type InstanceCreationDef added in v0.90.0

type InstanceCreationDef = authkit.InstanceCreationDef

InstanceCreationDef is the per-persona generated-creation config (#263).

type IssuedSession added in v0.98.0

type IssuedSession struct {
	SessionID       string
	RefreshToken    string
	AccessToken     string
	AccessExpiresAt time.Time
}

IssuedSession is a freshly established refresh session plus its paired access token.

func (IssuedSession) TokenSet added in v0.98.0

func (s IssuedSession) TokenSet() authkit.TokenSet

TokenSet is the wire shape of an IssuedSession.

type KeysConfig

type KeysConfig struct {
	// Source can be nil — if nil, authkit resolves keys from the filesystem:
	// <Path>/keys.json (default /vault/auth), hot-reloaded on rotation. When no
	// keys.json exists, construction FAILS unless AllowEphemeralDevKeys is set.
	// Hosts NEVER handle the private key — they delegate the signing OPERATION
	// to authkit; there is no API that returns a private key or PEM (a future
	// Vault-Transit backend, authkit future #72, drops in behind the same
	// Signer seam).
	Source jwtkit.KeySource
	// Path overrides the filesystem DIRECTORY the local key resolver scans for
	// keys.json (and totp.key, #148) when Source is nil. Empty defaults to
	// /vault/auth. There is no env fallback (#231; AUTHKIT_KEYS_PATH is read by
	// cmd/authkit-server only).
	Path string
	// AllowEphemeralDevKeys opts in to auto-generating an RSA dev signing
	// keypair when Source is nil and no <Path>/keys.json exists. It lives in
	// memory, unless Path is explicit — then it is written to <Path>/keys.json
	// so restarts reuse it. DEVELOPMENT ONLY — the default (false) is
	// fail-closed: with no keys configured, NewFromConfig returns a hard error
	// instead of silently minting dev keys (#231). This flag is deliberately
	// NOT derived from Environment.
	AllowEphemeralDevKeys bool
	// VerifyOnly constructs the Runtime with NO active signer (#87): token
	// MINTING returns ErrMissingSigner, while VERIFICATION and all RBAC reads
	// work fully and the JWKS endpoint serves an empty key set. When true, key
	// resolution is SKIPPED. Ignored when Source is non-nil. Use it for a
	// pure resource-server / control-plane deployment that only verifies inbound
	// tokens.
	VerifyOnly bool
}

type Keyset

type Keyset struct {
	Active     jwtkit.Signer
	PublicKeys map[string]crypto.PublicKey // kid -> pub
}

Keyset is a fixed active signer + public-key set for the low-level NewService constructor (explicit-key tests). It is converted to a jwtkit.KeySource at construction and never read again directly — hosts that need rotation should provide a live jwtkit.KeySource via Config.Keys.Source / NewFromConfig instead. See #238.

type LoginChallengeInput added in v0.100.0

type LoginChallengeInput struct {
	UserID     string
	Challenge  string
	FactorID   string
	Code       string
	BackupCode bool
	UserAgent  string
	IP         string
}

LoginChallengeInput supplies the second proof; clients never supply AMR or first-factor provenance. Backup codes are independently stored recovery keys.

type LoginOutcome added in v0.98.0

type LoginOutcome struct {
	Recovery       *AccountRecoveryConfirmation
	Enrollment     *authkit.TokenSet
	AllowedMethods []string
	ReturnTo       string
	Created        bool
	Kind           LoginOutcomeKind
	UserID         string
	Reason         error
	Session        *IssuedSession
	Verification   *VerificationRequired
	Challenge      *TwoFactorChallenge
}

LoginOutcome is the result of a password login. Exactly one of Session, Verification and Challenge is set, per Kind; Reason is set for LoginRejected.

type LoginOutcomeKind added in v0.98.0

type LoginOutcomeKind string

LoginOutcomeKind is the closed set of ways a login attempt ends.

const (
	LoginProviderLinked LoginOutcomeKind = "provider_linked"
	LoginContactChanged LoginOutcomeKind = "contact_changed"
	// LoginSessionIssued: the caller is signed in; Session carries the tokens.
	LoginSessionIssued LoginOutcomeKind = "session_issued"
	// LoginVerificationRequired: the identifier still needs verifying; a fresh
	// code was just sent to Verification.Identifier over Verification.Channel.
	LoginVerificationRequired LoginOutcomeKind = "verification_required"
	// LoginTwoFactorRequired: the password verified; a second factor is now
	// pending (Challenge carries the issued challenge and the factor menu).
	LoginTwoFactorRequired LoginOutcomeKind = "2fa_required"
	// LoginTwoFAEnrollmentRequired: the password verified but the deployment
	// requires a second factor the user has not enrolled yet.
	LoginTwoFAEnrollmentRequired LoginOutcomeKind = "2fa_enrollment_required"
	// LoginRejected: no session; Reason says why (ErrInvalidCredentials,
	// ErrUserBanned, ErrPasswordResetRequired).
	LoginRejected LoginOutcomeKind = "rejected"
)
const LoginRecoveryRequired LoginOutcomeKind = "account_recovery_required"

type LoginSessionInput added in v0.98.0

type LoginSessionInput struct {
	UserID      string
	AuthMethods []string       // how the session was established, e.g. {"pwd"}
	Event       string         // session-created audit event, e.g. "password_login"
	Extra       map[string]any // extra access-token claims
	UserAgent   string
	IP          string
}

LoginSessionInput describes the session a completed authentication earns.

type MFAContinuationRequiredError added in v0.100.0

type MFAContinuationRequiredError struct {
	UserID    string
	SessionID string
	Reason    error
}

MFAContinuationRequiredError identifies the already-validated refresh session that needs a first-factor continuation. It never authorizes an arbitrary user.

func (*MFAContinuationRequiredError) Error added in v0.100.0

func (*MFAContinuationRequiredError) Unwrap added in v0.100.0

func (e *MFAContinuationRequiredError) Unwrap() error

type MFAStatus added in v0.98.0

type MFAStatus = authkit.MFAStatus

type MigrationOptions added in v0.110.0

type MigrationOptions struct {
	River       *RiverOwnership
	RiverSchema string
	// RuntimePool identifies the existing database user that will run AuthKit.
	// When supplied, initialization grants that user runtime access directly.
	// Both pools must connect to the same database and remain host-owned.
	// Nil applies migrations without provisioning runtime privileges.
	RuntimePool *pgxpool.Pool
}

MigrationOptions declares River ownership alongside AuthKit initialization. River uses the same declaration as Deps.River. Nil owns River initialization; RiverFromHost skips it. RiverSchema defaults to public, matching Config.River.

type Passkey

type Passkey struct {
	ID                      string     `json:"id"`
	UserID                  string     `json:"user_id,omitempty"`
	Label                   *string    `json:"label,omitempty"`
	Transports              []string   `json:"transports,omitempty"`
	AuthenticatorAttachment string     `json:"authenticator_attachment,omitempty"`
	BackupEligible          bool       `json:"backup_eligible"`
	BackupState             bool       `json:"backup_state"`
	CreatedAt               time.Time  `json:"created_at"`
	LastUsedAt              *time.Time `json:"last_used_at,omitempty"`
}

type PasskeyConfig

type PasskeyConfig struct {
	RPID             string
	RPDisplayName    string
	Origins          []string
	UserVerification string
}

PasskeyConfig configures WebAuthn relying-party identity and UV policy.

type PasswordLoginInput added in v0.98.0

type PasswordLoginInput struct {
	Identifier string
	Password   string
	UserAgent  string
	IP         string
}

PasswordLoginInput is a password login attempt. Identifier is an email (contains "@"), an E.164 phone ("+…") or a username.

type PasswordlessLoginInput added in v0.100.0

type PasswordlessLoginInput struct {
	Identifier string
	Code       string
	Token      string
	UserAgent  string
	IP         string
}

PasswordlessLoginInput selects either a typed code or a link token, never both, and supplies request metadata for the resulting authentication.

type PasswordlessStartRequest added in v0.98.0

type PasswordlessStartRequest = authkit.PasswordlessStartRequest

type PasswordlessStartResult added in v0.98.0

type PasswordlessStartResult = authkit.PasswordlessStartResult

type PendingChangeKind

type PendingChangeKind string

PendingChangeKind identifies one of the four verification-gated "deferred change" flows. They all share the same shape — "hold a change until an emailed/texted code is verified, then finalize it" — so they share one record type, one ephemeral storage namespace, and one set of generic operations, differing only in their per-kind finalizer.

const (
	KindVerifyEmail   PendingChangeKind = "verify_email"
	KindVerifyPhone   PendingChangeKind = "verify_phone"
	KindRegisterEmail PendingChangeKind = "register_email"
	KindRegisterPhone PendingChangeKind = "register_phone"
	KindChangeEmail   PendingChangeKind = "change_email"
	KindChangePhone   PendingChangeKind = "change_phone"
)

type PendingPasskeyAccount added in v0.98.0

type PendingPasskeyAccount struct {
	UserID   string
	Creation *protocol.CredentialCreation
}

PendingPasskeyAccount is a passkey-only account ceremony. UserID is the server-minted uuidv7 that becomes the user id and WebAuthn user handle once FinishPasskeyAccount succeeds; no user row exists before that.

type PendingRegistration

type PendingRegistration struct {
	Email             string
	Username          string
	PasswordHash      string
	PreferredLanguage string
}

PendingRegistration represents an unverified registration

type PermissionGroupStore

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

PermissionGroupStore is the database access layer for permission-groups. It holds a db.DBTX (a *pgxpool.Pool or a pgx.Tx), so callers choose the txn scope.

func NewPermissionGroupStore

func NewPermissionGroupStore(q db.DBTX) *PermissionGroupStore

NewPermissionGroupStore wraps a db.DBTX (pool or transaction).

func (*PermissionGroupStore) AssignRole added in v0.98.0

func (st *PermissionGroupStore) AssignRole(ctx context.Context, groupID string, subject authkit.Subject, role authkit.Role) error

AssignRole replaces the current role for a group and subject. The composite primary key enforces one assignment; callers validate the role definition.

func (*PermissionGroupStore) CanOnGroup added in v0.98.0

func (st *PermissionGroupStore) CanOnGroup(ctx context.Context, schema *GroupSchema, subject authkit.Subject, groupID string, perm authkit.Perm) (bool, error)

CanOnGroup is the end-to-end DB-backed authorization check: walk the target group's chain, preload any custom roles, and test perm coverage against the schema. The caller constructs perm per the two-persona rule (e.g. for an action on a persona-RT resource reached from an ancestor of persona LT, the perm is `LT:RT:<action>`).

func (*PermissionGroupStore) CreateGroup added in v0.98.0

func (st *PermissionGroupStore) CreateGroup(ctx context.Context, g authkit.GroupRef, parentID string) (string, error)

CreateGroup inserts a permission-group and returns its internal id. parentID is empty for the root group. The containment trigger + CHECK enforce shape at the DB (the trigger resolves the parent's persona by parent_id); callers SHOULD also pre-validate via GroupSchema.ValidateParent for a clear error before hitting the DB.

func (*PermissionGroupStore) CreateGroupNamed added in v0.98.0

func (st *PermissionGroupStore) CreateGroupNamed(ctx context.Context, g authkit.GroupRef, parentID, displayName string) (string, error)

CreateGroupNamed is CreateGroup with a first-class display name (#264): free-form, non-unique vanity metadata (the slug stays the unique handle).

func (*PermissionGroupStore) CustomRole added in v0.98.0

func (st *PermissionGroupStore) CustomRole(ctx context.Context, groupID string, role authkit.Role) (permissions []string, requiresMFA bool, err error)

CustomRole returns a single per-group custom role's stored permissions and requires_mfa flag, or (nil, false, nil) if no such custom role is defined — absence is not an error (the caller may be about to CREATE it).

func (*PermissionGroupStore) CustomRolesFor added in v0.98.0

func (st *PermissionGroupStore) CustomRolesFor(ctx context.Context, groupIDs []string) (CustomRoleResolver, error)

CustomRolesFor preloads the custom roles for a set of group ids and returns a CustomRoleResolver backed by the result — so the pure decision core resolves custom-role grants without per-call DB access.

func (*PermissionGroupStore) DeleteCustomRole added in v0.98.0

func (st *PermissionGroupStore) DeleteCustomRole(ctx context.Context, groupID string, role authkit.Role) error

DeleteCustomRole retires a definition and every reference to it. The caller must hold the group lifecycle lock in a transaction. An absent definition is a no-op, so a catalog role cannot accidentally lose its assignments here.

func (*PermissionGroupStore) DeleteGroup added in v0.98.0

func (*PermissionGroupStore) GrantsOnGroup added in v0.98.0

func (st *PermissionGroupStore) GrantsOnGroup(ctx context.Context, schema *GroupSchema, subject authkit.Subject, groupID string) ([]string, error)

GrantsOnGroup is GrantsOnGroups for one group; no grants is an empty slice.

func (*PermissionGroupStore) GrantsOnGroups added in v0.134.0

func (st *PermissionGroupStore) GrantsOnGroups(ctx context.Context, schema *GroupSchema, subject authkit.Subject, groupIDs []string) (map[string][]string, error)

GrantsOnGroups returns, per live target group, the de-duplicated UNION of grant PATTERNS the subject holds across that group's parent chain, resolved against the schema's catalog + per-group custom roles, in one query. Globs like `root:*` are returned verbatim, not expanded. Targets granting nothing are absent. Latent assignments of deleted/reserved accounts are included.

func (*PermissionGroupStore) GroupByInstanceSlug added in v0.98.0

func (st *PermissionGroupStore) GroupByInstanceSlug(ctx context.Context, g authkit.GroupRef) (string, error)

func (*PermissionGroupStore) GroupByLiveInstanceSlug added in v0.98.0

func (st *PermissionGroupStore) GroupByLiveInstanceSlug(ctx context.Context, g authkit.GroupRef) (string, error)

GroupByLiveInstanceSlug resolves (persona, instance_slug) WITHOUT tombstone forwarding — the group currently holding the slug, or ErrGroupNotFound.

func (*PermissionGroupStore) GroupInstanceByID added in v0.98.0

func (st *PermissionGroupStore) GroupInstanceByID(ctx context.Context, groupID string) (GroupInstance, error)

GroupInstanceByID is GroupInstancesByIDs for one id; absence is ErrGroupNotFound.

func (*PermissionGroupStore) GroupInstancesByIDs added in v0.134.0

func (st *PermissionGroupStore) GroupInstancesByIDs(ctx context.Context, groupIDs []string) (map[string]GroupInstance, error)

GroupInstancesByIDs reads many groups' own identity rows (#269), including retained soft-deleted ones (DeletedAt set), in one query. Unknown and malformed ids are absent. Ids are ones the caller already resolved.

func (*PermissionGroupStore) GroupMembers added in v0.98.0

func (st *PermissionGroupStore) GroupMembers(ctx context.Context, groupID string) ([]GroupMember, error)

GroupMembers lists the live role-assignments in a group.

func (*PermissionGroupStore) InstanceSlugAvailable added in v0.98.0

func (st *PermissionGroupStore) InstanceSlugAvailable(ctx context.Context, g authkit.GroupRef) (bool, error)

InstanceSlugAvailable applies exactly the resolver's request-time expiry rule.

func (*PermissionGroupStore) OwnerCount added in v0.98.0

func (st *PermissionGroupStore) OwnerCount(ctx context.Context, groupID string) (int, error)

OwnerCount returns the count of live, unbanned, unreserved user owners and enabled application owners. Lifecycle safety uses the transaction-bound Runtime guard, which also checks the deployment's MFA policy.

func (*PermissionGroupStore) ResolveGroupSlug added in v0.98.0

func (*PermissionGroupStore) RootGroupID added in v0.98.0

func (st *PermissionGroupStore) RootGroupID(ctx context.Context) (string, error)

RootGroupID returns the singleton root group's internal id (ErrGroupNotFound if the deployment has not seeded one yet).

func (*PermissionGroupStore) RootRolesForUsers added in v0.98.0

func (st *PermissionGroupStore) RootRolesForUsers(ctx context.Context, rootGID string, userIDs []string) (map[string][]string, error)

RootRolesForUsers returns, for each user id, the role slugs directly assigned on the root group (rootGID). Root roles are direct assignments on the parentless root group, so no parent walk is needed — this batches a whole page's lookups into one query (the admin-directory enrichment path; avoids a per-row N+1).

func (*PermissionGroupStore) SearchGroupInstances added in v0.98.0

func (st *PermissionGroupStore) SearchGroupInstances(ctx context.Context, persona authkit.Persona, query, afterSlug, afterID string, limit int) ([]GroupInstance, error)

SearchGroupInstances searches canonical names only. Former names are addresses, not additional directory entries. Keyset ordering keeps the host's paginated binding join bounded without loading every group or performing per-row reads.

func (*PermissionGroupStore) SeedContainment added in v0.98.0

func (st *PermissionGroupStore) SeedContainment(ctx context.Context, schema *GroupSchema) error

SeedContainment reconciles the containment schema (group_persona_parents) from a validated GroupSchema. Idempotent; call once at bootstrap so the DB trigger can enforce the declared tree shape. root has no rows (parentless).

func (*PermissionGroupStore) SetGroupDisplayName added in v0.98.0

func (st *PermissionGroupStore) SetGroupDisplayName(ctx context.Context, groupID, displayName string) error

SetGroupDisplayName updates a group's free-form display name.

func (*PermissionGroupStore) SubjectGroups added in v0.98.0

func (st *PermissionGroupStore) SubjectGroups(ctx context.Context, subject authkit.Subject) ([]SubjectGroupMembership, error)

SubjectGroups lists every group membership a subject holds (cross-persona), the data behind /me/groups.

func (*PermissionGroupStore) UnassignRole added in v0.98.0

func (st *PermissionGroupStore) UnassignRole(ctx context.Context, groupID string, subject authkit.Subject, role authkit.Role) error

UnassignRole deletes the matching current assignment.

func (*PermissionGroupStore) UnassignSubject added in v0.98.0

func (st *PermissionGroupStore) UnassignSubject(ctx context.Context, groupID string, subject authkit.Subject) error

UnassignSubject deletes the subject's current assignment in this group.

func (*PermissionGroupStore) UpsertCustomRole added in v0.98.0

func (st *PermissionGroupStore) UpsertCustomRole(ctx context.Context, groupID string, def authkit.CustomRoleDef) error

UpsertCustomRole defines/updates a per-group custom role's permission set and its requires_mfa flag (#247). Only meaningful for personas whose CustomRoles capability is set; the caller enforces that + validates each grant pattern against the group's persona.

func (*PermissionGroupStore) WalkAssignments added in v0.98.0

func (st *PermissionGroupStore) WalkAssignments(ctx context.Context, groupID string, subject authkit.Subject) ([]GroupAssignment, error)

WalkAssignments walks the target group's parent chain to the root and returns the subject's assignments at each ancestor where it holds at least one role — exactly the []GroupAssignment that GroupSchema.ResolveGrants/Can consume. This is the additive walk-up made concrete.

type PersonaCapabilities added in v0.72.0

type PersonaCapabilities = authkit.PersonaCapabilities

type PersonaDef

type PersonaDef struct {
	Name         authkit.Persona
	Roles        []RoleDef       // app-declared; owner (=<persona>:*) is injected if absent
	Parent       authkit.Persona // declared persona; empty only for root. Non-root must name exactly one parent.
	Capabilities PersonaCapabilities
	Catalog      []string
	// Creation opts the persona into the generated instance-creation route
	// (#263): POST /<persona>, owner seeded from the authenticated user. Only
	// root-parented personas may enable it (validated at schema build).
	Creation InstanceCreationDef
}

PersonaDef declares one permission-group persona, which is also the first permission segment. `Name == RootPersona` is the parentless singleton.

func IntrinsicRootPersona

func IntrinsicRootPersona(extraRootRoles ...RoleDef) PersonaDef

IntrinsicRootPersona returns the base `root` PersonaDef authkit ships: the parentless singleton persona. Its apex is the `owner` role (= root:*), auto-injected by normalizePersona. An app passes this to BuildSchema along with EXTRA root roles (bounded operator bundles like doujins's `admin`, which must NOT hold root:roles:manage if they shouldn't be able to promote) and its other personas; the extra root roles may hold any root: perm (intrinsic or app-declared). Custom roles are OFF on root (operators are not end users).

type PreferredLanguage added in v0.98.0

type PreferredLanguage = authkit.PreferredLanguage

type ProfileInput added in v0.98.0

type ProfileInput struct {
	UserID          string
	ClaimsUsername  string // fallback when the row carries no username
	AuthTime        time.Time
	StepUpSatisfied bool // the presented token is fresh enough for sensitive actions
	// EnabledProviders lists the deployment's login providers;
	// ProviderSupportsStepUp reports which linked providers can re-authenticate.
	EnabledProviders       []string
	ProviderSupportsStepUp func(provider string) bool
}

ProfileInput is what the transport knows that the engine does not: the verified claims' username/auth-time/sensitivity and the deployment's provider registry.

type RBACDriftReport added in v0.98.0

type RBACDriftReport struct {
	GroupUserRoles int `json:"group_user_roles"`
	CustomRoles    int `json:"group_custom_roles"`
	APIKeys        int `json:"api_keys"`
}

RBACDriftReport counts orphaned authority rows — assigned group roles, custom roles, and API keys whose role definitions no longer exist.

func (RBACDriftReport) Total added in v0.98.0

func (r RBACDriftReport) Total() int

type RedeemGroupInviteLinkResult added in v0.98.0

type RedeemGroupInviteLinkResult = authkit.RedeemGroupInviteLinkResult

RedeemGroupInviteLinkResult reports which (persona, instance, role) a redemption granted, so the caller/SPA can route the user to the right place.

type RegisterInput added in v0.98.0

type RegisterInput struct {
	Identifier         string
	Username           string
	Password           string
	PreferredLanguage  string
	AccountInviteToken string
	UserAgent          string
	IP                 string
}

RegisterInput is a native-user registration attempt: Identifier is an email or an E.164 phone; the account is password-backed.

type RegisterOutcome added in v0.98.0

type RegisterOutcome struct {
	Login    *LoginOutcome
	Kind     RegisterOutcomeKind
	Username string
	Email    *string
	Phone    *string
	Session  *IssuedSession
}

RegisterOutcome reports who was registered and what happens next.

type RegisterOutcomeKind added in v0.98.0

type RegisterOutcomeKind string

RegisterOutcomeKind is the closed set of ways a registration ends.

const (
	RegisterLoginRequired RegisterOutcomeKind = "login_required"
	// RegisterSessionIssued: the account exists and is signed in (no
	// verification pending).
	RegisterSessionIssued RegisterOutcomeKind = "session_issued"
	// RegisterVerifyEmail / RegisterVerifyPhone: the registration is pending
	// until the code just sent to the identifier is confirmed.
	RegisterVerifyEmail RegisterOutcomeKind = "verify_email"
	RegisterVerifyPhone RegisterOutcomeKind = "verify_phone"
)

type RegisteredApplication added in v0.98.0

type RegisteredApplication = authkit.RegisteredApplication

type RegistrationConfig

type RegistrationConfig struct {
	// Verification controls registration verification: "none"|"optional"|
	// "required". Empty defaults to "none".
	Verification RegistrationVerificationPolicy
	// NativeUserMode controls public native-user self-registration. Empty
	// defaults to "open". Non-open modes disable every public user-creation path
	// while leaving embedded admin/bootstrap core APIs available.
	NativeUserMode RegistrationMode
	// PasswordlessLogin enables contact-based passwordless sessions. Off by
	// default; hosts must opt in before /passwordless/start sends challenges.
	PasswordlessLogin bool
	// PasswordlessAutoRegistration lets a verified unknown contact create a
	// no-password user during passwordless confirmation. Off by default.
	PasswordlessAutoRegistration bool
	// AllowMissingSenders lets verification, contact-change, password-reset and
	// login-code flows proceed when no email/SMS sender is wired: nothing is
	// delivered and the engine hands the code back to its caller (dev rigs read
	// it from there). The default (false) makes a missing sender an error.
	AllowMissingSenders bool
	// VerificationSendTimeout bounds each in-line email/SMS provider send
	// (registration/verification codes, password-reset links, passwordless login
	// codes) so a misconfigured/unreachable provider cannot hang the request that
	// triggered it. 0 (unset) defaults to 15 seconds.
	VerificationSendTimeout time.Duration
}

RegistrationConfig controls verification policy and public self-registration.

type RegistrationMode

type RegistrationMode = authkit.RegistrationMode

type RegistrationVerificationPolicy

type RegistrationVerificationPolicy = authkit.RegistrationVerificationPolicy

Registration policy vocabulary is defined in authkit (core-free) and re-exported here (#147). The former AdminOnly/AdminBootstrapOnly/ManifestOnly modes were removed — RegistrationMode is now public self-registration policy only (Open/InviteOnly/Closed).

type RemoteAppKey added in v0.98.0

type RemoteAppKey = authkit.RemoteAppKey

RemoteAppKey is defined in authkit (core-free) and re-exported here.

type RemoteApplication added in v0.98.0

type RemoteApplication = authkit.RemoteApplication

RemoteApplication is a federation principal: an external system that authenticates by signing JWTs verified against its JWKS/public keys. Defined in authkit (core-free) and re-exported here.

type RemoteApplicationAccessParams added in v0.98.0

type RemoteApplicationAccessParams = authkit.RemoteApplicationAccessParams

RemoteApplicationAccessParams describes a remote application access token to mint (#76): a remote_application signs a short-lived JWT that authenticates it AS ITSELF. The principal's authority is the STORED set AuthKit assigned it (permission-group role membership only), resolved at verify from the validated `iss`. The token therefore carries NO authority role claims of its own — and even if a caller adds them, the verifier ignores them.

type RemovedMFARoleAssignment

type RemovedMFARoleAssignment struct {
	PermissionGroupID string
	Persona           authkit.Persona
	InstanceSlug      string
	Role              authkit.Role
	RemovedAt         time.Time
}

type ResolvedAPIKey added in v0.98.0

type ResolvedAPIKey = authkit.ResolvedAPIKey

ResolvedAPIKey is defined in authkit (core-free) and re-exported here.

type RiverConfig added in v0.110.0

type RiverConfig struct {
	Schema          string
	CleanupInterval time.Duration
}

RiverConfig configures PostgreSQL maintenance. The default schema is public and cleanup runs hourly. Managed clients require a fleet with the same full worker/schedule set; unrelated libraries sharing a schema must compose one host-owned configuration so whichever replica leads has every schedule.

type RiverOwnership added in v0.110.0

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

RiverOwnership declares who initializes and runs River. Nil means AuthKit owns its client. Use RiverFromHost for a fleet shared with other libraries. Pass the same declaration to Deps and MigrationOptions.

func RiverFromHost added in v0.110.0

func RiverFromHost() *RiverOwnership

RiverFromHost selects a host-owned River fleet. AuthKit never migrates, starts, or stops it. Pass RiverJobs() to riverhelpers.New to register AuthKit's workers and schedules in the host fleet.

type RoleDef

type RoleDef struct {
	Name        authkit.Role
	Permissions []string
	RequiresMFA bool
}

RoleDef is a named permission bundle within a persona's catalog. Its permissions are grant patterns, all in the owning persona namespace.

type Runtime added in v0.115.0

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

Runtime owns the local engine and its resources. Applications perform all business and administrator operations through Client. The engine is a named, private field so none of its operation or storage methods escape on Runtime.

func New

func New(cfg Config, deps Deps) (*Runtime, error)

New constructs one local runtime, including Config.HTTP when configured.

func NewWithKeys added in v0.98.0

func NewWithKeys(cfg Config, keys Keyset, deps Deps) (*Runtime, error)

NewWithKeys constructs a runtime with an explicit fixed signing keyset.

func (*Runtime) Client added in v0.115.0

func (r *Runtime) Client() authkit.Client

func (*Runtime) Close added in v0.115.0

func (r *Runtime) Close()

func (*Runtime) ConfigureHTTP added in v0.115.0

func (r *Runtime) ConfigureHTTP(cfg HTTPConfiguration) error

ConfigureHTTP is for hosts that must finish provisioning before selecting HTTP policy. Prefer Config.HTTP when the policy is known at construction.

func (*Runtime) HTTPRoutes added in v0.115.0

func (r *Runtime) HTTPRoutes() ([]HTTPRoute, error)

func (*Runtime) RiverJobs added in v0.115.0

func (r *Runtime) RiverJobs() riverhelpers.Contribution

func (*Runtime) SetEntitlementsProvider added in v0.115.0

func (r *Runtime) SetEntitlementsProvider(provider EntitlementsProvider)

SetEntitlementsProvider resolves the AuthKit/billing construction cycle. The provider remains a host-owned dependency, not a Client operation.

func (*Runtime) Start added in v0.115.0

func (r *Runtime) Start(ctx context.Context) error

func (*Runtime) Verifier added in v0.115.0

func (r *Runtime) Verifier() *verify.Verifier

type SMSHealthChecker

type SMSHealthChecker interface {
	CheckHealth(ctx context.Context) error
}

SMSHealthChecker is an optional capability for SMS senders that can verify, without sending a message, that they are configured to actually deliver (valid credentials, an attached sender, and a verified/registered number). CheckHealth returns nil when delivery is expected to succeed, or a descriptive error explaining why it will not (e.g. an unverified toll-free sender that would otherwise fail silently with Twilio error 30032).

type SMSSender

type SMSSender interface {
	SendVerification(ctx context.Context, phone string, msg VerificationMessage) error
	SendPasswordResetLink(ctx context.Context, phone, resetURL string) error
	SendLoginCode(ctx context.Context, phone, code string) error
	// SendContactChanged goes to the number that was just REPLACED.
	SendContactChanged(ctx context.Context, phone string, change ContactChange) error
}

SMSSender sends verification/login/reset/notice SMS messages.

type ServiceJWTClaims added in v0.98.0

type ServiceJWTClaims = authkit.ServiceJWTClaims

ServiceJWTClaims is defined in authkit (core-free) and re-exported here.

func MintServiceJWT

func MintServiceJWT(ctx context.Context, signer jwtkit.Signer, issuer string, opts ServiceJWTMintOptions) (string, ServiceJWTClaims, error)

MintServiceJWT signs a service JWT with an explicit signer and issuer. Hosts can use this helper when they manage the signing key outside core.Service.

type ServiceJWTMintOptions added in v0.98.0

type ServiceJWTMintOptions = authkit.ServiceJWTMintOptions

ServiceJWTMintOptions controls service-JWT minting for embedded hosts.

type Session added in v0.98.0

type Session = authkit.Session

Session is defined in the lean authkit contract package (#138 inversion); aliased here so engine code keeps using the bare name.

type SessionEventType

type SessionEventType string

SessionEventType identifies a session lifecycle event.

const (
	SessionEventCreated          SessionEventType = "session_created"
	SessionEventRevoked          SessionEventType = "session_revoked"
	SessionEventPasswordChange   SessionEventType = "password_changed"
	SessionEventPasswordRecovery SessionEventType = "password_recovery"
	SessionEventFailed           SessionEventType = "session_failed"
	// SessionEventAccountSessionsRevoked records one account-wide emergency
	// revoke (no session id); each revoked session has its own revoked event.
	SessionEventAccountSessionsRevoked SessionEventType = "account_sessions_revoked"
)

type SessionFreshness

type SessionFreshness struct {
	LastAuthenticatedAt           time.Time
	TimeUntilStepUpRequired       time.Duration
	StepUpRequiredForSensitiveOps bool
	AuthMethods                   []string
}

func (SessionFreshness) AssuranceClaims added in v0.98.0

func (f SessionFreshness) AssuranceClaims() (authTime int64, amr []string, acr string)

type SessionRevokeReason

type SessionRevokeReason string

SessionRevokeReason identifies why a session (or set of sessions) was revoked.

const (
	SessionRevokeReasonUnknown              SessionRevokeReason = ""
	SessionRevokeReasonLogout               SessionRevokeReason = "logout"
	SessionRevokeReasonUserRevoke           SessionRevokeReason = "user_revoke"
	SessionRevokeReasonUserRevokeAll        SessionRevokeReason = "user_revoke_all"
	SessionRevokeReasonAdminRevoke          SessionRevokeReason = "admin_revoke"
	SessionRevokeReasonAdminRevokeAll       SessionRevokeReason = "admin_revoke_all"
	SessionRevokeReasonPasswordChange       SessionRevokeReason = "password_change"
	SessionRevokeReasonAdminSetPassword     SessionRevokeReason = "admin_set_password"
	SessionRevokeReasonContactChange        SessionRevokeReason = "contact_change"
	SessionRevokeReasonUserDisabled         SessionRevokeReason = "user_disabled"
	SessionRevokeReasonBanned               SessionRevokeReason = "banned"
	SessionRevokeReasonSoftDeleted          SessionRevokeReason = "soft_deleted"
	SessionRevokeReasonHardDeleted          SessionRevokeReason = "hard_deleted"
	SessionRevokeReasonEvicted              SessionRevokeReason = "evicted"
	SessionRevokeReasonRefreshReuseDetected SessionRevokeReason = "refresh_reuse_detected"
)

type SolanaLinkedAccount

type SolanaLinkedAccount = authkit.SolanaLinkedAccount

SolanaLinkedAccount is the wire type; see authkit.SolanaLinkedAccount.

type SolanaSNSResolver

type SolanaSNSResolver interface {
	ResolvePrimaryName(ctx context.Context, address string) (string, error)
}

SolanaSNSResolver resolves a wallet's primary SNS name after a verified link. The default talks to the public sdk-proxy; hosts and tests inject their own via WithSolanaSNSResolver.

type SubjectGroupMembership added in v0.98.0

type SubjectGroupMembership = authkit.SubjectGroupMembership

SubjectGroupMembership is one (persona, resource, role) a subject holds.

type TOTPEnrollment added in v0.98.0

type TOTPEnrollment struct {
	UserID      string
	Code        string
	MakeDefault bool
	Mode        FactorEnrollmentMode
}

TOTPEnrollment completes a pending authenticator-app enrollment: Code is the current TOTP for the pending secret; MakeDefault promotes it to the user's default second factor; Mode is the deployment's factor-enrollment policy.

type TokenConfig

type TokenConfig struct {
	// EntitlementAllowlist selects coarse provider-granted names for native
	// access-token snapshots. Empty skips the mint-time provider lookup and
	// omits the claim. Selection never grants an entitlement by itself.
	EntitlementAllowlist []string
	Issuer               string
	IssuedAudiences      []string // tokens issued will contain ALL of these audiences
	ExpectedAudiences    []string // audiences accepted at verification; empty defaults to IssuedAudiences
	AccessTokenDuration  time.Duration
	RefreshTokenDuration time.Duration
	// SessionMaxPerUser caps concurrent refresh sessions per user. 0 (unset)
	// applies the default of 3; any negative value (e.g. -1) means unlimited.
	// Eviction is always evict-oldest.
	SessionMaxPerUser int
	// RefreshRotationGrace is how long a just-rotated refresh token keeps being
	// answered with the successor it rotated into, instead of being read as
	// reuse and revoking the family (ak#274). It exists for the race in which
	// two holders of ONE token refresh at once — a shared credential file, a
	// retried request, a response lost in flight — which is otherwise
	// indistinguishable from theft and is punished as theft. 0 (unset) applies
	// the default of 30s; any negative value disables the window and restores
	// strictly single-use rotation.
	RefreshRotationGrace time.Duration
	// AccountIssuers lists every issuer whose deployment shares this account
	// store (same database schema), e.g. two sites with separate logins over
	// one set of accounts. Account-level revocations — admin emergency revoke,
	// password/contact changes, ban and deletion — cover refresh sessions on
	// all of them. Logout and a user's own session management stay on Issuer.
	// Issuer is always included; empty means Issuer alone. Every deployment
	// sharing the store should configure the same set.
	AccountIssuers []string
}

TokenConfig is the JWT issuing/verification contract plus session limits.

type TrustSourcePolicy added in v0.98.0

type TrustSourcePolicy struct {
	AllowPrivateNetworkJWKS bool
}

NormalizeRemoteAppTrustSource validates the mutually-exclusive trust source of a registration and returns the normalized mode. Empty mode is inferred: a key list means static, otherwise jwks. It is the single validation gate so the XOR rule cannot be bypassed. allowInsecureJWKS relaxes the https/private-address jwks_uri checks (Applications.AllowPrivateNetworkJWKS; local federation only). TrustSourcePolicy relaxes remote-application trust-source validation. AllowPrivateNetworkJWKS admits loopback/private-network JWKS URLs (local development only; production leaves it off, see Config.Applications).

type TwoFactorChallenge added in v0.98.0

type TwoFactorChallenge struct {
	Method      string
	Destination string // where the code went (email/phone), unmasked
	Challenge   string
	Factor      TwoFactorFactor
	Factors     []TwoFactorFactor
}

TwoFactorChallenge is the second-factor step a password login opened.

type TwoFactorConfig

type TwoFactorConfig struct {
	// Mode is the account-wide 2FA policy: Disabled (no enroll/challenge/verify
	// routes usable), Optional (users may enroll), or Required (every user must
	// enroll before normal session use; existing un-enrolled users are challenged
	// on their next authenticated request). Empty defaults to Optional; other
	// values fail construction. Per-role
	// RoleDef.RequiresMFA remains available for narrower enforcement.
	Mode TwoFactorMode

	// Methods is the set of second-factor channels the host enables
	// (Email/SMS/TOTP). Empty defaults to all three. A method whose dependency is
	// missing (e.g. SMS with no SMS sender) fails closed regardless of this list.
	Methods []TwoFactorMethod

	// TOTPSecretKey encrypts persisted authenticator-app shared secrets. It must
	// be 16, 24, or 32 RAW bytes (not base64/hex). This is an OVERRIDE for
	// tests/custom key management; the normal path loads the key from
	// <Keys.Path>/totp.key (vault-mounted key material, same model as JWT
	// signing keys; wired in NewFromConfig, #232). An override of any other
	// length is a hard construction error. Without either, TOTP enrollment
	// fails closed.
	TOTPSecretKey []byte
}

TwoFactorConfig configures 2FA policy and key material (#148).

type TwoFactorEnrollInput added in v0.98.0

type TwoFactorEnrollInput struct {
	LoginChallenge string
	// SessionID is the caller's session; a confirmed code marks it 2FA-verified.
	SessionID   string
	UserAgent   string
	IP          string
	UserID      string
	Mode        FactorEnrollmentMode
	Method      string // "email" | "sms" | "totp"; empty with FactorID+MakeDefault re-points the default
	Code        string // email/SMS setup code or TOTP code; empty starts the method's setup
	PhoneNumber string
	MakeDefault bool
	FactorID    string
}

TwoFactorEnrollInput is one enrollment request.

type TwoFactorEnrollKind added in v0.98.0

type TwoFactorEnrollKind string

TwoFactorEnrollKind is the closed set of enrollment results.

const (
	TwoFactorEnrollDefaultSet  TwoFactorEnrollKind = "default_set"
	TwoFactorEnrollCodeSent    TwoFactorEnrollKind = "code_sent"    // email/SMS setup code delivered
	TwoFactorEnrollTOTPStarted TwoFactorEnrollKind = "totp_started" // secret + otpauth URI handed out
	TwoFactorEnrollEnabled     TwoFactorEnrollKind = "enabled"
)

type TwoFactorEnrollOutcome added in v0.98.0

type TwoFactorEnrollOutcome struct {
	Login           *LoginOutcome
	Kind            TwoFactorEnrollKind
	Method          string
	Secret          string
	OTPAuthURI      string
	BackupCodes     []string
	SessionVerified bool
}

TwoFactorEnrollOutcome carries the TOTP material for TwoFactorEnrollTOTPStarted and the plaintext backup codes (shown once) for TwoFactorEnrollEnabled. SessionVerified reports that the input session now holds 2FA assurance.

type TwoFactorEnrollmentScope added in v0.98.0

type TwoFactorEnrollmentScope struct {
	Mode       FactorEnrollmentMode
	HasFactors bool
}

TwoFactorEnrollmentScope is what an enrollment call may do: the factor slot policy and whether the account already holds a factor.

type TwoFactorFactor

type TwoFactorFactor struct {
	ID           string
	UserID       string
	Method       string
	PhoneNumber  *string
	TOTPSecret   []byte
	LastTOTPStep *int64
	IsDefault    bool
	Enabled      bool
	CreatedAt    time.Time
	UpdatedAt    time.Time
}

type TwoFactorMethod added in v0.98.0

type TwoFactorMethod = authkit.TwoFactorMethod

type TwoFactorMode added in v0.98.0

type TwoFactorMode = authkit.TwoFactorMode

Two-factor policy vocabulary is defined in authkit (core-free) and re-exported here (#148).

type TwoFactorSettings

type TwoFactorSettings struct {
	UserID       string
	Enabled      bool
	Method       string // "email", "sms", or "totp"
	PhoneNumber  *string
	TOTPSecret   []byte
	LastTOTPStep *int64
	BackupCodes  []string // Hashed backup codes
	Factors      []TwoFactorFactor
	CreatedAt    time.Time
	UpdatedAt    time.Time
}

type User added in v0.98.0

type User = authkit.User

User is defined in the lean authkit contract package (#138 inversion); aliased here so engine code keeps using the bare name.

type VerificationInput added in v0.100.0

type VerificationInput struct {
	Identifier string
	Code       string
	Token      string
	UserID     string
	SessionID  string
	UserAgent  string
	IP         string
}

VerificationInput completes a delivered code/link. UserID and SessionID are supplied only from an authenticated host principal for contact changes.

type VerificationMessage

type VerificationMessage struct {
	// Fixed-length numeric code for manual entry (optional).
	Code string
	// AuthKit-built scanner-safe verification link (optional).
	LinkURL string
	// Purpose lets senders vary copy without adding new sender methods.
	Purpose string
}

VerificationMessage is the payload AuthKit hands a sender: a code, a link, or both. Purpose lets senders vary copy without adding new methods.

func (VerificationMessage) Validate added in v0.98.0

func (m VerificationMessage) Validate() error

type VerificationRequired added in v0.98.0

type VerificationRequired struct {
	Identifier string
	Channel    string // "email" | "phone"
}

VerificationRequired names the contact channel a login is parked on.

type VerifiedPasskey added in v0.98.0

type VerifiedPasskey struct {
	UserID         string
	PasskeyID      string
	CredentialID   string
	BackupEligible bool
	BackupState    bool
	// contains filtered or unexported fields
}

VerifiedPasskey is the identity proof a discoverable assertion yields: the stable user and the credential that signed. It carries no session, token, cookie or claim; the host binds it to its own pending operation.

Source Files

Jump to

Keyboard shortcuts

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