embedded

package
v0.99.1 Latest Latest
Warning

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

Go to latest
Published: Sep 6, 2026 License: MIT Imports: 55 Imported by: 0

Documentation

Overview

Package embedded is the AuthKit engine: the concrete *Client 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 profiles.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 (
	ErrAccountRegistrationInviteConsumed = authkit.ErrAccountRegistrationInviteConsumed
	ErrAccountRegistrationInviteExpired  = authkit.ErrAccountRegistrationInviteExpired
	ErrAccountRegistrationInviteNotFound = authkit.ErrAccountRegistrationInviteNotFound
	ErrAccountRegistrationInviteRevoked  = authkit.ErrAccountRegistrationInviteRevoked
)
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
	ErrTwoFASendFailed             = authkit.ErrTwoFASendFailed
	ErrTwoFAChallengeFailed        = authkit.ErrTwoFAChallengeFailed
	ErrSessionIssueFailed          = authkit.ErrSessionIssueFailed
	ErrEmailVerificationSendFailed = authkit.ErrEmailVerificationSendFailed
	ErrPhoneVerificationSendFailed = authkit.ErrPhoneVerificationSendFailed
)

ErrTwoFASendFailed etc. are the flow sentinels a transport maps (root package).

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
	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 (
	// ErrAttributeDefNotFound is defined in authkit (core-free) and re-exported here.
	ErrAttributeDefNotFound = authkit.ErrAttributeDefNotFound
	// ErrInvalidAttributeDef indicates a malformed definition registration.
	ErrInvalidAttributeDef = authkit.ErrInvalidAttributeDef
)
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 ErrCannotRemoveLastAdminRole = authkit.ErrCannotRemoveLastAdminRole

ErrCannotRemoveLastAdminRole is returned by the permission-group last-owner guard (refuseIfLastOwner) 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 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 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 (*Client).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 ValidatePassword

func ValidatePassword(value string) error

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 ValidateUsername

func ValidateUsername(username 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 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 (profiles.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 Client

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

Client is the core auth service used by HTTP adapters.

func New

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

New builds the engine from host configuration and runtime dependencies. Deps.Postgres is required; with neither Deps.Redis nor Deps.EphemeralStore the ephemeral store is the per-process memory store, which needs the explicit Config.Ephemeral.AllowMemory opt-in (#305). If Keys.Source is nil, keys are resolved from <Keys.Path>/keys.json — or, ONLY with the explicit Keys.AllowEphemeralDevKeys opt-in, generated for dev.

func NewWithKeys added in v0.98.0

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

NewWithKeys is the low-level constructor: explicit Keyset, no key/TOTP resolution, no required-field checks, no memory-store default. The Keyset is fixed for the lifetime of the Client — hosts that need hot-reloaded signing keys construct via New with a live jwtkit.KeySource (#238).

func (*Client) AdminDeleteUser added in v0.98.0

func (s *Client) AdminDeleteUser(ctx context.Context, id string) error

AdminDeleteUser hard-deletes the user in ONE transaction: sessions are revoked first (for the audit trail; every AuthKit dependent table, refresh_sessions and group_user_roles included, cascades on the row delete). A host table that references profiles.users(id) without ON DELETE CASCADE aborts the whole delete with ErrUserReferenced, so a user is never left half-deleted (#304).

func (*Client) AdminGetUser

func (s *Client) AdminGetUser(ctx context.Context, id string) (*AdminUser, error)

func (*Client) AdminListUsers

func (s *Client) AdminListUsers(ctx context.Context, opts AdminUserListOptions) (*AdminListUsersResult, error)

AdminListUsers is the generic admin user-directory list (issue #91): generic role/status filter + search + sort + offset pagination, with optional provider-backed entitlement filtering. Each row is enriched with role slugs and (via the entitlements provider) entitlement names.

func (*Client) AdminRevokeUserSessions

func (s *Client) AdminRevokeUserSessions(ctx context.Context, userID string) error

func (*Client) AdminRevokeUserSessionsAs added in v0.98.0

func (s *Client) AdminRevokeUserSessionsAs(ctx context.Context, actorUserID, userID string) error

AdminRevokeUserSessionsAs is the actor-aware AdminRevokeUserSessions.

func (*Client) AdminSetPassword

func (s *Client) AdminSetPassword(ctx context.Context, userID, new string) error

AdminSetPassword force-sets a user's password (admin only, no current password required)

func (*Client) ApplyBootstrapManifest

func (s *Client) ApplyBootstrapManifest(ctx context.Context, manifest BootstrapManifest, opts BootstrapReconcileOptions) (BootstrapManifestResult, error)

func (*Client) AssignGroupRole

func (s *Client) AssignGroupRole(ctx context.Context, group authkit.GroupRef, subject authkit.Subject, role authkit.Role) error

AssignGroupRole grants a subject a role in the group addressed by (persona, instanceSlug). The role must be a catalog role (or any role for custom-enabled types). Gated by the MFA-required-role rule (#148/root-owner-MFA); genesis callers that must run before any policy can apply use AssignGroupRoleGenesis.

func (*Client) AssignGroupRoleAs

func (s *Client) AssignGroupRoleAs(ctx context.Context, actorUserID string, group authkit.GroupRef, subject authkit.Subject, role authkit.Role) error

AssignGroupRoleAs is the actor-aware AssignGroupRole: it enforces the #136 capability + no-escalation rules against actorUserID before assigning. Runtime callers (HTTP role-management endpoints) use this; genesis paths (bootstrap, migration) keep using the unchecked AssignGroupRole.

func (*Client) AssignGroupRoleGenesis added in v0.98.0

func (s *Client) AssignGroupRoleGenesis(ctx context.Context, group authkit.GroupRef, subject authkit.Subject, role authkit.Role) error

AssignGroupRoleGenesis grants a role with NEITHER actor-authz (#136) NOR the MFA-required-role gate (#148/root-owner-MFA). Reserved for genesis/bootstrap callers (GenesisClient, the bootstrap manifest) — the deploy-time trust root that runs before any actor-authorized request path (or any chance to enroll MFA) exists, so no runtime policy can apply yet. Never call this from a runtime request handler; use AssignGroupRole or AssignGroupRoleAs there.

func (*Client) AssignRemoteApplicationRole added in v0.98.0

func (s *Client) AssignRemoteApplicationRole(ctx context.Context, appID string, role authkit.Role) error

AssignRemoteApplicationRole grants a remote_application a role in its own controlling permission-group with NO actor check (#308): reachable only via bootstrap and embedded.Client.Genesis(). Runtime callers use AssignRemoteApplicationRoleAs.

func (*Client) AssignRemoteApplicationRoleAs added in v0.98.0

func (s *Client) AssignRemoteApplicationRoleAs(ctx context.Context, actorUserID string, group authkit.GroupRef, appSlug string, role authkit.Role) error

AssignRemoteApplicationRoleAs is the actor-aware remote-application role assignment behind PUT /<persona>/:instance_slug/remote-applications/:app/ roles/:role (#263) — the SubjectKindRemoteApp symmetric of the member-role route. The :app slug must resolve to a remote application CONTROLLED BY the addressed group (same scope rule as the remote-app delete route), and the actor must hold the persona's credentials:manage capability plus every permission the role confers (no-escalation), so nobody can grant an application authority above their own.

func (*Client) AssignRoleBySlug

func (s *Client) AssignRoleBySlug(ctx context.Context, userID string, role authkit.Role) error

Exported wrappers for admin/HTTP adapters.

func (*Client) AssignRoleBySlugAs

func (s *Client) AssignRoleBySlugAs(ctx context.Context, actorUserID, userID string, role authkit.Role) error

AssignRoleBySlugAs is the actor-aware root-group convenience (the runtime equivalent of assignRoleBySlug). "owner" is no longer a reserved special case: it is assignable only by an actor who already holds root:* (rule 2).

func (*Client) AssignRolesBySlugAs added in v0.80.0

func (s *Client) AssignRolesBySlugAs(ctx context.Context, actorUserID string, userIDs []string, role authkit.Role) ([]authkit.OpResult, error)

func (*Client) BanUser

func (s *Client) BanUser(ctx context.Context, userID string, reason *string, until *time.Time, bannedBy string) error

BanUser disables a user account and stores ban metadata. bannedBy is the acting user and must hold every root grant the target holds (#286), so a bounded operator can never lock out a more privileged account. The ban, session revoke and device-key revoke commit together.

func (*Client) BeginDeviceKeyEnrollment added in v0.98.0

func (s *Client) BeginDeviceKeyEnrollment(ctx context.Context, email, publicKey, label string) (DeviceKeyChallenge, error)

BeginDeviceKeyEnrollment sends an email proof and records the proposed key.

func (*Client) BeginDeviceKeyLogin added in v0.98.0

func (s *Client) BeginDeviceKeyLogin(ctx context.Context, deviceKeyID string) (DeviceKeyChallenge, error)

BeginDeviceKeyLogin returns an indistinguishable challenge for active, revoked, and unknown ids.

func (*Client) BeginDiscoverablePasskeyVerification added in v0.98.0

func (s *Client) BeginDiscoverablePasskeyVerification(ctx context.Context) (*protocol.CredentialAssertion, error)

BeginDiscoverablePasskeyVerification starts an identity-proof ceremony: the public response is identical for every caller, and user verification is required because the passkey is the only factor.

func (*Client) BeginPasskeyAccount added in v0.98.0

func (s *Client) BeginPasskeyAccount(ctx context.Context) (PendingPasskeyAccount, error)

BeginPasskeyAccount starts a passkey-only account: it mints the user id (no row yet), uses its bytes as the discoverable user handle, and requires user verification. Allowed only while public native registration is open.

func (*Client) BeginPasskeyLogin added in v0.98.0

func (s *Client) BeginPasskeyLogin(ctx context.Context, identifier string) (*protocol.CredentialAssertion, error)

BeginPasskeyLogin always issues a discoverable assertion with an empty allowCredentials list (AK2-PK-002): scoping it to a known identifier would leak account existence and credential ids to an unauthenticated caller, so the identifier is deliberately ignored and the user is resolved at finish from the asserted credential's user handle.

func (*Client) BeginPasskeyRegistration added in v0.98.0

func (s *Client) BeginPasskeyRegistration(ctx context.Context, userID string) (*protocol.CredentialCreation, error)

BeginPasskeyRegistration starts adding a passkey to an already identified user. The same ceremony finishes as either FinishPasskeyRegistration (add) or FinishPasskeyReplacement (replace all).

func (*Client) BeginTwoFactorEnrollment added in v0.98.0

func (s *Client) BeginTwoFactorEnrollment(ctx context.Context, userID string, enrollmentToken bool, sessionID string) (TwoFactorEnrollmentScope, error)

BeginTwoFactorEnrollment decides the enrollment scope for a caller. An enrollment-only token (issued at login when a factor is mandatory) may fill the FIRST factor only, and only while no session or factor exists — ErrTwoFAFactorExists otherwise. A full session may add further factors.

func (*Client) Can

func (s *Client) Can(ctx context.Context, subject authkit.Subject, group authkit.GroupRef, perm authkit.Perm) (bool, error)

Can is the Client-level authorization check: resolve the group addressed by (persona, instanceSlug), then test perm coverage via the additive walk-up. The caller constructs perm per the two-persona rule (LT:RT:action).

func (*Client) CanOnGroup added in v0.98.0

func (s *Client) CanOnGroup(ctx context.Context, subject authkit.Subject, groupID string, perm authkit.Perm) (bool, error)

CanOnGroup evaluates live assignments for the exact resolved group. A rename or reclaimed name cannot redirect this check to a different owner.

func (*Client) ChangePassword

func (s *Client) ChangePassword(ctx context.Context, userID, current, new string, keepSessionID *string) error

ChangePassword sets or changes a user's password. If the user already has a password, current must verify; otherwise current is ignored. Always Argon2id-hashes the new password and upserts it, then revokes all other sessions for the user; caller may keep one active session via keepSessionID.

func (*Client) CheckPendingRegistrationConflict added in v0.98.0

func (s *Client) CheckPendingRegistrationConflict(ctx context.Context, email, username string) (bool, bool, error)

CheckPendingRegistrationConflict checks if email or username exists in users or pending registration cache. Returns (emailTaken, usernameTaken, error)

func (*Client) CheckPhoneRegistrationConflict added in v0.98.0

func (s *Client) CheckPhoneRegistrationConflict(ctx context.Context, phone, username string) (bool, bool, error)

CheckPhoneRegistrationConflict checks if phone or username exists in users OR pending tables. Returns (phoneTaken, usernameTaken, error)

func (*Client) CheckSMSHealth

func (s *Client) CheckSMSHealth(ctx context.Context) error

CheckSMSHealth probes whether the configured SMS sender can actually deliver, without sending a message, when the sender implements SMSHealthChecker. The verdict gates phone-based flows via SMSAvailable. It returns the probe error (nil = healthy) so callers can log it. When no sender is configured or the sender cannot self-check, it records healthy (delivery readiness is then governed solely by sender presence, as before).

func (*Client) CheckUserPassword added in v0.98.0

func (s *Client) CheckUserPassword(ctx context.Context, userID, pass string) error

CheckUserPassword is the error-returning form of VerifyUserPassword: nil on success, ErrPasswordResetRequired when the stored hash is flagged HashAlgoLegacyResetRequired (no plaintext can verify; the user must reset), and a generic unauthorized error otherwise. Callers that need to route reset-required users (step-up, change-password) should use this form.

func (*Client) CleanupExpiredAuthState

func (s *Client) CleanupExpiredAuthState(ctx context.Context) error

CleanupExpiredAuthState removes expired transient AuthKit state that lives in postgres. Short-lived verification state — pending registrations, pending email/phone changes, email/phone verifications, and password resets — now lives entirely in the ephemeral store (Redis when multi-instance, in-memory otherwise) and expires automatically by TTL, so no database sweep is needed for it. The postgres sweep covers revoked/expired refresh sessions and their consumed-token history, long-dead invite rows (retained inviteRetention past their terminal moment), and session-event history past Config.SessionEventRetention (#245).

func (*Client) Clear2FAChallenge added in v0.98.0

func (s *Client) Clear2FAChallenge(ctx context.Context, userID string) error

Clear2FAChallenge removes the stored challenge after successful 2FA verification.

func (*Client) ClearEmailVerified added in v0.98.0

func (s *Client) ClearEmailVerified(ctx context.Context, id string) error

ClearEmailVerified marks the user's email address unverified again.

func (*Client) ClearEmailVerifyCodeAttempts added in v0.98.0

func (s *Client) ClearEmailVerifyCodeAttempts(ctx context.Context, email string)

ClearEmailVerifyCodeAttempts resets the per-email failed-attempt counter after a successful confirmation.

func (*Client) ClearPhoneVerifyCodeAttempts added in v0.98.0

func (s *Client) ClearPhoneVerifyCodeAttempts(ctx context.Context, phone string)

ClearPhoneVerifyCodeAttempts resets the per-phone failed-attempt counter after a successful confirmation.

func (*Client) CompleteExternalLogin added in v0.98.0

func (s *Client) CompleteExternalLogin(ctx context.Context, in ExternalLoginInput) (ExternalLoginOutcome, error)

CompleteExternalLogin resolves the identity to a user and signs it in. Resolution errors: ErrProviderAlreadyLinked, ErrProviderChangeRequiresUnlink, ErrAccountExistsLinkRequired, ErrRegistrationDisabled, ErrProviderLinkFailed, ErrUserCreationFailed. Session errors: ErrUserBanned, or a stage-prefixed ErrSessionIssueFailed.

func (*Client) Config added in v0.77.0

func (s *Client) Config() Config

Config returns THE configuration (#237): the host Config, normalized once at construction. Both the engine and the HTTP transport read it — there is no parallel flat options struct (#236 bug class is structurally impossible).

func (*Client) ConfirmEmailChange added in v0.98.0

func (s *Client) ConfirmEmailChange(ctx context.Context, userID, email, code string, keepSessionID *string) error

ConfirmEmailChange verifies the code and applies the new email. Every other session is revoked; keepSessionID (the confirming session) survives.

func (*Client) ConfirmEmailChangeByToken added in v0.98.0

func (s *Client) ConfirmEmailChangeByToken(ctx context.Context, token string) (string, error)

ConfirmEmailChangeByToken applies a pending email change using its high-entropy link token.

func (*Client) ConfirmEmailVerification added in v0.98.0

func (s *Client) ConfirmEmailVerification(ctx context.Context, email, code string) (userID string, err error)

ConfirmEmailVerification verifies a short typed code for a SPECIFIC email and marks email_verified = true. The record is keyed by the account that owns the address, so the code is only ever compared against that account's own record; the HTTP layer caps attempts per-identifier. For the unguessable 256-bit emailed link token use ConfirmEmailVerificationByToken instead.

func (*Client) ConfirmEmailVerificationByToken added in v0.98.0

func (s *Client) ConfirmEmailVerificationByToken(ctx context.Context, token string) (userID string, err error)

ConfirmEmailVerificationByToken verifies the 256-bit emailed link token and marks email_verified = true. The token's own entropy is the security boundary.

func (*Client) ConfirmPasswordReset added in v0.98.0

func (s *Client) ConfirmPasswordReset(ctx context.Context, token, newPassword string) (string, error)

ConfirmPasswordReset verifies token and sets a new password.

func (*Client) ConfirmPasswordlessCode

func (s *Client) ConfirmPasswordlessCode(ctx context.Context, identifier, code string) (PasswordlessConfirmResult, error)

func (*Client) ConfirmPasswordlessToken

func (s *Client) ConfirmPasswordlessToken(ctx context.Context, token string) (PasswordlessConfirmResult, error)

func (*Client) ConfirmPendingPhoneRegistration added in v0.98.0

func (s *Client) ConfirmPendingPhoneRegistration(ctx context.Context, phone, code string) (userID string, err error)

ConfirmPendingPhoneRegistration finalizes a pending phone registration from the short typed code, honored only against the record issued for this phone.

func (*Client) ConfirmPendingPhoneRegistrationByToken added in v0.98.0

func (s *Client) ConfirmPendingPhoneRegistrationByToken(ctx context.Context, token string) (string, error)

ConfirmPendingPhoneRegistrationByToken finalizes a pending phone registration from the 256-bit link token.

func (*Client) ConfirmPendingRegistration added in v0.98.0

func (s *Client) ConfirmPendingRegistration(ctx context.Context, email, code string) (userID string, err error)

ConfirmPendingRegistration finalizes a pending email registration from the short typed code. The code is only honored against the record issued for this exact address (the record is keyed by it), so a guessed code can never confirm another signup; the HTTP layer caps attempts per-identifier. For the 256-bit emailed link token use ConfirmPendingRegistrationByToken instead.

func (*Client) ConfirmPendingRegistrationByToken added in v0.98.0

func (s *Client) ConfirmPendingRegistrationByToken(ctx context.Context, token string) (userID string, err error)

ConfirmPendingRegistrationByToken finalizes a pending email registration from the 256-bit emailed link token, whose entropy is the security boundary.

func (*Client) ConfirmPhoneChange added in v0.98.0

func (s *Client) ConfirmPhoneChange(ctx context.Context, userID, phone, code string, keepSessionID *string) error

ConfirmPhoneChange verifies the code and applies the new phone. Every other session is revoked; keepSessionID (the confirming session) survives.

func (*Client) ConfirmPhoneChangeByToken added in v0.98.0

func (s *Client) ConfirmPhoneChangeByToken(ctx context.Context, token string) (string, error)

ConfirmPhoneChangeByToken applies a pending phone change using its high-entropy link token.

func (*Client) ConfirmPhoneVerificationByTokenUserID added in v0.98.0

func (s *Client) ConfirmPhoneVerificationByTokenUserID(ctx context.Context, token string) (string, error)

ConfirmPhoneVerificationByTokenUserID verifies phone ownership using a one-click token and returns the user ID.

func (*Client) ConfirmPhoneVerificationUserID added in v0.98.0

func (s *Client) ConfirmPhoneVerificationUserID(ctx context.Context, phone, code string) (string, error)

ConfirmPhoneVerificationUserID verifies a token, marks phone_verified = true, and returns the user ID.

func (*Client) ConsumeAccountRegistrationInvite added in v0.98.0

func (s *Client) ConsumeAccountRegistrationInvite(ctx context.Context, email, userID, token string) error

func (*Client) Create2FAChallenge added in v0.98.0

func (s *Client) Create2FAChallenge(ctx context.Context, userID string) (string, error)

Create2FAChallenge creates a short-lived challenge to prove password verification before 2FA.

func (*Client) CreateAccountRegistrationInvite added in v0.72.0

func (s *Client) CreateGroupInviteLink(ctx context.Context, req CreateGroupInviteLinkRequest) (GroupInviteLinkCreated, error)

CreateGroupInviteLink mints an unbound single-use invite link. Returns the plaintext code ONCE.

func (*Client) CreateInstanceForSubject added in v0.98.0

func (s *Client) CreateInstanceForSubject(ctx context.Context, group authkit.GroupRef, displayName, ownerUserID string) (CreateInstanceResult, error)

CreateInstanceForSubject is the #263 creation path: validate the slug against the persona's creation config, gate reserved slugs on the root escalation role, consult the host admission seam, then create the group with ownerUserID seeded as owner. If the slug is already held and the caller is a member of that group, it returns Created=false instead of a conflict.

func (*Client) CreatePendingPhoneRegistrationWithLanguage added in v0.98.0

func (s *Client) CreatePendingPhoneRegistrationWithLanguage(ctx context.Context, phone, username, passwordHash, preferredLanguage string) (string, error)

func (*Client) CreatePendingRegistrationWithLanguage added in v0.98.0

func (s *Client) CreatePendingRegistrationWithLanguage(ctx context.Context, email, username, passwordHash string, ttl time.Duration, preferredLanguage string) (string, error)

func (*Client) CreatePermissionGroup

func (s *Client) CreatePermissionGroup(ctx context.Context, req CreatePermissionGroupRequest) (string, error)

CreatePermissionGroup validates containment against the schema, resolves the parent group, creates the group, and (atomically) seeds the owner assignment. Returns the INTERNAL group id (for the caller's own bookkeeping; never exposed over the wire).

func (*Client) CreateUser

func (s *Client) CreateUser(ctx context.Context, email, username string) (*User, error)

CreateUser inserts a new user with the given email and username.

func (*Client) DefineGroupCustomRole added in v0.98.0

func (s *Client) DefineGroupCustomRole(ctx context.Context, actorUserID string, group authkit.GroupRef, def authkit.CustomRoleDef) error

DefineGroupCustomRole creates/updates a custom role in the group addressed by (persona, instanceSlug), acting as actorUserID. Requires the persona to allow custom roles; every permission must be a valid grant pattern in that persona's namespace (namespace purity) and must not collide with a catalog role name. requiresMFA mirrors RoleDef.RequiresMFA for catalog roles (#247).

#247 SECURITY: redefining an EXISTING custom role is a DEFERRED grant (a widened grant set) — and, for a narrowed one, a deferred revoke — to EVERY subject currently holding it, the same class of risk invite-minting already gates (AK2-AUTHZ-1). Without this check, a bounded admin holding <persona>:roles:manage (but not the role's own grants) could redefine a role someone else holds to the full catalog, instantly widening their OWN effective grants without ever passing AssignGroupRoleAs's no-escalation gate. The actor must hold roles:manage AND already cover every permission in BOTH the role's current grants (if it exists) and the requested ones.

func (*Client) DelegationAuthorizer added in v0.98.0

func (s *Client) DelegationAuthorizer() DelegationAuthorizer

DelegationAuthorizer returns the host-injected delegated-token authorizer (#277), nil when none was wired.

func (*Client) DeleteGroupCustomRole added in v0.98.0

func (s *Client) DeleteGroupCustomRole(ctx context.Context, actorUserID string, group authkit.GroupRef, role authkit.Role) error

DeleteGroupCustomRole removes a custom role from a group, acting as actorUserID. #247 SECURITY: deleting a role is a DEFERRED REVOKE from every subject currently holding it, gated by the same capability + no-escalation rule as DefineGroupCustomRole (covering the role's stored grants; a not-yet-defined role has nothing to revoke, so only the capability check applies).

func (*Client) DeleteGroupInstanceByID added in v0.98.0

func (s *Client) DeleteGroupInstanceByID(ctx context.Context, groupID string, opts DeletePermissionGroupOptions) error

DeleteGroupInstanceByID is the trusted host's lifecycle primitive. The host authorizes deletion before calling it; retries always target the captured UUID. ReleaseSlug releases the current name only, preserving earlier reservations.

func (*Client) DeletePasskey added in v0.98.0

func (s *Client) DeletePasskey(ctx context.Context, userID, id string) error

func (*Client) DeletePendingPhoneRegistrationByPhone added in v0.98.0

func (s *Client) DeletePendingPhoneRegistrationByPhone(ctx context.Context, phone string) error

DeletePendingPhoneRegistrationByPhone removes a pending phone registration for the given phone, if one exists. No-op when none exists.

func (*Client) DeletePendingRegistrationByEmail added in v0.98.0

func (s *Client) DeletePendingRegistrationByEmail(ctx context.Context, email string) error

DeletePendingRegistrationByEmail removes a pending email registration for the given email, if one exists. No-op when none exists.

func (*Client) DeletePermissionGroup added in v0.88.0

func (s *Client) DeletePermissionGroup(ctx context.Context, group authkit.GroupRef, opts DeletePermissionGroupOptions) error

DeletePermissionGroup deletes a group instance (children, role assignments, api keys, and remote applications cascade). Delete-time naming rule (#264 ruling 5): by DEFAULT the slug is TOMBSTONED to the group uuid forever — fail-safe, published references can never be re-claimed. Passing ReleaseSlug frees the name instead; that is safe ONLY for names nothing ever referenced, and the judgment is the host's. authkit itself never deletes a group — dormancy policy is entirely host-side.

func (*Client) DeleteRemoteApplication

func (s *Client) DeleteRemoteApplication(ctx context.Context, issuer string) error

DeleteRemoteApplication removes a remote_application by OIDC issuer URL.

func (*Client) DeriveUsernameForOAuth added in v0.98.0

func (s *Client) DeriveUsernameForOAuth(ctx context.Context, provider, preferred, email, displayName string) string

DeriveUsernameForOAuth prefers provider-preferred usernames; falls back to email local part or display name.

func (*Client) Disable2FAFactorWithRemovedRoles added in v0.98.0

func (s *Client) Disable2FAFactorWithRemovedRoles(ctx context.Context, userID, factorID string) ([]RemovedMFARoleAssignment, error)

func (*Client) Disable2FAWithRemovedRoles added in v0.98.0

func (s *Client) Disable2FAWithRemovedRoles(ctx context.Context, userID string) ([]RemovedMFARoleAssignment, error)

Disable2FAWithRemovedRoles disables account MFA and removes active user role assignments whose catalog role requires MFA.

func (*Client) DocumentStore added in v0.98.0

func (s *Client) DocumentStore() documents.Store

DocumentStore returns the engine-owned documents.Store over the signed_documents table (migration 0005). Digest immutability is enforced by the guarded upsert: an existing digest only ever accepts a compact-JWS replacement for the SAME type + payload bytes.

func (*Client) Enable2FA added in v0.98.0

func (s *Client) Enable2FA(ctx context.Context, userID, method string, phoneNumber *string, mode FactorEnrollmentMode) ([]string, error)

Enable2FA enables two-factor authentication for a user and generates backup codes. Returns the plaintext backup codes (caller must show these to user ONCE).

func (*Client) Enable2FADefault added in v0.98.0

func (s *Client) Enable2FADefault(ctx context.Context, userID, method string, phoneNumber *string, mode FactorEnrollmentMode) ([]string, error)

func (*Client) EnableTOTP2FA added in v0.98.0

func (s *Client) EnableTOTP2FA(ctx context.Context, in TOTPEnrollment) ([]string, error)

EnableTOTP2FA verifies the pending secret and enables authenticator-app 2FA for the user, returning fresh backup codes.

func (*Client) EnrollTwoFactor added in v0.98.0

func (s *Client) EnrollTwoFactor(ctx context.Context, in TwoFactorEnrollInput) (TwoFactorEnrollOutcome, error)

EnrollTwoFactor runs the enrollment decision tree. Input problems: ErrInvalidTwoFAMethod, ErrPhoneNumberRequired, ErrPhoneNumberMustBeE164, ErrInvalidCode, ErrTwoFAFactorExists; engine failures carry a stage prefix wrapping ErrPhoneTwoFAUnavailable / ErrTwoFASetupCodeSendFailed (with the delivery sentinel) / ErrTwoFAEnableFailed.

func (*Client) EnsureRootGroup

func (s *Client) EnsureRootGroup(ctx context.Context) (string, error)

EnsureRootGroup creates the singleton root group if absent (idempotent) and returns its internal id. Concurrent cold boots race the singleton index; the loser adopts the winner's row instead of failing (#258).

func (*Client) EntitlementsProvider

func (s *Client) EntitlementsProvider() EntitlementsProvider

func (*Client) EphemeralBackend added in v0.98.0

func (s *Client) EphemeralBackend() string

EphemeralBackend names the live ephemeral store: "redis", "memory", "custom" (a host-supplied EphemeralStore) or "none".

func (*Client) EphemeralRedisClient added in v0.98.0

func (s *Client) EphemeralRedisClient() *redis.Client

EphemeralRedisClient returns the *redis.Client backing the engine's ephemeral store when it is Redis-backed (Deps.Redis), or nil for a memory store. The HTTP transport reuses it so a host that wired Redis on the engine doesn't also have to pass it to authhttp — one Redis client, no split-brain ephemeral state (authkit #210). The type assertion is also THE redis-vs-memory discriminator.

func (*Client) ExchangeRefreshToken

func (s *Client) ExchangeRefreshToken(ctx context.Context, refreshToken string, ua string, ip net.IP) (idToken string, expiresAt time.Time, newRefresh string, err error)

ExchangeRefreshToken rotates a refresh token and returns a new ID token + refresh token.

func (*Client) ExternalInvitesEnabled

func (s *Client) ExternalInvitesEnabled() bool

ExternalInvitesEnabled exposes the registration-mode gate for HTTP adapters (so a closed-registration deployment can omit/zero the invite-link routes).

func (*Client) FinishDeviceKeyEnrollment added in v0.98.0

func (s *Client) FinishDeviceKeyEnrollment(ctx context.Context, enrollmentID, code, signature, secondFactor string) (DeviceKeyAuthResult, error)

FinishDeviceKeyEnrollment consumes both proofs, enrolls the key, and mints no refresh session. An existing account with a usable second factor must also present it (secondFactor: a factor code or backup code) — email possession alone never enrolls a standing credential on an MFA-protected account (#293).

func (*Client) FinishDeviceKeyLogin added in v0.98.0

func (s *Client) FinishDeviceKeyLogin(ctx context.Context, challengeID, signature string) (DeviceKeyAuthResult, error)

FinishDeviceKeyLogin atomically consumes a challenge and issues only a short access token.

func (*Client) FinishDiscoverablePasskeyVerification added in v0.98.0

func (s *Client) FinishDiscoverablePasskeyVerification(ctx context.Context, response []byte) (VerifiedPasskey, error)

FinishDiscoverablePasskeyVerification validates the assertion and returns the verified user/credential without minting any session or token.

func (*Client) FinishPasskeyAccount added in v0.98.0

func (s *Client) FinishPasskeyAccount(ctx context.Context, response []byte) (*User, Passkey, error)

FinishPasskeyAccount consumes the ceremony once, validates the credential, then inserts the user (no email/username/password), its handle and the passkey in one transaction. A replayed or concurrent finish cannot create a second user because the ceremony consume is atomic.

func (*Client) FinishPasskeyLogin added in v0.98.0

func (s *Client) FinishPasskeyLogin(ctx context.Context, response []byte, userAgent string, ip net.IP) (PasskeyLoginResult, error)

FinishPasskeyLogin composes the verification primitive with the browser session issuance; it is the only passkey path that mints a session.

func (*Client) FinishPasskeyRegistration added in v0.98.0

func (s *Client) FinishPasskeyRegistration(ctx context.Context, userID string, response []byte) (Passkey, error)

func (*Client) FinishPasskeyReplacement added in v0.98.0

func (s *Client) FinishPasskeyReplacement(ctx context.Context, userID string, response []byte) (Passkey, error)

FinishPasskeyReplacement registers the new credential and tombstones every other active passkey of the user in the same transaction, for hosts with a single-passkey policy. Any failure leaves the prior passkeys active.

func (*Client) GenerateAvailableUsername added in v0.98.0

func (s *Client) GenerateAvailableUsername(ctx context.Context, base string) string

GenerateAvailableUsername tries base, then minimal numeric suffixes, then a short fallback.

func (*Client) GenerateSIWSChallenge added in v0.98.0

func (s *Client) GenerateSIWSChallenge(ctx context.Context, cache siws.ChallengeCache, domain, address, username string) (siws.SignInInput, error)

GenerateSIWSChallenge creates a new SIWS challenge for the given address. The challenge is stored in the cache and must be verified within 15 minutes.

func (*Client) Genesis added in v0.79.0

func (s *Client) Genesis() GenesisClient

Genesis returns the unchecked bootstrap/migration sub-client. See GenesisClient.

func (*Client) Get2FASettings added in v0.98.0

func (s *Client) Get2FASettings(ctx context.Context, userID string) (*TwoFactorSettings, error)

Get2FASettings retrieves a user's 2FA settings

func (*Client) GetPendingPhoneRegistrationByPhone added in v0.98.0

func (s *Client) GetPendingPhoneRegistrationByPhone(ctx context.Context, phone string) (*PendingRegistration, error)

GetPendingPhoneRegistrationByPhone looks up a pending phone registration by phone number. (PendingRegistration.Email carries the phone for phone registrations, preserving prior behavior.)

func (*Client) GetPendingRegistrationByEmail added in v0.98.0

func (s *Client) GetPendingRegistrationByEmail(ctx context.Context, email string) (*PendingRegistration, error)

GetPendingRegistrationByEmail looks up a pending registration by email.

func (*Client) GetPreferredLanguage added in v0.98.0

func (s *Client) GetPreferredLanguage(ctx context.Context, userID string) (PreferredLanguage, error)

func (*Client) GetProviderLinkByIssuer added in v0.98.0

func (s *Client) GetProviderLinkByIssuer(ctx context.Context, issuer, subject string) (string, *string, error)

Issuer-based provider link helpers (preferred)

func (*Client) GetRemoteApplication

func (s *Client) GetRemoteApplication(ctx context.Context, issuer string) (*RemoteApplication, error)

GetRemoteApplication returns a remote_application by OIDC issuer URL.

func (*Client) GetRemoteApplicationBySlug added in v0.98.0

func (s *Client) GetRemoteApplicationBySlug(ctx context.Context, slug string) (*RemoteApplication, error)

GetRemoteApplicationBySlug returns a remote_application by slug.

func (*Client) GetSolanaAddress added in v0.98.0

func (s *Client) GetSolanaAddress(ctx context.Context, userID string) (string, error)

GetSolanaAddress retrieves the Solana wallet address linked to a user, if any.

func (*Client) GetSolanaLinkedAccount added in v0.98.0

func (s *Client) GetSolanaLinkedAccount(ctx context.Context, userID string) (*SolanaLinkedAccount, error)

GetSolanaLinkedAccount retrieves the SIWS-linked wallet and its AuthKit-owned metadata.

func (*Client) GetUserByEmail

func (s *Client) GetUserByEmail(ctx context.Context, email string) (*User, error)

GetUserByEmail looks up a user by email.

func (*Client) GetUserByPhone

func (s *Client) GetUserByPhone(ctx context.Context, phone string) (*User, error)

GetUserByPhone looks up a user by phone number.

func (*Client) GetUserByUsername

func (s *Client) GetUserByUsername(ctx context.Context, username string) (*User, error)

GetUserByUsername looks up a user by username.

func (*Client) GetUserMetadata

func (s *Client) GetUserMetadata(ctx context.Context, userID string) (map[string]any, error)

GetUserMetadata returns a user's arbitrary metadata (internal/admin flags).

func (*Client) GroupInstanceByID added in v0.98.0

func (s *Client) GroupInstanceByID(ctx context.Context, groupID string) (GroupInstance, error)

GroupInstanceByID reads the identity already resolved by a host. It never interprets the UUID as a mutable name.

func (*Client) GroupInstanceForSlug added in v0.93.0

func (s *Client) GroupInstanceForSlug(ctx context.Context, group authkit.GroupRef) (GroupInstance, error)

GroupInstanceForSlug reads one instance's own identity — id, persona, slug, display name (#269). This is the read behind GET /<persona>/:instance_slug: the id is a JOIN KEY a host needs for its own ledger rows, never an address. Authorization is the caller's job (the route gates on <persona>:settings:read).

func (*Client) GroupNamingState added in v0.98.0

func (s *Client) GroupNamingState(ctx context.Context, id string) (authkit.NamingState, error)

func (*Client) HardDeleteUser

func (s *Client) HardDeleteUser(ctx context.Context, userID string) error

HardDeleteUser permanently deletes the user row and dependent AuthKit rows via ON DELETE CASCADE.

func (*Client) HardDeleteUserAs added in v0.98.0

func (s *Client) HardDeleteUserAs(ctx context.Context, actorUserID, userID string) error

HardDeleteUserAs is the actor-aware HardDeleteUser.

func (*Client) HardDeleteUsers added in v0.80.0

func (s *Client) HardDeleteUsers(ctx context.Context, userIDs []string) ([]authkit.OpResult, error)

func (*Client) HasEmailSender

func (s *Client) HasEmailSender() bool

HasEmailSender returns true if an email sender is configured.

func (*Client) HasPassword added in v0.98.0

func (s *Client) HasPassword(ctx context.Context, userID string) (bool, error)

HasPassword reports whether the user has a local password set.

func (s *Client) HasProviderLink(ctx context.Context, userID, issuer, providerSlug string) (bool, error)

HasProviderLink reports whether userID holds a link to subject-issuer under providerSlug — the step-up gate's "is this the user's own provider" check.

func (*Client) HasSMSSender

func (s *Client) HasSMSSender() bool

HasSMSSender returns true if an SMS sender is configured.

func (s *Client) ImportUnverifiedSolanaLinks(ctx context.Context, inputs []ImportUnverifiedSolanaLinkInput) (ImportUnverifiedSolanaLinksResult, error)

ImportUnverifiedSolanaLinks imports legacy wallet claims with one outcome per input row. It never verifies a wallet: only a successful SIWS proof may promote an imported claim. Migration tooling only; runtime request handlers must use the normal proof-aware Solana link flow.

func (*Client) ImportUser added in v0.98.0

func (s *Client) ImportUser(ctx context.Context, input ImportUserInput) (*User, error)

func (*Client) ImportUsers

func (s *Client) ImportUsers(ctx context.Context, inputs []ImportUserInput) (ImportUsersResult, error)

ImportUsers bulk-imports users for fast legacy migration (target: 500k+). It is the sole import API: validate/normalize happens in Go (identical to the legacy single-row path) so accuracy is preserved, then clean rows load via chunked multi-row INSERTs — no per-row round-trips.

Semantics are INSERT-OR-SKIP (not upsert): a row whose username/email/phone already exists, or which duplicates an earlier row in the same batch, is skipped and reported, never overwritten. This makes a re-run idempotent (resume a partial import) without clobbering changes a user made after they were imported. Invalid rows are rejected individually and never abort the batch.

Each input may carry an optional pre-hashed PasswordHash; for inserted rows it is stored verbatim (the verify-time hash whitelist still governs login).

func (*Client) IsUserReserved added in v0.98.0

func (s *Client) IsUserReserved(ctx context.Context, userID string) (bool, error)

IsUserReserved reports whether a user is a reserved, non-loginable placeholder (the `reserved` metadata flag). The login gate (ensureUserAccess) consults it so reserved placeholders cannot authenticate. The owner-namespace reservation FLOW that set this flag was removed in the permission-group hard cut (#111); the read gate stays as defense-in-depth for any externally-set flag.

func (*Client) IssueAuthenticatedSession added in v0.98.0

func (s *Client) IssueAuthenticatedSession(ctx context.Context, userID, userAgent string, ip net.IP, authMethods []string, extra map[string]any) (sessionID, refreshToken, accessToken string, accessExpiresAt time.Time, refreshExpiresAt *time.Time, err error)

IssueAuthenticatedSession creates a refresh session AND mints its paired access token for an ALREADY-AUTHENTICATED user in one shot (#227). It loads + gates the user row (ensureUserAccess) and computes MFAStatus ONCE, threading both through the session-creation gate and the access-token mint — instead of the 2× user-read / 2× MFA-read that the separate IssueRefreshSession* + MintAccessToken calls incurred on the password-login and 2FA-verify paths.

authMethods records how the session was established (e.g. []string{"pwd"} for password login, []string{"pwd","otp","mfa"} after a verified second factor). extra is merged into the access token; the freshly-created session id is added as "sid". The banned/deleted/reserved gate and the MFA gate behave exactly as they do for the separate calls (same ErrUserBanned / ErrTwoFAEnrollmentRequired at the same point). Returns the session id so the caller can emit its own session-created audit log.

func (*Client) IssueLoginSession added in v0.98.0

func (s *Client) IssueLoginSession(ctx context.Context, in LoginSessionInput) (IssuedSession, error)

IssueLoginSession creates the refresh session, mints its access token and writes the session-created audit event — the shared tail of every login. The liveness and MFA gates fire exactly as IssueAuthenticatedSession does (ErrUserBanned, ErrTwoFAEnrollmentRequired).

func (*Client) IssueRefreshSession added in v0.98.0

func (s *Client) IssueRefreshSession(ctx context.Context, userID, userAgent string, ip net.IP) (sessionID, refreshToken string, expiresAt *time.Time, err error)

IssueRefreshSession creates a session row and returns a new refresh token string.

func (*Client) IssueRefreshSessionWithAuthMethods added in v0.98.0

func (s *Client) IssueRefreshSessionWithAuthMethods(ctx context.Context, userID, userAgent string, ip net.IP, authMethods []string) (sessionID, refreshToken string, expiresAt *time.Time, err error)

IssueRefreshSessionWithAuthMethods creates a refresh session and records the authentication methods that established it. Callers minting a session after MFA should pass e.g. []string{"pwd", "otp", "mfa"}.

func (*Client) JWKS

func (s *Client) JWKS() jwtkit.JWKS

JWKS returns a JWKS built from the CURRENT public keys — read fresh from the KeySource on every call, so a rotation is reflected on the very next request (#238).

func (*Client) LinkProviderByIssuer

func (s *Client) LinkProviderByIssuer(ctx context.Context, userID, issuer, providerSlug, subject string, email *string) error

func (*Client) LinkSolanaWallet added in v0.98.0

func (s *Client) LinkSolanaWallet(ctx context.Context, cache siws.ChallengeCache, userID string, output siws.SignInOutput) error

LinkSolanaWallet links a Solana wallet to an existing user account.

func (*Client) List2FAFactors added in v0.98.0

func (s *Client) List2FAFactors(ctx context.Context, userID string) ([]TwoFactorFactor, error)

func (*Client) ListAPIKeys

func (s *Client) ListAPIKeys(ctx context.Context, group authkit.GroupRef) ([]APIKey, error)

ListAPIKeys returns metadata for every API key of the permission-group addressed by (persona, instanceSlug), including revoked/expired ones. The secret is never returned.

func (*Client) ListDeviceKeys added in v0.98.0

func (s *Client) ListDeviceKeys(ctx context.Context, userID, currentID string) ([]DeviceKey, error)

ListDeviceKeys returns the user's machine credentials after proving that the device which minted the caller's token is still active.

func (*Client) ListEffectivePermissions

func (s *Client) ListEffectivePermissions(ctx context.Context, subject authkit.Subject, group authkit.GroupRef) ([]string, error)

ListEffectivePermissions returns the subject's effective grant PATTERNS in the group addressed by (persona, instanceSlug) — the de-duplicated union of every perm its roles grant, with globs (e.g. `root:*`) returned VERBATIM. This is the read primitive behind a "what can I do here" introspection endpoint (#421): a client fetches it once and gates UI on the strings (glob-matching with the same authkit.Perm.Matches the server enforces with) instead of re-deriving authority from role slugs. Scoped per group instance BY DESIGN — perms are persona- namespaced, so a global union would be both large and meaningless. An unknown group ⇒ empty (no authority), not an error; real lookup failures propagate (fail-closed — never a partial set returned as if complete).

func (*Client) ListEnabledRemoteApplications added in v0.98.0

func (s *Client) ListEnabledRemoteApplications(ctx context.Context) ([]RemoteApplication, error)

ListEnabledRemoteApplications returns only the enabled remote_applications: the verification-facing snapshot a Verifier trusts issuers from.

func (*Client) ListEntitlements

func (s *Client) ListEntitlements(ctx context.Context, userID string) []string

ListEntitlements returns current entitlement names for a user (fresh from the provider — a one-element batch, #221). A provider failure is logged and returned as none — callers (admin user views) degrade rather than fail.

func (s *Client) ListGroupInviteLinks(ctx context.Context, group authkit.GroupRef) ([]GroupInviteLink, error)

ListGroupInviteLinks lists the group's invite links (active and inactive), newest first. Never returns the code or its hash.

func (*Client) ListGroupMembers

func (s *Client) ListGroupMembers(ctx context.Context, group authkit.GroupRef) ([]GroupMember, error)

ListGroupMembers returns the role-assignments in the group addressed by (persona, instanceSlug).

func (*Client) ListPasskeys added in v0.98.0

func (s *Client) ListPasskeys(ctx context.Context, userID string) ([]Passkey, error)

func (*Client) ListRemoteApplications

func (s *Client) ListRemoteApplications(ctx context.Context) ([]RemoteApplication, error)

ListRemoteApplications returns every registered remote_application, enabled or not (the admin read).

func (*Client) ListRemoteApplicationsForGroup added in v0.98.0

func (s *Client) ListRemoteApplicationsForGroup(ctx context.Context, group authkit.GroupRef) ([]RemoteApplication, error)

ListRemoteApplicationsForGroup returns the remote_applications whose controlling permission_group_id is the group addressed by (persona, instanceSlug) (#111). It resolves the group via the store, then filters remote_applications by permission_group_id so a per-persona management caller sees only the issuers it controls (ListRemoteApplications lists ALL groups').

func (*Client) ListSessionEvents added in v0.98.0

func (s *Client) ListSessionEvents(ctx context.Context, userID string, eventTypes ...SessionEventType) ([]AuthSessionEvent, error)

ListSessionEvents returns a user's recent session events, most recent first (capped at listSessionEventsLimit). No eventTypes means all event types.

func (*Client) ListSubjectGroups

func (s *Client) ListSubjectGroups(ctx context.Context, subject authkit.Subject) ([]SubjectGroupMembership, error)

ListSubjectGroups returns every group membership a subject holds (the cross-persona discovery behind /me/groups).

func (*Client) ListUserSessions

func (s *Client) ListUserSessions(ctx context.Context, userID string) ([]Session, error)

ListUserSessions lists active sessions for a user and issuer.

func (*Client) ListUsersDeletedBefore

func (s *Client) ListUsersDeletedBefore(ctx context.Context, cutoff time.Time, limit int) ([]string, error)

ListUsersDeletedBefore returns user IDs for users soft-deleted before the cutoff. It is intended for retention/purge workflows in the host application.

func (*Client) LogPasswordChanged added in v0.98.0

func (s *Client) LogPasswordChanged(ctx context.Context, userID string, sessionID string, ip *string, ua *string)

LogPasswordChanged records a password change event for a user (best-effort).

func (*Client) LogPasswordRecovery added in v0.98.0

func (s *Client) LogPasswordRecovery(ctx context.Context, userID string, method, sessionID string, ip *string, ua *string)

LogPasswordRecovery records a password recovery event for a user (best-effort).

func (*Client) LogSessionCreated added in v0.98.0

func (s *Client) LogSessionCreated(ctx context.Context, userID string, method string, sessionID string, ip *string, ua *string)

LogSessionCreated records a session creation event (best-effort).

func (*Client) LogSessionFailed added in v0.98.0

func (s *Client) LogSessionFailed(ctx context.Context, userID string, sessionID string, reason *string, ip *string, ua *string)

LogSessionFailed records a failed session event for a user (best-effort).

func (*Client) MFAStatus added in v0.98.0

func (s *Client) MFAStatus(ctx context.Context, userID string) (MFAStatus, error)

func (*Client) MFAStatusWith added in v0.98.0

func (s *Client) MFAStatusWith(settings *TwoFactorSettings, settingsErr error) (MFAStatus, error)

MFAStatusWith derives MFAStatus from an ALREADY-loaded Get2FASettings result (and its lookup error) instead of re-reading 2FA settings here (#228), so a caller that already read them — e.g. GET /me, which threads one Get2FASettings through MFAStatus, the step-up methods, and the step-up 2FA options — does not recompute the read. Behaviour matches MFAStatus exactly: a "no 2FA row" lookup (pgx.ErrNoRows) is the empty/disabled status, any other error propagates.

func (*Client) MarkEmailVerified added in v0.98.0

func (s *Client) MarkEmailVerified(ctx context.Context, id string) error

MarkEmailVerified records that the user's email address is verified.

func (*Client) MarkSessionAuthenticated added in v0.98.0

func (s *Client) MarkSessionAuthenticated(ctx context.Context, userID, sessionID string) error

func (*Client) MarkSessionAuthenticatedWithMethods added in v0.98.0

func (s *Client) MarkSessionAuthenticatedWithMethods(ctx context.Context, userID, sessionID string, authMethods []string) error

MarkSessionAuthenticatedWithMethods refreshes the session's sensitive-action auth window and records how the user re-proved identity.

func (*Client) MayCreateInstance added in v0.98.0

func (s *Client) MayCreateInstance(ctx context.Context, group authkit.GroupRef, subject string) error

MayCreateInstance consults the host admission seam (#263). A nil predicate allows; a predicate error is wrapped as ErrGroupCreationRefused. The seam sees the normalized slug (#269) so a host can refuse a specific namespace outright, not merely price the attempt.

func (*Client) Mint2FAEnrollmentToken added in v0.98.0

func (s *Client) Mint2FAEnrollmentToken(ctx context.Context, userID string) (token string, expiresAt time.Time, err error)

func (*Client) MintAPIKeyWithOptions

func (s *Client) MintAPIKeyWithOptions(ctx context.Context, group authkit.GroupRef, opts APIKeyMintOptions) (APIKey, string, error)

MintAPIKeyWithOptions inserts a new API key. The key references exactly ONE role (opts.Role) valid for the owning group's persona; its effective permissions are resolved from the role at use time.

func (*Client) MintAccessToken added in v0.80.0

func (s *Client) MintAccessToken(ctx context.Context, userID string, extra map[string]any) (token string, expiresAt time.Time, err error)

MintAccessToken builds and signs an access token (JWT) for the given user. Includes core registered claims plus: - entitlements (authoritative short-lived snapshot) Extra claims in `extra` are merged into the token body (e.g., sid).

func (*Client) MintDelegatedAccessToken

func (s *Client) MintDelegatedAccessToken(ctx context.Context, p DelegatedAccessParams) (string, error)

MintDelegatedAccessToken signs a canonical delegated access token using the Client's internal signer. The host passes claims/params only and NEVER touches the private key. When p.Issuer is empty it defaults to the Client's configured Issuer. See the package-level MintDelegatedAccessToken for the claim contract.

func (*Client) MintRemoteApplicationAccessToken

func (s *Client) MintRemoteApplicationAccessToken(ctx context.Context, p RemoteApplicationAccessParams) (string, error)

MintRemoteApplicationAccessToken signs a remote application access token using the Client's internal signer. When p.Issuer is empty it defaults to the Client's configured Issuer.

func (*Client) MintServiceJWT

func (s *Client) MintServiceJWT(ctx context.Context, opts ServiceJWTMintOptions) (string, ServiceJWTClaims, error)

MintServiceJWT creates a short-lived signed service JWT from AuthKit's active signing key. It defaults to a 15-minute lifetime and stamps `token_use=service`; it does not grant host permissions by itself.

func (*Client) NamingPolicy added in v0.98.0

func (s *Client) NamingPolicy() authkit.NamingPolicy

NamingPolicy returns the normalized site policy for users and groups.

func (*Client) PasskeysEnabled added in v0.98.0

func (s *Client) PasskeysEnabled() bool

PasskeysEnabled reports whether passkey (WebAuthn) support is configured. Passkeys require a Relying Party ID (PasskeyConfig.RPID); without it every WebAuthn ceremony fails closed (the origin must match the RPID). The HTTP transport uses this to skip mounting the /passkeys/* routes entirely rather than exposing endpoints that can only error.

func (*Client) PasswordLogin added in v0.98.0

func (s *Client) PasswordLogin(ctx context.Context, in PasswordLoginInput) (LoginOutcome, error)

PasswordLogin runs the whole password-login decision tree. It returns an error only when the engine itself failed (a send, the challenge store, the session insert — each prefixed with its stage and, for sends, the delivery sentinel); every policy result is a LoginOutcome.

func (*Client) PatchUserMetadata

func (s *Client) PatchUserMetadata(ctx context.Context, userID string, patch map[string]any) error

PatchUserMetadata merges patch into a user's metadata.

func (*Client) PermissionGroupSchema added in v0.98.0

func (s *Client) PermissionGroupSchema() *GroupSchema

PermissionGroupSchema returns the validated schema this Client was built with (the intrinsic root-only schema if constructed without Config groups).

func (*Client) Postgres

func (s *Client) Postgres() *pgxpool.Pool

Postgres returns the attached pgx pool (may be nil).

func (*Client) ProviderSlugs added in v0.98.0

func (s *Client) ProviderSlugs(ctx context.Context, userID string) ([]string, error)

ProviderSlugs returns the distinct provider slugs linked to userID.

func (*Client) PublicKeysByKID

func (s *Client) PublicKeysByKID() map[string]crypto.PublicKey

PublicKeysByKID returns the CURRENT public keys indexed by key ID, read fresh from the KeySource on every call (#238).

func (*Client) PublicNativeUserRegistrationEnabled added in v0.98.0

func (s *Client) PublicNativeUserRegistrationEnabled() bool

PublicNativeUserRegistrationEnabled reports whether public native-user self-registration / auto-registration is allowed.

func (*Client) PublicUsersByIDs added in v0.92.0

func (s *Client) PublicUsersByIDs(ctx context.Context, ids []string) (map[string]authkit.PublicUserRef, error)

PublicUsersByIDs resolves many user IDs to the PUBLIC-safe display projection in ONE query (#268): the batch read for "render N comment authors" that is safe to nest directly into a response body, because authkit.PublicUserRef has no email field at all.

Deleted/banned policy, decided once here so three hosts stop each inventing it:

  • A SOFT-DELETED user resolves to a TOMBSTONE — present in the map with Deleted set and every display field blanked. Existing references (a comment, a gallery) therefore still render something stable (PublicUserRef.DisplayName → "user-<id8>") instead of silently disappearing, and a caller cannot accidentally publish a deleted account's old name.
  • A BANNED user is returned normally. A ban is an ACCESS decision (see UserLivenessByIDs), not a visibility one; suppressing bans here would retroactively rewrite public history and would leak moderation state to anyone who diffed the page.
  • An id that matches no row at all is ABSENT from the map, like UsersByIDs. authkit.PublicDisplayName covers that case for callers that want one branch-free lookup.

func (*Client) RBACDriftReport added in v0.72.0

func (s *Client) RBACDriftReport(ctx context.Context) (RBACDriftReport, error)

func (*Client) RecordFailedDeviceKeyEnrollment added in v0.98.0

func (s *Client) RecordFailedDeviceKeyEnrollment(ctx context.Context, enrollmentID string)

RecordFailedDeviceKeyEnrollment bounds online guessing without consuming a valid ceremony on one typo.

func (*Client) RecordFailedEmailVerifyCode added in v0.98.0

func (s *Client) RecordFailedEmailVerifyCode(ctx context.Context, email string)

RecordFailedEmailVerifyCode increments the per-email failed-attempt counter for the typed email-verification code. After maxEmailVerifyCodeAttempts failures it invalidates every outstanding code/pending-registration for that address so the short numeric code cannot be brute-forced within its TTL (AK security audit F1). No-op without an ephemeral store.

func (*Client) RecordFailedPasswordlessCode

func (s *Client) RecordFailedPasswordlessCode(ctx context.Context, identifier string)

func (*Client) RecordFailedPhoneVerifyCode added in v0.98.0

func (s *Client) RecordFailedPhoneVerifyCode(ctx context.Context, phone string)

RecordFailedPhoneVerifyCode is the phone twin of RecordFailedEmailVerifyCode: after maxPhoneVerifyCodeAttempts wrong guesses the outstanding code(s) for the number are invalidated. No-op without an ephemeral store.

func (s *Client) RedeemGroupInviteLink(ctx context.Context, code, redeemerUserID string) (RedeemGroupInviteLinkResult, error)

RedeemGroupInviteLink redeems code on behalf of the authenticated redeemerUserID: it validates the link (live, not expired/revoked, unredeemed), assigns the role in the same transaction, and stamps redeemed_at. Idempotent: if the redeemer already holds that role, it succeeds without consuming the link.

func (*Client) RedisKeyPrefix added in v0.98.0

func (s *Client) RedisKeyPrefix() string

RedisKeyPrefix is the namespace every Redis key of this deployment is written under (ephemeral store, OIDC/SIWS caches, rate-limit counters).

func (*Client) RegenerateBackupCodes added in v0.98.0

func (s *Client) RegenerateBackupCodes(ctx context.Context, userID string) ([]string, error)

RegenerateBackupCodes generates new backup codes for a user (invalidating old ones). Returns the plaintext codes (caller must show these to user ONCE).

func (*Client) Register added in v0.98.0

func (s *Client) Register(ctx context.Context, in RegisterInput) (RegisterOutcome, error)

Register runs the registration decision tree. Input problems come back as the validation errors (ValidationErrorCode) and the sentinels ErrInvalidIdentifier / ErrEmailInUse / ErrPhoneInUse / ErrUsernameInUse / ErrRegistrationDisabled / ErrEmailRegistrationUnavailable / ErrPhoneRegistrationUnavailable; engine failures carry a stage prefix and, for sends, the delivery sentinel.

func (*Client) RegisterApplicationFromDomain added in v0.88.0

func (s *Client) RegisterApplicationFromDomain(ctx context.Context, domain string) (*RegisteredApplication, error)

RegisterApplicationFromDomain is the create-or-reprove registration flow (#264): fetch + validate the domain's application.json (the domain-control proof), then atomically create the remote_application row + its SERVICE-OWNED org (the application principal is the org's owner) — or, when the slug already exists as a domain-rooted application, refresh its issuer/keys/config from the re-fetched document. Re-registration is the boot-time self-heal AND the rotation-from-root path: the old keypair may be gone entirely, the fresh domain proof adopts whatever the document declares now.

func (*Client) RegisterRemoteAppAttributeDef added in v0.98.0

func (s *Client) RegisterRemoteAppAttributeDef(ctx context.Context, appID, key string, version int32, definition json.RawMessage) (*RemoteAppAttributeDef, error)

RegisterRemoteAppAttributeDef stores (or updates) a definition for the remote_application. version defaults to 1 when zero. The caller authority is the remote_application itself (it owns its users' restrictions); the http layer enforces that.

func (*Client) RegistrationAllowedForEmailWithInvite added in v0.98.0

func (s *Client) RegistrationAllowedForEmailWithInvite(ctx context.Context, email, token string) (bool, error)

func (*Client) RegistrationVerificationEnabled added in v0.98.0

func (s *Client) RegistrationVerificationEnabled() bool

func (*Client) RegistrationVerificationPolicy added in v0.98.0

func (s *Client) RegistrationVerificationPolicy() RegistrationVerificationPolicy

RegistrationVerificationPolicy returns the effective registration verification policy ("none" when unset/invalid).

func (*Client) RegistrationVerificationRequired added in v0.98.0

func (s *Client) RegistrationVerificationRequired() bool

func (*Client) RemoveGroupSubjectAs

func (s *Client) RemoveGroupSubjectAs(ctx context.Context, actorUserID string, group authkit.GroupRef, subject authkit.Subject) error

RemoveGroupSubjectAs strips every role a subject holds in a group. It enforces the #136 capability + no-escalation rules for EVERY role the subject currently holds before stripping them, so a bounded admin cannot remove a member whose authority it does not itself hold (e.g. a non-owner cannot remove an owner).

func (*Client) RemoveRoleBySlug

func (s *Client) RemoveRoleBySlug(ctx context.Context, userID string, role authkit.Role) error

func (*Client) RemoveRoleBySlugAs

func (s *Client) RemoveRoleBySlugAs(ctx context.Context, actorUserID, userID string, role authkit.Role) error

RemoveRoleBySlugAs is the actor-aware root-group revoke.

func (*Client) RemoveRolesBySlugAs added in v0.80.0

func (s *Client) RemoveRolesBySlugAs(ctx context.Context, actorUserID string, userIDs []string, role authkit.Role) ([]authkit.OpResult, error)

func (*Client) RenamePasskey added in v0.98.0

func (s *Client) RenamePasskey(ctx context.Context, userID, id, label string) error

func (*Client) RequestEmailChange added in v0.98.0

func (s *Client) RequestEmailChange(ctx context.Context, userID, newEmail string) error

RequestEmailChange initiates an email change by sending a verification code to the new email. The current email is NOT changed until the user confirms via ConfirmEmailChange. The old address is not notified by AuthKit (only a security log line); a host that wants that notification sends it itself.

func (*Client) RequestEmailVerification added in v0.98.0

func (s *Client) RequestEmailVerification(ctx context.Context, email string, ttl time.Duration) error

RequestEmailVerification creates a verification code and dispatches an email.

func (*Client) RequestPasswordReset added in v0.98.0

func (s *Client) RequestPasswordReset(ctx context.Context, email string, ttl time.Duration, ip *string, ua *string) error

RequestPasswordReset creates a password reset token and dispatches a reset link via email. Returns nil for unknown emails to prevent user enumeration (202-like behavior).

func (*Client) RequestPhoneChange added in v0.98.0

func (s *Client) RequestPhoneChange(ctx context.Context, userID, newPhone string) error

RequestPhoneChange initiates a phone number change by sending a verification code to the new phone. The current phone is NOT changed until the user confirms via ConfirmPhoneChange.

func (*Client) RequestPhonePasswordReset added in v0.98.0

func (s *Client) RequestPhonePasswordReset(ctx context.Context, phone string, ttl time.Duration, ip *string, ua *string) error

RequestPhonePasswordReset creates a password reset token and sends a reset link via SMS. Always returns nil for unknown phone numbers to prevent user enumeration (202-like behavior).

func (*Client) RequestPhoneVerification added in v0.98.0

func (s *Client) RequestPhoneVerification(ctx context.Context, phone string, ttl time.Duration) error

RequestPhoneVerification looks up the user by phone number and sends a verification code. This mirrors the RequestEmailVerification pattern - caller only needs to provide the phone number.

func (*Client) Require2FAForLoginFactor added in v0.98.0

func (s *Client) Require2FAForLoginFactor(ctx context.Context, userID, factorID string) (destination, method string, factor TwoFactorFactor, err error)

func (*Client) Require2FAForStepUpMethod added in v0.98.0

func (s *Client) Require2FAForStepUpMethod(ctx context.Context, userID, sessionID, method string) (destination, selectedMethod string, factor TwoFactorFactor, err error)

func (*Client) ResolveAPIKey

func (s *Client) ResolveAPIKey(ctx context.Context, keyID, secret string) (groupRef string, permissions []string, err error)

ResolveAPIKey validates a presented API key (key_id + secret) and returns the owning permission-group id and the key's effective permissions resolved from its role at verify time (a role edit is reflected immediately — perms are never frozen into the key).

func (*Client) ResolveAPIKeyDetailed added in v0.72.0

func (s *Client) ResolveAPIKeyDetailed(ctx context.Context, keyID, secret string) (ResolvedAPIKey, error)

ResolveAPIKeyDetailed validates a presented API key and returns the full resolution result (id, key_id, owning group, role, and role-resolved permissions).

func (*Client) ResolveExternalIdentity added in v0.98.0

func (s *Client) ResolveExternalIdentity(ctx context.Context, in ExternalLoginInput) (userID string, created bool, err error)

ResolveExternalIdentity maps a verified provider identity to a local user without issuing a session: the explicit link target, the already-linked account, or a newly registered one (created reports the last case).

func (*Client) ResolveGroupIDForSlug

func (s *Client) ResolveGroupIDForSlug(ctx context.Context, group authkit.GroupRef) (string, error)

ResolveGroupIDForSlug maps the API addressing key (persona, instanceSlug) to the group's INTERNAL id, for IN-PROCESS callers that must thread the controlling permission_group_id into a sibling resource (e.g. a remote_application's permission_group_id, #111). ErrGroupNotFound if no live group matches. Out-of-process callers use GroupInstanceForSlug, which the HTTP descriptor route exposes under an authorization gate (#269).

func (*Client) ResolveGroupSlug added in v0.98.0

func (s *Client) ResolveGroupSlug(ctx context.Context, group authkit.GroupRef) (authkit.NameResolution, error)

func (*Client) ResolveRemoteAppAttributeDef

func (s *Client) ResolveRemoteAppAttributeDef(ctx context.Context, appID, key string, version int32) (*RemoteAppAttributeDef, error)

ResolveRemoteAppAttributeDef returns the definition for (appID, key, version). version <= 0 resolves the LATEST version. The returned Definition is opaque.

func (*Client) ResolveRemoteApplicationAuthority

func (s *Client) ResolveRemoteApplicationAuthority(ctx context.Context, appID string) (authkit.RemoteApplicationAuthority, error)

ResolveRemoteApplicationAuthority resolves a remote_application's effective permissions — the additive walk-up of every role it holds across its controlling permission-group's parent chain (#111) — plus the owning group instance the authority is bound to (#248). Permissions is an empty slice (no error) when the app holds no roles.

func (*Client) ResolveUsername added in v0.98.0

func (s *Client) ResolveUsername(ctx context.Context, name string) (authkit.NameResolution, error)

ResolveUsername resolves current names and unexpired aliases directly to UUID.

func (*Client) RevokeAPIKey

func (s *Client) RevokeAPIKey(ctx context.Context, group authkit.GroupRef, tokenID string) (bool, error)

RevokeAPIKey marks the API key revoked. It is scoped to the group so a token cannot be revoked from a different group. Returns false if no matching, not-already-revoked token exists.

func (*Client) RevokeAllSessions

func (s *Client) RevokeAllSessions(ctx context.Context, userID string, keepSessionID *string) error

func (*Client) RevokeDeviceKey added in v0.98.0

func (s *Client) RevokeDeviceKey(ctx context.Context, userID, currentID, targetID string) error

RevokeDeviceKey idempotently revokes one key owned by the caller. The token's own key is checked live in the same transaction first, so a revoked machine cannot use the remainder of its access-token lifetime to revoke a replacement machine.

func (s *Client) RevokeGroupInviteLink(ctx context.Context, group authkit.GroupRef, linkID string) error

RevokeGroupInviteLink revokes a link by id, scoped to the group addressed by (persona, instanceSlug) so a manager cannot revoke another group's link.

func (*Client) RevokeOtherDeviceKeys added in v0.98.0

func (s *Client) RevokeOtherDeviceKeys(ctx context.Context, userID, currentID string) error

RevokeOtherDeviceKeys atomically revokes every key except the live key that minted the caller's email-proven token.

func (*Client) RevokeSessionByIDForUser added in v0.98.0

func (s *Client) RevokeSessionByIDForUser(ctx context.Context, userID, sessionID string) error

RevokeSessionByIDForUser revokes a session by id ensuring it belongs to the user.

func (*Client) RoleSlugsByUsers added in v0.80.0

func (s *Client) RoleSlugsByUsers(ctx context.Context, userIDs []string) (map[string][]string, error)

RoleSlugsByUsers returns each user's LIVE configured root permission-group role slugs in ONE call (#220 — replaces ListRoleSlugsByUser and the error-propagating ListRoleSlugsByUserErr). The map is keyed by user id; users holding no live roles are absent. A failure resolving roles is RETURNED, not swallowed into an empty result, so authz callers can FAIL CLOSED instead of treating a backend outage as "no roles" (#136). A missing root group is genuinely empty (not an error). Root has no parent groups, so direct root-group assignments ARE the effective set. Roles that have drifted out of the configured catalog are excluded (splitConfiguredRootRoles), which is also the correct authz reading: an unconfigured role confers nothing.

func (*Client) SMSAvailable

func (s *Client) SMSAvailable() bool

SMSAvailable reports whether phone-based flows should be offered: a sender is configured and (if a health check has run) it was found able to deliver.

func (*Client) SMSHealthy added in v0.98.0

func (s *Client) SMSHealthy() bool

SMSHealthy reports the last CheckSMSHealth verdict; true until a check has run.

func (*Client) Schema

func (s *Client) Schema() string

Schema returns the Postgres schema AuthKit's tables live in ("profiles" unless configured otherwise via Config.Schema).

func (*Client) SeedPermissionGroupContainment

func (s *Client) SeedPermissionGroupContainment(ctx context.Context) error

SeedPermissionGroupContainment writes the declared containment schema into group_persona_parents so the DB trigger can enforce tree shape. Idempotent; call once at bootstrap.

func (*Client) SendPhone2FASetupCode added in v0.98.0

func (s *Client) SendPhone2FASetupCode(ctx context.Context, userID, phone, code string) error

SendPhone2FASetupCode generates and sends a 6-digit code for 2FA setup to the user's phone.

func (*Client) SendPhoneVerificationToUser added in v0.98.0

func (s *Client) SendPhoneVerificationToUser(ctx context.Context, phone, userID string, ttl time.Duration) error

SendPhoneVerificationToUser creates a verification code and sends it via SMS to a known user. Use RequestPhoneVerification if you only have a phone number and need to look up the user. Always returns nil for security.

func (*Client) SendWelcome added in v0.98.0

func (s *Client) SendWelcome(ctx context.Context, userID string)

SendWelcome triggers the welcome email if an EmailSender is configured.

func (*Client) SessionFreshness added in v0.98.0

func (s *Client) SessionFreshness(ctx context.Context, userID, sessionID string, now time.Time) (SessionFreshness, error)

func (*Client) SetDefault2FAFactor added in v0.98.0

func (s *Client) SetDefault2FAFactor(ctx context.Context, userID, factorID string) error

func (*Client) SetEntitlementsProvider

func (s *Client) SetEntitlementsProvider(p EntitlementsProvider)

SetEntitlementsProvider installs the entitlements provider AFTER construction.

This is the ONE sanctioned post-construction setter — #108 otherwise removed every mutating builder in favor of constructor options. It exists for a genuine initialization CYCLE: an embedded billing engine (e.g. OpenRails) authenticates through this Client — it needs the Verifier/Core, so the Client must exist first — yet that same engine is the SOURCE of the entitlements provider, so the provider cannot exist at construction time. The host builds the Client, builds the engine with it, then installs the engine's provider here. Safe because entitlements are read LAZILY at token-mint time; call it during wiring, before serving requests. Hosts WITHOUT this cycle should set Deps.Entitlements instead.

func (*Client) SetPasswordAfterFreshAuth added in v0.98.0

func (s *Client) SetPasswordAfterFreshAuth(ctx context.Context, userID, new string, keepSessionID *string) error

SetPasswordAfterFreshAuth sets a new password without verifying a current one, for flows that already proved freshness (e.g. step-up). It still revokes other sessions, keeping keepSessionID if provided.

func (*Client) SetPermissionGroupDisplayName added in v0.88.0

func (s *Client) SetPermissionGroupDisplayName(ctx context.Context, group authkit.GroupRef, displayName string) error

SetPermissionGroupDisplayName updates a group's free-form, non-unique display name (#264 naming doctrine: vanity naming lives here, renameable at will; the slug stays the unique handle). Callers gate authorization.

func (*Client) SetPreferredLanguage added in v0.98.0

func (s *Client) SetPreferredLanguage(ctx context.Context, userID, language string) error

func (*Client) SetProviderUsername added in v0.98.0

func (s *Client) SetProviderUsername(ctx context.Context, userID, provider, subject, username string) error

func (*Client) SignDocument added in v0.86.0

func (s *Client) SignDocument(ctx context.Context, envelope documents.Envelope) (documents.SignedDocument, error)

SignDocument signs through the Client's live key source, so normal AuthKit key rotation applies without exposing private key material to the host.

func (*Client) SoftDeleteUser

func (s *Client) SoftDeleteUser(ctx context.Context, id string) error

SoftDeleteUser marks the user deleted without dropping rows. Sessions and device keys are revoked in the same transaction.

func (*Client) SoftDeleteUserAs added in v0.98.0

func (s *Client) SoftDeleteUserAs(ctx context.Context, actorUserID, userID string) error

SoftDeleteUserAs is the actor-aware SoftDeleteUser.

func (*Client) SoftDeleteUsers added in v0.80.0

func (s *Client) SoftDeleteUsers(ctx context.Context, userIDs []string) ([]authkit.OpResult, error)

func (*Client) StartPasswordless

func (s *Client) StartPasswordless(ctx context.Context, req PasswordlessStartRequest) (PasswordlessStartResult, error)

func (*Client) StartTOTPEnrollment added in v0.98.0

func (s *Client) StartTOTPEnrollment(ctx context.Context, userID string) (secret, otpauthURI string, err error)

StartTOTPEnrollment creates a short-lived pending authenticator-app secret.

func (*Client) TwoFactorAllowedMethods added in v0.98.0

func (s *Client) TwoFactorAllowedMethods() []string

TwoFactorAllowedMethods is the set of currently-usable methods, in stable order. Empty when 2FA is disabled or no method's dependency is satisfied — what status and enrollment-required responses report to clients.

func (*Client) TwoFactorEnabled added in v0.98.0

func (s *Client) TwoFactorEnabled() bool

TwoFactorEnabled reports whether any 2FA flow is usable (Mode != Disabled).

func (*Client) TwoFactorMethodAvailable added in v0.98.0

func (s *Client) TwoFactorMethodAvailable(method string) bool

TwoFactorMethodAvailable reports whether a second-factor method can be enrolled/used right now: enabled by policy AND its delivery dependency present.

func (*Client) UnassignGroupRole added in v0.98.0

func (s *Client) UnassignGroupRole(ctx context.Context, group authkit.GroupRef, subject authkit.Subject, role authkit.Role) error

UnassignGroupRole revokes a subject's role in a group.

func (*Client) UnassignGroupRoleAs

func (s *Client) UnassignGroupRoleAs(ctx context.Context, actorUserID string, group authkit.GroupRef, subject authkit.Subject, role authkit.Role) error

UnassignGroupRoleAs is the actor-aware UnassignGroupRole. Revoking is gated the same way (you cannot strip a role whose authority you do not hold — e.g. a non-owner cannot remove an owner).

func (*Client) UnbanUser

func (s *Client) UnbanUser(ctx context.Context, userID string) error

UnbanUser clears ban metadata and re-enables the account.

func (*Client) UnlinkProviderUnlessLast added in v0.98.0

func (s *Client) UnlinkProviderUnlessLast(ctx context.Context, userID, provider string) (bool, error)

UnlinkProviderUnlessLast atomically removes the provider link only if the user retains a login method afterward (a password, or another provider). Returns (false, nil) when removal would strip the last login method. The check and the delete run in one transaction, and UserProviderCountForUpdate locks the user's provider rows so two concurrent unlinks of different providers cannot both pass the "not last" check and leave the user with zero login methods.

func (*Client) UpdateAvatarURL added in v0.90.0

func (s *Client) UpdateAvatarURL(ctx context.Context, id string, avatarURL *string) error

UpdateAvatarURL sets (nil clears) a user's avatar URL/key string (#262). Blob storage and content validation are host-owned; authkit stores the string verbatim (trimmed) and serves it on GET /me.

func (*Client) UpdateEmail

func (s *Client) UpdateEmail(ctx context.Context, id, email string) error

UpdateEmail updates a user's email and re-triggers email verification.

func (*Client) UpdateGroupInstanceAs added in v0.98.0

func (s *Client) UpdateGroupInstanceAs(ctx context.Context, actorUserID, groupID string, update authkit.GroupInstanceUpdate) (authkit.GroupInstance, error)

UpdateGroupInstanceAs applies settings to one captured UUID. It authorizes before even a no-op and never resolves a mutable spelling after authorization.

func (*Client) UpdateImportedUser

func (s *Client) UpdateImportedUser(ctx context.Context, userID string, input ImportUserInput) (*User, error)

func (*Client) UpdateUsername

func (s *Client) UpdateUsername(ctx context.Context, id, username string) error

UpdateUsername applies the deployment policy to an account rename.

func (*Client) UpsertPasswordHash

func (s *Client) UpsertPasswordHash(ctx context.Context, userID, hash, algo string, params []byte) error

UpsertPasswordHash stores a precomputed password hash for a user.

func (*Client) UpsertRemoteApplication

func (s *Client) UpsertRemoteApplication(ctx context.Context, in RemoteApplication) (*RemoteApplication, error)

UpsertRemoteApplication registers or updates a remote_application keyed by its issuer. An existing issuer can only be updated by its controlling group.

func (*Client) UpsertRoleBySlug

func (s *Client) UpsertRoleBySlug(ctx context.Context, name string, role authkit.Role, description *string) error

func (*Client) UserLivenessByIDs added in v0.92.0

func (s *Client) UserLivenessByIDs(ctx context.Context, ids []string) (map[string]authkit.UserLiveness, error)

UserLivenessByIDs resolves many user IDs to their account-liveness verdict plus fresh identity fields in ONE query (#267) — the read behind verify's per-request liveness gate, and the reason a host never needs an admin-privileged user lookup to refresh a display name onto a request.

The verdict is the SAME gate that guards token mint at login and refresh (ensureUserAccess): deleted, reserved and banned accounts are denied, and an expired temporary ban is cleared and allowed. Both paths evaluate livenessAllowed, so they cannot drift.

Errors PROPAGATE. Callers are authorization gates and must fail CLOSED on a lookup failure rather than read an outage as "allowed" — the same contract RoleSlugsByUsers carries. IDs that match no row are absent from the map, which a gate must also treat as a denial.

func (*Client) UserNamingState added in v0.98.0

func (s *Client) UserNamingState(ctx context.Context, id string) (authkit.NamingState, error)

func (*Client) UserProfile added in v0.98.0

func (s *Client) UserProfile(ctx context.Context, in ProfileInput) (authkit.UserProfile, error)

UserProfile builds the caller's profile. Errors: the user row is missing (stage "load_user"), or a store failure (stage "load_password" / "load_2fa").

func (s *Client) UserProfileLinks(ctx context.Context, userID string) (providerSlugs []string, aliases []string, err error)

UserProfileLinks returns the user's linked provider slugs (non-null) and username aliases — the two extra lists GET /me needs beyond AdminGetUser. Keeps raw db.Queries out of the HTTP layer, which previously built its own db handle inline.

func (*Client) UsersByIDs added in v0.66.0

func (s *Client) UsersByIDs(ctx context.Context, ids []string) (map[string]authkit.UserRef, error)

UsersByIDs resolves many user IDs to slim display projections (username/email) in ONE query — the batch read behind "resolve N authors for display" without N+1 single fetches. IDs that don't exist are simply absent from the result.

PRIVILEGED: the projection carries Email. For anything rendered to other users, call PublicUsersByIDs instead (#268).

This replaces the removed authkit/identity store's batch reads. Mutations are NOT exposed here on purpose: username/email writes go through UpdateUsername/ UpdateEmail, which enforce the rename cooldown + validation that raw table writes (the old identity.Store) silently skipped.

func (*Client) ValidateUsernameForRegistration added in v0.98.0

func (s *Client) ValidateUsernameForRegistration(ctx context.Context, username string) (string, error)

func (*Client) ValidateVerificationConfiguration

func (s *Client) ValidateVerificationConfiguration() error

ValidateVerificationConfiguration ensures registration verification policy can be satisfied by currently configured delivery senders.

func (*Client) Verify2FAChallenge added in v0.98.0

func (s *Client) Verify2FAChallenge(ctx context.Context, userID, challenge string) (bool, error)

Verify2FAChallenge verifies the challenge created during the password step.

func (*Client) Verify2FACode added in v0.98.0

func (s *Client) Verify2FACode(ctx context.Context, userID, code string) (bool, error)

Verify2FACode verifies a 2FA code entered by the user during login. Returns true if code is valid, false otherwise.

func (*Client) Verify2FAFactorCode added in v0.98.0

func (s *Client) Verify2FAFactorCode(ctx context.Context, userID, factorID, code string) (bool, error)

func (*Client) Verify2FAStepUpMethodCode added in v0.98.0

func (s *Client) Verify2FAStepUpMethodCode(ctx context.Context, userID, sessionID, method, code string) (bool, error)

func (*Client) VerifyBackupCode added in v0.98.0

func (s *Client) VerifyBackupCode(ctx context.Context, userID, backupCode string) (bool, error)

VerifyBackupCode verifies a 2FA backup code for account recovery. On success, removes the used backup code from the user's backup codes.

func (*Client) VerifyPendingPassword added in v0.98.0

func (s *Client) VerifyPendingPassword(ctx context.Context, email, pass string) bool

VerifyPendingPassword checks if the provided password matches the pending registration's hash. Returns true if password is correct, false otherwise.

func (*Client) VerifyPendingPhonePassword added in v0.98.0

func (s *Client) VerifyPendingPhonePassword(ctx context.Context, phone, pass string) bool

VerifyPendingPhonePassword checks if the provided password matches the pending phone registration's hash. Returns true if password is correct, false otherwise.

func (*Client) VerifyPhone2FASetupCode added in v0.98.0

func (s *Client) VerifyPhone2FASetupCode(ctx context.Context, userID, phone, code string) (bool, error)

VerifyPhone2FASetupCode checks the code for 2FA phone setup.

func (*Client) VerifySIWSAndLogin added in v0.98.0

func (s *Client) VerifySIWSAndLogin(ctx context.Context, cache siws.ChallengeCache, output siws.SignInOutput, extra map[string]any) (accessToken string, expiresAt time.Time, refreshToken, userID string, created bool, err error)

VerifySIWSAndLogin verifies a SIWS signature and logs in or creates a user. Returns access token, expiry, refresh token, user ID, and whether a new user was created.

type Config

type Config struct {
	// 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
	// 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 that set a non-default schema
	// must also run the migrations rendered for that schema — see
	// migrations/postgres.FSForSchema.
	Schema string

	// SolanaNetwork is the SIWS chain selector ("mainnet"/"testnet"/"devnet").
	// Empty defaults to mainnet. Solana Name Client (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 Client. 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 {
	// 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 {
	// 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 *redis.Client
	// EphemeralStore is a host-supplied store; mutually exclusive with Redis.
	EphemeralStore EphemeralStore
	Email          EmailSender
	SMS            SMSSender
	Entitlements   EntitlementsProvider
	// 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 Client 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 — they are baked verbatim into the `entitlements` claim of access tokens and surfaced on admin user views. Providers should 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)
	// 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 ExternalLoginInput added in v0.98.0

type ExternalLoginInput struct {
	Identity ExternalIdentity
	// LinkUserID, when set, is the authenticated user explicitly linking this
	// identity (never a registration path).
	LinkUserID         string
	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 ExternalLoginOutcome added in v0.98.0

type ExternalLoginOutcome struct {
	Kind    ExternalLoginOutcomeKind
	UserID  string
	Created bool
	Session *IssuedSession
}

ExternalLoginOutcome reports the resolved user, whether it was just created, and the session (for ExternalSessionIssued).

type ExternalLoginOutcomeKind added in v0.98.0

type ExternalLoginOutcomeKind string

ExternalLoginOutcomeKind is the closed set of ways an external login ends.

const (
	ExternalSessionIssued           ExternalLoginOutcomeKind = "session_issued"
	ExternalTwoFAEnrollmentRequired ExternalLoginOutcomeKind = "2fa_enrollment_required"
)

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 GenesisClient added in v0.79.0

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

GenesisClient is the explicitly-dangerous bootstrap/migration seam (#241): its mutators run with NO actor check and NO no-escalation enforcement — the opposite of the actor-checked `*As` methods on the Client itself (AssignRoleBySlugAs / AssignGroupRoleAs / RemoveGroupSubjectAs). One mistaken call here can hand out root:*.

Genesis skips ACTOR checks only. The MFA-required-role enrollment gate (#148/root-owner-MFA) is a subject-state invariant — "no user holds an MFA-required role without enrolled 2FA" — and still applies here: assigning such a role to a non-enrolled user fails closed with ErrTwoFAEnrollmentRequired. Enroll the user first, or run with TwoFactor.Mode: Disabled (the gate is inert then). The bootstrap manifest is the one seam that bypasses it (a manifest-seeded user has no session to have enrolled with).

Reach it via Client.Genesis(). Use it ONLY for one-time bootstrap/seed/ migration code that runs before any actor-authorized request path exists (e.g. provisioning the first owner of a fresh install) — never from a runtime request handler, where the corresponding `*As` method belongs.

func (GenesisClient) AssignGroupRole added in v0.79.0

func (g GenesisClient) AssignGroupRole(ctx context.Context, group authkit.GroupRef, subject authkit.Subject, role authkit.Role) error

AssignGroupRole grants a role to a subject in a permission group with NO actor check and NO no-escalation enforcement. The MFA-required-role enrollment gate still applies (see GenesisClient). Bootstrap/migration only. Runtime callers use AssignGroupRoleAs.

func (GenesisClient) AssignRemoteApplicationRole added in v0.98.0

func (g GenesisClient) AssignRemoteApplicationRole(ctx context.Context, appID string, role authkit.Role) error

AssignRemoteApplicationRole grants a remote application (by id) a role in its controlling group with NO actor check and NO no-escalation enforcement (#308). Bootstrap/migration only — see GenesisClient. Runtime callers use AssignRemoteApplicationRoleAs.

func (GenesisClient) AssignRoleBySlug added in v0.79.0

func (g GenesisClient) AssignRoleBySlug(ctx context.Context, userID string, role authkit.Role) error

AssignRoleBySlug grants userID the named root-persona role with NO actor check and NO no-escalation enforcement. The MFA-required-role enrollment gate still applies (see GenesisClient). Bootstrap/migration only. Runtime callers use AssignRoleBySlugAs.

func (GenesisClient) RemoveRoleBySlug added in v0.79.0

func (g GenesisClient) RemoveRoleBySlug(ctx context.Context, userID string, role authkit.Role) error

RemoveRoleBySlug revokes userID's named root-persona role with NO actor check and NO no-escalation enforcement. Bootstrap/migration only — see GenesisClient. Runtime callers use RemoveRoleBySlugAs.

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 profiles. Construction does not query, migrate, write or start workers. Hosts remain responsible for authorizing any subsequent action.

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 profiles. 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 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 Client 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 LoginOutcome added in v0.98.0

type LoginOutcome struct {
	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 (
	// 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"
)

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 MFAStatus added in v0.98.0

type MFAStatus = authkit.MFAStatus

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 PasskeyLoginResult

type PasskeyLoginResult struct {
	UserID       string
	SessionID    string
	RefreshToken string
	AccessToken  string
	ExpiresAt    time.Time
}

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 PasswordlessConfirmResult added in v0.98.0

type PasswordlessConfirmResult = authkit.PasswordlessConfirmResult

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 (
	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 grants subject a role in a group, REPLACING any previous role it held there (#247 hard rule: one role per subject per group, enforced by a partial unique index on (permission_group_id, subject) — no per-group role unions). A single atomic UPSERT: no live row yet -> insert fresh; a live row already exists (whatever role it holds) -> its role is overwritten in place. Deliberately NOT a soft-delete-then-insert (a two-statement version of that has a same-snapshot visibility trap: a writable CTE's UPDATE is invisible to the following INSERT's ON CONFLICT check in the SAME command, so the INSERT would spuriously conflict with the row the CTE just tried to retire and silently no-op instead of swapping the role). The role NAME is validated against the persona catalog / custom roles by the caller before assignment.

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 removes a per-group custom role (and its permissions).

func (*PermissionGroupStore) DeleteGroup added in v0.98.0

DeleteGroup reserves the final canonical name forever unless explicitly released. Existing aliases keep the promises made by their individual renames.

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 returns the de-duplicated UNION of grant PATTERNS the subject holds in the group addressed by groupID (its assignments across the parent chain), resolved against the schema's catalog + per-group custom roles. Unlike CanOnGroup (which tests ONE perm), this returns the whole effective grant set as PATTERNS — globs like `root:*` are returned verbatim, NOT expanded into every concrete perm (the caller glob-matches with authkit.Perm.Matches). Powers the permission-introspection endpoint (authkit/doujins #421). An empty assignment set returns an empty (non-nil) slice.

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 reads one group's own identity row (#269) — the descriptor behind GET /<persona>/:instance_slug and the `group_id` on the creation response. Takes an id the caller already resolved from (persona, slug), so tombstone forwarding and the root singleton are handled once, upstream.

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 how many subjects (users + remote applications) currently hold the owner role in a group — the last-owner guard (#193) refuses to remove the final owner so a group can never be orphaned.

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 soft-deletes a role assignment.

func (*PermissionGroupStore) UnassignSubject added in v0.98.0

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

UnassignSubject soft-deletes every active role assignment a subject holds in a 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 {
	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 (
	// 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 RemoteAppAttributeDef added in v0.98.0

type RemoteAppAttributeDef = authkit.RemoteAppAttributeDef

RemoteAppAttributeDef is one REFERENCE-mode attribute definition (#75): a remote_application registers (key, version) -> definition, and a platform resolves a token's `attributes.<key>: "<ref>"` reference back to it. The Definition is an OPAQUE JSON doc — AuthKit stores and serves it but NEVER interprets its semantics (same agnosticism as the token attributes bag). RemoteAppAttributeDef is defined in authkit (core-free) and re-exported here.

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 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 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"
)

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 {
	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
}

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 TwoFAEnrollmentRequiredError added in v0.98.0

type TwoFAEnrollmentRequiredError struct{ UserID string }

TwoFAEnrollmentRequiredError wraps ErrTwoFAEnrollmentRequired with the userID of the gated account, so the refresh-token path can mint a usable enrollment token instead of stranding the user (#148, grounding note b — a 403 with no token at refresh is a lockout). errors.Is(err, ErrTwoFAEnrollmentRequired) still matches.

func (*TwoFAEnrollmentRequiredError) Error added in v0.98.0

func (*TwoFAEnrollmentRequiredError) Unwrap added in v0.98.0

func (e *TwoFAEnrollmentRequiredError) Unwrap() error

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. 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 {
	UserID      string
	Mode        FactorEnrollmentMode
	Method      string // "email" | "sms" | "totp"; empty with FactorID+MakeDefault re-points the default
	Code        string // SMS setup code / 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"    // 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 {
	Kind        TwoFactorEnrollKind
	Method      string
	Secret      string
	OTPAuthURI  string
	BackupCodes []string
}

TwoFactorEnrollOutcome carries the TOTP material for TwoFactorEnrollTOTPStarted and the plaintext backup codes (shown once) for TwoFactorEnrollEnabled.

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 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
}

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