Documentation
¶
Overview ¶
Package authkit holds authentication primitives shared between authkit's issuing core and its verification layer: plain data types, opaque-credential parsing, and sentinel errors that carry NO dependency on Postgres or the rest of core (stdlib only). It exists so the verification path — and, later, a standalone verify module (agents #110/#107) — can depend on these without pulling in the storage layer. The core package re-exports every symbol here as an alias, so existing callers using core.X are unaffected.
Index ¶
- Constants
- Variables
- func APIKeyMarker(prefix string) string
- func ErrorForCode(code string) error
- func ErrorMessage(code string) string
- func ErrorTypeForStatus(status int) string
- func FormatAPIKey(prefix, keyID, secret string) string
- func HasAPIKeyPrefix(prefix, token string) bool
- func ParseAPIKey(prefix, token string) (keyID, secret string, ok bool)
- func PermMatches(grant, concrete string) bool
- func PermissionTokenCovers(grant, requested string) bool
- type APIKey
- type APIKeyMintOptions
- type APIKeys
- type AccountRegistrationInvite
- type AccountRegistrationInviteCreated
- type Admin
- type AdminListUsersResult
- type AdminUser
- type AdminUserListOptions
- type AdminUserSort
- type AdminUserStatus
- type AuthCapabilities
- type AuthPasskeyCapabilities
- type AuthPasswordCapabilities
- type AuthPasswordlessCapabilities
- type AuthProviderSummary
- type AuthRegistrationCapabilities
- type AuthSolanaCapabilities
- type AuthVerificationCapabilities
- type Authorizer
- type Bootstrap
- type BootstrapManifest
- type BootstrapManifestGroupRole
- type BootstrapManifestRemoteApplication
- type BootstrapManifestResult
- type BootstrapManifestUser
- type BootstrapReconcileOptions
- type BootstrapUserPassword
- type Client
- type CreateAccountRegistrationInviteRequest
- type CreateGroupInviteLinkRequest
- type CreatePermissionGroupRequest
- type CustomJWTMintOptions
- type DelegatedAccessParams
- type Entitlements
- type ErrorEnvelope
- type ErrorObject
- type GroupInviteLink
- type GroupInviteLinkCreated
- type GroupMember
- type Groups
- type ImportUserInput
- type ImportUserResult
- type ImportUserStatus
- type ImportUsersResult
- type MFAStatus
- type Maintenance
- type Passwordless
- type PasswordlessConfirmResult
- type PasswordlessStartRequest
- type PasswordlessStartResult
- type Passwords
- type PersonaCapabilities
- type PreferredLanguage
- type Principal
- type PrincipalKind
- type Providers
- type RBACDriftReport
- type RedeemGroupInviteLinkResult
- type RegistrationMode
- type RegistrationVerificationPolicy
- type RemoteAppAttributeDef
- type RemoteAppKey
- type RemoteApplication
- type RemoteApplicationAccessParams
- type RemoteApps
- type ResolvedAPIKey
- type Roles
- type Senders
- type ServiceJWTClaims
- type ServiceJWTMintOptions
- type Session
- type Sessions
- type SubjectGroupMembership
- type Tokens
- type TwoFactorMethod
- type TwoFactorMode
- type User
- type UserRef
- type Users
Constants ¶
const ( ImportStatusInserted ImportUserStatus = "inserted" ImportStatusSkipped ImportUserStatus = "skipped" ImportStatusRejected ImportUserStatus = "rejected" AdminUserStatusActive AdminUserStatus = "active" // not deleted, not banned AdminUserStatusBanned AdminUserStatus = "banned" // not deleted, currently banned AdminUserStatusDeleted AdminUserStatus = "deleted" // soft-deleted AdminUserStatusAny AdminUserStatus = "any" // no deleted/banned predicate AdminUserSortCreatedAt AdminUserSort = "created_at" // default AdminUserSortLastLogin AdminUserSort = "last_login" AdminUserSortUsername AdminUserSort = "username" AdminUserSortEmail AdminUserSort = "email" )
const ( ErrorTypeInvalidRequest = "invalid_request_error" ErrorTypeAuthentication = "authentication_error" ErrorTypeAuthorization = "authorization_error" ErrorTypeRateLimit = "rate_limit_error" ErrorTypeAPI = "api_error" )
Error type categories, aligned with openrails' / Stripe's taxonomy strings.
const ( RemoteAppModeJWKS = "jwks" RemoteAppModeStatic = "static" )
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.
const ( // ServiceJWTTokenUse is the required `token_use` claim for service JWTs. ServiceJWTTokenUse = "service" // DefaultServiceJWTLifetime is the recommended lifetime for first-party // machine-to-machine service JWTs. DefaultServiceJWTLifetime = 15 * time.Minute )
const PermWildcard = "*"
PermWildcard is the wildcard CHARACTER used inside namespace-anchored globs (`org:*`, `org:members:*`, `org:*:read`, `root:*`). A bare standalone `*` is NOT a valid grant — it is rejected everywhere.
Variables ¶
var ( // ErrInvalidAccessToken indicates an API key that does not exist, has a bad // secret, or whose owning permission group is gone. Deliberately indistinguishable from // a malformed token so callers learn nothing from the error. ErrInvalidAccessToken = errors.New("invalid_token") // ErrAccessTokenRevoked indicates the API key was explicitly revoked. ErrAccessTokenRevoked = errors.New("token_revoked") // ErrAccessTokenExpired indicates the API key is past its expires_at. ErrAccessTokenExpired = errors.New("token_expired") )
var ( ErrBootstrapDatabaseNotEmpty = errors.New("bootstrap_database_not_empty") ErrCannotRemoveLastAdminRole = errors.New("cannot_remove_last_admin_role") ErrAccountRegistrationInviteConsumed = errors.New("account_registration_invite_consumed") ErrAccountRegistrationInviteEmailMismatch = errors.New("account_registration_invite_email_mismatch") ErrAccountRegistrationInviteExpired = errors.New("account_registration_invite_expired") ErrAccountRegistrationInviteNotFound = errors.New("account_registration_invite_not_found") ErrAccountRegistrationInviteRevoked = errors.New("account_registration_invite_revoked") ErrCustomClaimsReserved = errors.New("custom_jwt_reserved_claim") ErrCustomJWTReservedType = errors.New("custom_jwt_reserved_type") ErrEmailAlreadyVerified = errors.New("email_already_verified") ErrEmailDeliveryFailed = errors.New("email_delivery_failed") ErrEmailInUse = errors.New("email_in_use") ErrEmptyCustomClaims = errors.New("custom_jwt_empty_claims") ErrExternalInvitesDisabled = errors.New("external_invites_disabled") ErrGroupNotFound = errors.New("permission_group_not_found") ErrInsufficientRoleAuthority = errors.New("insufficient_role_authority") ErrInvalidAttributeDef = errors.New("invalid_attribute_def") ErrInvalidBootstrapManifest = errors.New("invalid_bootstrap_manifest") ErrInvalidUntil = errors.New("invalid_until") ErrInviteEmailMismatch = errors.New("group_invite_email_mismatch") ErrInviteLinkExhausted = errors.New("group_invite_link_exhausted") ErrInviteLinkExpired = errors.New("group_invite_link_expired") ErrInviteLinkNotFound = errors.New("group_invite_link_not_found") ErrInviteLinkRevoked = errors.New("group_invite_link_revoked") ErrMissingSigner = errors.New("missing_signer") ErrNotGroupMember = errors.New("not_group_member") ErrOwnerSlugTaken = errors.New("owner_slug_taken") ErrPasskeyCloneDetected = errors.New("passkey_clone_detected") ErrPasskeyNotFound = errors.New("passkey_not_found") ErrPasskeyUserVerificationRequired = errors.New("passkey_user_verification_required") ErrPasswordlessDisabled = errors.New("passwordless_disabled") ErrPasswordResetRequired = errors.New("password_reset_required") ErrPendingRegistrationNotFound = errors.New("pending_registration_not_found") ErrPhoneAlreadyVerified = errors.New("phone_already_verified") ErrPhoneInUse = errors.New("phone_in_use") ErrRegistrationDisabled = errors.New("registration_disabled") ErrRemoteApplicationNotFound = errors.New("remote_application_not_found") ErrRenameRateLimited = errors.New("rename_rate_limited") ErrReservedIssuer = errors.New("reserved_issuer") ErrResourceScopeDenied = errors.New("resource_scope_denied") ErrRoleAssignmentEscalation = errors.New("role_assignment_escalation") ErrSMSDeliveryFailed = errors.New("sms_delivery_failed") ErrStepUpRequired = errors.New("step_up_required") ErrTooManyCustomClaims = errors.New("custom_jwt_too_many_claims") ErrTwoFAEnrollmentRequired = errors.New("2fa_enrollment_required") ErrUserBanned = errors.New("user_banned") ErrUserNotFound = errors.New("user_not_found") ErrUserRoleNotFound = errors.New("user_role_not_found") ErrVerificationLinkExpired = errors.New("verification_link_expired") ErrSIWSAddressMismatch = errors.New("siws_address_mismatch") ErrSIWSChallengeExpired = errors.New("siws_challenge_expired") ErrSIWSChallengeNotFound = errors.New("siws_challenge_not_found") ErrSIWSDomainInvalid = errors.New("siws_domain_invalid") ErrSIWSSignatureInvalid = errors.New("siws_signature_invalid") ErrSIWSTimestampInvalid = errors.New("siws_timestamp_invalid") ErrWalletAlreadyLinked = errors.New("wallet_already_linked") ErrProviderAlreadyLinked = errors.New("provider_already_linked") )
Sentinel errors — the wire-contract error identities shared by the embedded engine and (Phase 2) the remote SDK so errors.Is works across transports (#138 contract inversion). internal/authcore aliases these.
var ErrAttributeDefNotFound = errors.New("attribute_def_not_found")
ErrAttributeDefNotFound indicates no registered remote-application attribute definition matched.
var ErrInvalidRemoteApplication = errors.New("invalid_remote_application")
ErrInvalidRemoteApplication indicates a malformed remote_application registration payload.
var ErrInvalidServiceJWT = errors.New("invalid_service_jwt")
ErrInvalidServiceJWT indicates a presented service JWT failed verification.
Functions ¶
func APIKeyMarker ¶
APIKeyMarker returns the leading marker that identifies an API key for the given application prefix: "<prefix>_st_" when prefix is non-empty, else "st_".
func ErrorForCode ¶ added in v0.68.0
ErrorForCode maps a wire error code (a sentinel's Error() string) back to the sentinel, so a remote client re-derives errors.Is(err, authkit.ErrX) identity across the network. Unknown/empty codes return nil — the caller supplies its own fallback. The server emits err.Error() as the code; remote/ resolves it here, so the wire-error contract has ONE source of truth (#142).
func ErrorMessage ¶
ErrorMessage returns a human-readable English message for a wire error code: a curated message for common codes, otherwise a humanized form of the code so the message is never empty. Localized catalogs are a future extension.
func ErrorTypeForStatus ¶
ErrorTypeForStatus maps an HTTP status to its error-type category (the same inference openrails performs).
func FormatAPIKey ¶
FormatAPIKey assembles the full presented token: <marker><key_id>_<secret>.
func HasAPIKeyPrefix ¶
HasAPIKeyPrefix reports whether token carries the API-key marker for prefix. Used by middleware to route to the API-key path before attempting JWT verification.
func ParseAPIKey ¶
ParseAPIKey splits a presented token into its key_id and secret. key_id and secret are base62 (no underscores), so the first "_" after the marker is the unambiguous delimiter. ok is false if the token lacks the marker or either part is empty.
func PermMatches ¶
PermMatches reports whether a GRANT token authorizes a CONCRETE permission. The grant may be a literal (`org:members:read`) or a namespace-anchored glob where `*` wildcards a whole segment (`org:members:*`, `org:*:read`, `org:*`). The namespace (segment 0) must be a literal — a bare `*` (or a `*` namespace) never matches. A two-segment glob `ns:*` matches every concrete `ns:…` perm.
This is the shared, authz-critical matcher used by both core's RBAC checks and the verification layer's permission-coverage checks.
func PermissionTokenCovers ¶
PermissionTokenCovers reports whether a stored grant token covers a requested permission token using AuthKit's namespace-anchored glob semantics.
Types ¶
type APIKeyMintOptions ¶
type APIKeys ¶ added in v0.72.0
type APIKeys interface {
MintAPIKey(ctx context.Context, persona, instanceSlug, name, role, createdBy string, expiresAt *time.Time) (APIKey, string, error)
MintAPIKeyWithOptions(ctx context.Context, persona, instanceSlug string, opts APIKeyMintOptions) (APIKey, string, error)
ListAPIKeys(ctx context.Context, persona, instanceSlug string) ([]APIKey, error)
RevokeAPIKey(ctx context.Context, persona, instanceSlug, tokenID string) (bool, error)
ResolveAPIKey(ctx context.Context, keyID, secret string) (string, []string, error)
ResolveAPIKeyDetailed(ctx context.Context, keyID, secret string) (ResolvedAPIKey, error)
}
APIKeys mints, lists, revokes, and resolves opaque API keys.
type AccountRegistrationInvite ¶ added in v0.72.0
type AccountRegistrationInviteCreated ¶ added in v0.72.0
type Admin ¶ added in v0.72.0
type Admin interface {
AdminCountUsers(ctx context.Context, opts AdminUserListOptions) (int64, error)
AdminGetUser(ctx context.Context, id string) (*AdminUser, error)
AdminListUserSessions(ctx context.Context, userID string) ([]Session, error)
AdminListUsers(ctx context.Context, opts AdminUserListOptions) (*AdminListUsersResult, error)
AdminRevokeUserSessions(ctx context.Context, userID string) error
AdminSetPassword(ctx context.Context, userID, new string) error
BanUser(ctx context.Context, userID string, reason *string, until *time.Time, bannedBy string) error
UnbanUser(ctx context.Context, userID string) error
}
Admin is the intrinsic admin view of the user directory: list, inspect, ban, and admin-side session/password control.
type AdminListUsersResult ¶
type AdminUser ¶
type AdminUser struct {
ID string `json:"id"`
Email *string `json:"email"` // Nullable for phone-only users
PhoneNumber *string `json:"phone_number"`
Username *string `json:"username"`
DiscordUsername *string `json:"discord_username"`
EmailVerified bool `json:"email_verified"`
PhoneVerified bool `json:"phone_verified"`
BannedAt *time.Time `json:"banned_at,omitempty"`
BannedUntil *time.Time `json:"banned_until,omitempty"`
BanReason *string `json:"ban_reason,omitempty"`
BannedBy *string `json:"banned_by,omitempty"`
DeletedAt *time.Time `json:"deleted_at"`
Biography *string `json:"biography"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
LastLogin *time.Time `json:"last_login"`
Roles []string `json:"roles"`
RemovedRoles []string `json:"removed_roles,omitempty"`
Entitlements []string `json:"entitlements"`
}
type AdminUserListOptions ¶
type AdminUserListOptions struct {
Page int
PageSize int
Search string // ILIKE over username/email/phone_number
Role string // root_role slug (e.g. "admin"); empty = no role filter
Status AdminUserStatus // empty = non-deleted (historical default)
Sort AdminUserSort // empty = created_at
Desc bool // true = descending
Entitlement string // empty = no entitlement filter; else provider-backed
}
type AdminUserSort ¶
type AdminUserSort string
type AdminUserStatus ¶
type AdminUserStatus string
type AuthCapabilities ¶ added in v0.72.0
type AuthCapabilities struct {
Registration AuthRegistrationCapabilities `json:"registration"`
Providers []AuthProviderSummary `json:"providers"`
Password AuthPasswordCapabilities `json:"password"`
Passwordless AuthPasswordlessCapabilities `json:"passwordless"`
Passkeys AuthPasskeyCapabilities `json:"passkeys"`
Solana AuthSolanaCapabilities `json:"solana"`
Verification AuthVerificationCapabilities `json:"verification"`
Languages []string `json:"languages,omitempty"`
}
AuthCapabilities is the public, static auth feature-discovery response.
type AuthPasskeyCapabilities ¶ added in v0.72.0
type AuthPasskeyCapabilities struct {
Login bool `json:"login"`
}
type AuthPasswordCapabilities ¶ added in v0.72.0
type AuthPasswordCapabilities struct {
Login bool `json:"login"`
}
type AuthPasswordlessCapabilities ¶ added in v0.72.0
type AuthProviderSummary ¶ added in v0.72.0
type AuthRegistrationCapabilities ¶ added in v0.72.0
type AuthSolanaCapabilities ¶ added in v0.72.0
type AuthSolanaCapabilities struct {
Login bool `json:"login"`
}
type AuthVerificationCapabilities ¶ added in v0.72.0
type AuthVerificationCapabilities struct {
Registration string `json:"registration"`
}
type Authorizer ¶ added in v0.67.0
type Authorizer interface {
Can(ctx context.Context, subjectID, subjectKind, persona, instanceSlug, perm string) (bool, error)
ListEffectivePermissions(ctx context.Context, subjectID, subjectKind, persona, instanceSlug string) ([]string, error)
IsUserAllowed(ctx context.Context, userID string) (bool, error)
ListRoleSlugsByUserErr(ctx context.Context, userID string) ([]string, error)
}
Authorizer is a cross-cutting consumer slice (#143): the "can this subject do X here" methods. Unlike the per-topic interfaces in client.go, it spans three of them (Groups for permission checks, Users for the live-user/ban gate, Roles for role resolution), so it is defined here as its own narrow view rather than mapping to one. doujins's request gate depends on it.
Add a cross-cutting slice like this only when a real consumer signature needs one; the per-topic interfaces (Users, Tokens, Groups, ...) cover the rest.
type Bootstrap ¶ added in v0.72.0
type Bootstrap interface {
// ApplyBootstrapManifest applies a parsed manifest. There is deliberately no
// ApplyBootstrapManifestFile on the contract: a file path is the SERVER's
// filesystem, meaningless over a remote transport (#142). Hosts with a file
// load it themselves (e.g. embedded.LoadBootstrapManifestFile) then call this.
ApplyBootstrapManifest(ctx context.Context, manifest BootstrapManifest, opts BootstrapReconcileOptions) (BootstrapManifestResult, error)
}
Bootstrap applies a parsed bootstrap manifest (operator/deploy seeding).
type BootstrapManifest ¶
type BootstrapManifest struct {
Users []BootstrapManifestUser `json:"users" yaml:"users"`
RemoteApplications []BootstrapManifestRemoteApplication `json:"remote_applications" yaml:"remote_applications"`
GroupRoles []BootstrapManifestGroupRole `json:"group_roles" yaml:"group_roles"`
}
type BootstrapManifestGroupRole ¶
type BootstrapManifestGroupRole struct {
Username string `json:"username" yaml:"username"`
RemoteApplicationSlug string `json:"remote_application_slug" yaml:"remote_application_slug"`
Persona string `json:"persona" yaml:"persona"`
InstanceSlug string `json:"instance_slug" yaml:"instance_slug"`
Role string `json:"role" yaml:"role"`
}
type BootstrapManifestRemoteApplication ¶
type BootstrapManifestRemoteApplication struct {
Slug string `json:"slug" yaml:"slug"`
Issuer string `json:"issuer" yaml:"issuer"`
JWKSURI string `json:"jwks_uri" yaml:"jwks_uri"`
PublicKeys []RemoteAppKey `json:"public_keys" yaml:"public_keys"`
Enabled *bool `json:"enabled" yaml:"enabled"`
RootRole string `json:"root_role" yaml:"root_role"`
}
type BootstrapManifestResult ¶
type BootstrapManifestResult struct {
DryRun bool `json:"dry_run"`
AlreadyApplied bool `json:"already_applied"`
UsersCreated int `json:"users_created"`
UsersUpdated int `json:"users_updated"`
PasswordsSet int `json:"passwords_set"`
PasswordsKept int `json:"passwords_kept"`
RootRoleAssignments int `json:"root_role_assignments"`
GroupRoleAssignments int `json:"group_role_assignments"`
RemoteApplications int `json:"remote_applications"`
RemoteAppRootRoles int `json:"remote_application_root_roles"`
}
type BootstrapManifestUser ¶
type BootstrapManifestUser struct {
Email string `json:"email" yaml:"email"`
PhoneNumber string `json:"phone_number" yaml:"phone_number"`
Username string `json:"username" yaml:"username"`
EmailVerified bool `json:"email_verified" yaml:"email_verified"`
PhoneVerified bool `json:"phone_verified" yaml:"phone_verified"`
Banned bool `json:"banned" yaml:"banned"`
BannedAt *time.Time `json:"banned_at" yaml:"banned_at"`
BannedUntil *time.Time `json:"banned_until" yaml:"banned_until"`
BanReason *string `json:"ban_reason" yaml:"ban_reason"`
BannedBy *string `json:"banned_by" yaml:"banned_by"`
Metadata map[string]any `json:"metadata" yaml:"metadata"`
Password *BootstrapUserPassword `json:"password" yaml:"password"`
// RootRole assigns one root permission-group role to this user by name.
// "owner" (the built-in apex, root:*) is seeded SEED-IF-ABSENT; any other
// name is assigned as a same-named catalog role of the root persona.
RootRole string `json:"root_role" yaml:"root_role"`
}
type BootstrapUserPassword ¶
type BootstrapUserPassword struct {
Plaintext string `json:"plaintext" yaml:"plaintext"`
Hash string `json:"hash" yaml:"hash"`
HashAlgo string `json:"hash_algo" yaml:"hash_algo"`
HashParams map[string]any `json:"hash_params" yaml:"hash_params"`
ResetRequired bool `json:"reset_required" yaml:"reset_required"`
// Enforce makes the password DESIRED-STATE (#89): re-asserted on every
// reconcile. Default false = SEED-ONCE — the password is applied only when
// the user is first created, so a password rotated out of band (via the
// admin API) is never reverted to the manifest value on a later reconcile.
// Must not be combined with ResetRequired (forcing a reset every run is
// nonsensical).
Enforce bool `json:"enforce" yaml:"enforce"`
}
type Client ¶
type Client interface {
Users
Passwords
Admin
Roles
Groups
Tokens
APIKeys
Sessions
Providers
RemoteApps
Passwordless
Bootstrap
Senders
Entitlements
Maintenance
}
Client is the portable AuthKit contract: the full set of operations meaningful across both the in-process (embedded) and the Phase-2 remote transports (issue #138), composed from the topic interfaces above. Infra accessors (Postgres, Keyfunc, JWKS, raw Options/Schema) are deliberately OFF this interface; they stay on the concrete *embedded.Client. Code against authkit.Client (or one of the topic interfaces) so swapping backends is construction-only:
var c authkit.Client = embedded.New(cfg, pg) // today (in-process) // var c authkit.Client = remote.New(url, creds) // Phase 2 (standalone)
type CreateAccountRegistrationInviteRequest ¶ added in v0.72.0
type CustomJWTMintOptions ¶
type CustomJWTMintOptions struct {
// Claims is the host's claim set, e.g. {"cap_kind": "...", "grants": [...],
// "release_id": "..."}. Required and non-empty. It may carry `sub`/`aud`
// (unless overridden by the Subject/Audiences options) but may NOT carry the
// AuthKit-owned registered claims `iss`/`iat`/`exp`.
Claims map[string]any
// TTL is the token lifetime. Required (must be > 0); capped at
// MaxCustomJWTLifetime.
TTL time.Duration
// Type is the JOSE `typ` header (e.g. "worker-capability+jwt"). When empty the
// header is left unset — unlike the opinionated minters, MintCustomJWT does
// not impose a default `typ`; the host owns the token shape. It may NOT be one
// of AuthKit's own first-party classes (access / delegated-access /
// remote-application-access / service `+jwt`) — doing so returns
// ErrCustomJWTReservedType (AK2-AUTH-02).
Type string
// Subject, when set, becomes the `sub` claim and wins over any `sub` in Claims.
Subject string
// Audiences, when set, becomes the `aud` claim and wins over any `aud` in Claims.
Audiences []string
// Issuer, when set, becomes the `iss` claim; otherwise `iss` defaults to the
// Service's configured Issuer. This is the ONLY way to override `iss`.
Issuer string
}
type DelegatedAccessParams ¶
type DelegatedAccessParams struct {
// Issuer becomes the `iss` claim: the AuthKit issuer that signed the token.
// Must match a remote_application registered with the validating resource server.
// Required when minting via the free function; the *Service mint method
// defaults it to the Service's configured Issuer when empty.
Issuer string
// Audiences becomes the `aud` claim: the target resource API(s), e.g.
// "openrails", "tensorhub", or "gen-orchestrator".
Audiences []string
// DelegatedSubject becomes `delegated_sub`: the issuer-side subject id.
// Required. No local account is implied in the receiving service.
DelegatedSubject string
// Permissions becomes the `permissions` claim: an array of resource-defined
// permission strings (NOT OAuth's space-delimited `scope`). Receiving
// services validate these against their own permission set.
Permissions []string
// Attributes becomes the `attributes` claim: the canonical app-specific
// ESCAPE HATCH (#75). An object of issuer-asserted, NAMESPACED, OPAQUE
// key/values that AuthKit transports + optionally shape-validates but NEVER
// interprets — the semantics belong to the consuming app (tensorhub etc.).
// Each value is set in ONE of two modes, per key:
// INLINE — the value carries the full definition, e.g.
// {"tier":{"endpoints":[...],"caps":[...]}}. No lookup.
// REFERENCE — the value is a short string key, e.g. {"tier":"tier-1"},
// resolved by the consumer against a definition the
// remote_application registered ahead of time (see the
// attribute-def registry: Service.RegisterRemoteAppAttributeDef
// / ResolveRemoteAppAttributeDef). Keeps tokens small.
// Reserved well-known keys: `tier` (opaque entitlement-tier string) and
// `roles` (a uuid array; prefer the typed Roles field below). Everything
// else is free-form per consuming app. Values are arbitrary JSON.
Attributes map[string]any
// Roles is a convenience for emitting the delegated subject's role UUIDs into
// `attributes.roles` (a JSON array of UUID strings). Equivalent to setting
// Attributes["roles"] yourself; when both are set this typed field wins.
Roles []string
// TTL is the token lifetime. Defaults to 15m when zero.
TTL time.Duration
// JTI, when set, becomes the `jti` claim (token identifier). Optional.
JTI string
// NotBefore, when set, becomes the `nbf` claim. Optional.
NotBefore time.Time
}
type Entitlements ¶ added in v0.72.0
Entitlements reads a user's active entitlement names from the host-provided EntitlementsProvider.
type ErrorEnvelope ¶
type ErrorEnvelope struct {
Error ErrorObject `json:"error"`
}
ErrorEnvelope is the top-level error response: {"error": {...}}.
func NewErrorEnvelope ¶
func NewErrorEnvelope(status int, code string, param *string, metadata map[string]any) ErrorEnvelope
NewErrorEnvelope builds the canonical nested error envelope for an HTTP status + machine code: the type is derived from the status and the message from the code catalog. param and metadata are optional (omitted when nil/empty).
type ErrorObject ¶
type ErrorObject struct {
Type string `json:"type"`
Code string `json:"code"`
Message string `json:"message"`
Param *string `json:"param,omitempty"`
Metadata map[string]any `json:"metadata,omitempty"`
}
ErrorObject is the nested error detail carried under the top-level "error" key.
type GroupInviteLink ¶
type GroupInviteLink struct {
ID string
PermissionGroupID string
Role string
InvitedBy string
Email string // "" = permission-group shareable link; set = permission-group email-bound invite
MaxUses *int // nil = unlimited
Uses int
ExpiresAt *time.Time
RevokedAt *time.Time
CreatedAt time.Time
UpdatedAt time.Time
}
type GroupInviteLinkCreated ¶
type GroupMember ¶
type Groups ¶ added in v0.72.0
type Groups interface {
CreatePermissionGroup(ctx context.Context, req CreatePermissionGroupRequest) (string, error)
EnsureRootGroup(ctx context.Context) (string, error)
SeedPermissionGroupContainment(ctx context.Context) error
ResolveGroupIDForSlug(ctx context.Context, persona, instanceSlug string) (string, error)
CreateAccountRegistrationInvite(ctx context.Context, req CreateAccountRegistrationInviteRequest) (AccountRegistrationInviteCreated, error)
RevokeAccountRegistrationInvite(ctx context.Context, inviteID, actorUserID string) error
AssignGroupRole(ctx context.Context, persona, instanceSlug, subjectID, subjectKind, role string) error
AssignGroupRoleAs(ctx context.Context, actorUserID, persona, instanceSlug, subjectID, subjectKind, role string) error
UnassignGroupRoleAs(ctx context.Context, actorUserID, persona, instanceSlug, subjectID, subjectKind, role string) error
RemoveGroupSubjectAs(ctx context.Context, actorUserID, persona, instanceSlug, subjectID, subjectKind string) error
ListGroupMembers(ctx context.Context, persona, instanceSlug string) ([]GroupMember, error)
ListSubjectGroups(ctx context.Context, subjectID, subjectKind string) ([]SubjectGroupMembership, error)
Can(ctx context.Context, subjectID, subjectKind, persona, instanceSlug, perm string) (bool, error)
ListEffectivePermissions(ctx context.Context, subjectID, subjectKind, persona, instanceSlug string) ([]string, error)
CreateGroupInviteLink(ctx context.Context, req CreateGroupInviteLinkRequest) (GroupInviteLinkCreated, error)
ListGroupInviteLinks(ctx context.Context, persona, instanceSlug string) ([]GroupInviteLink, error)
RevokeGroupInviteLink(ctx context.Context, persona, instanceSlug, linkID string) error
RedeemGroupInviteLink(ctx context.Context, code, redeemerUserID string) (RedeemGroupInviteLinkResult, error)
ExternalInvitesEnabled() bool
}
Groups is the permission-group surface: lifecycle, membership, role assignment, authorization checks, and invite links.
type ImportUserInput ¶
type ImportUserInput struct {
Email string
PhoneNumber string
Username string
EmailVerified bool
PhoneVerified bool
BannedAt *time.Time
BannedUntil *time.Time
BanReason *string
BannedBy *string
Metadata map[string]any
CreatedAt *time.Time
UpdatedAt *time.Time
// Optional pre-hashed credential to import alongside the user (bulk legacy
// migration). When PasswordHash is non-empty and the user row is inserted,
// ImportUsers stores it verbatim. The verify-time whitelist (argon2id/bcrypt,
// else legacy-reset-required) still governs login; bulk import does not
// re-validate the hash, matching single-row UpsertPasswordHash.
PasswordHash string
HashAlgo string
HashParams []byte
}
type ImportUserResult ¶
type ImportUserResult struct {
Index int
UserID string // set when Status == inserted
Status ImportUserStatus
Reason string // set for skipped/rejected (machine-ish: "duplicate_in_batch", "already_exists", or a validation code)
}
type ImportUserStatus ¶
type ImportUserStatus string
type ImportUsersResult ¶
type ImportUsersResult struct {
Results []ImportUserResult
Inserted int
Skipped int
Rejected int
}
type Maintenance ¶ added in v0.72.0
type Maintenance interface {
CleanupExpiredAuthState(ctx context.Context) error
ValidateVerificationConfiguration() error
}
Maintenance is operational upkeep run outside a request: expire stale auth state, validate the verification configuration.
type Passwordless ¶ added in v0.72.0
type Passwordless interface {
StartPasswordless(ctx context.Context, req PasswordlessStartRequest) (PasswordlessStartResult, error)
ConfirmPasswordlessCode(ctx context.Context, identifier, code string) (PasswordlessConfirmResult, error)
ConfirmPasswordlessToken(ctx context.Context, token string) (PasswordlessConfirmResult, error)
RecordFailedPasswordlessCode(ctx context.Context, identifier string)
ClearPasswordlessCodeAttempts(ctx context.Context, identifier string)
}
Passwordless drives the email/SMS code/link login flow.
type PasswordlessStartResult ¶
type Passwords ¶ added in v0.72.0
type Passwords interface {
ChangePassword(ctx context.Context, userID, current, new string, keepSessionID *string) error
UpsertPasswordHash(ctx context.Context, userID, hash, algo string, params []byte) error
VerifyUserPassword(ctx context.Context, userID, pass string) bool
}
Passwords is the password credential surface: change, import, verify.
type PersonaCapabilities ¶ added in v0.72.0
PersonaCapabilities are opt-in generated management capabilities for a persona.
type PreferredLanguage ¶
type PreferredLanguage struct {
Language string
}
type Principal ¶ added in v0.72.0
type Principal struct {
Kind PrincipalKind `json:"kind"`
Issuer string `json:"issuer,omitempty"`
Subject string `json:"subject,omitempty"`
}
Principal is the small generic-auth shape host adapters expose.
type PrincipalKind ¶ added in v0.72.0
type PrincipalKind string
PrincipalKind is the broad AuthKit credential class for a verified request.
const ( PrincipalKindUser PrincipalKind = "user" PrincipalKindAPIKey PrincipalKind = "api_key" PrincipalKindRemoteApplication PrincipalKind = "remote_application" PrincipalKindDelegated PrincipalKind = "delegated" PrincipalKindService PrincipalKind = "service" )
type Providers ¶ added in v0.72.0
type Providers interface {
LinkProvider(ctx context.Context, userID, provider, subject string, email *string) error
LinkProviderByIssuer(ctx context.Context, userID, issuer, providerSlug, subject string, email *string) error
UnlinkProvider(ctx context.Context, userID, provider string) error
GetProviderUsername(ctx context.Context, userID, provider string) (string, error)
}
Providers links and unlinks external identity providers on an account.
type RBACDriftReport ¶ added in v0.72.0
type RBACDriftReport struct {
GroupUserRoles int `json:"group_user_roles"`
CustomRoles int `json:"group_custom_roles"`
APIKeys int `json:"api_keys"`
}
func (RBACDriftReport) Total ¶ added in v0.72.0
func (r RBACDriftReport) Total() int
type RegistrationMode ¶ added in v0.72.0
type RegistrationMode string
RegistrationMode is the public native-user self-registration policy (#147). It governs ONLY public self-registration; operators can always create users through privileged APIs, bootstrap, or manual DB operations regardless of mode.
Open — anyone may self-register.
InviteOnly — self-registration requires a valid account-registration invite
bound to the registrant's email (see account-registration invites).
Closed — no public self-registration at all.
The former AdminOnly / AdminBootstrapOnly / ManifestOnly modes were removed (#147): they described operator-side creation, not a public self-registration policy, and are subsumed by "use the privileged APIs" under any mode.
const ( RegistrationModeOpen RegistrationMode = "open" RegistrationModeInviteOnly RegistrationMode = "invite_only" RegistrationModeClosed RegistrationMode = "closed" )
type RegistrationVerificationPolicy ¶ added in v0.72.0
type RegistrationVerificationPolicy string
RegistrationVerificationPolicy controls whether a newly-registered contact must be verified.
const ( RegistrationVerificationNone RegistrationVerificationPolicy = "none" RegistrationVerificationOptional RegistrationVerificationPolicy = "optional" RegistrationVerificationRequired RegistrationVerificationPolicy = "required" )
type RemoteAppAttributeDef ¶
type RemoteAppAttributeDef struct {
RemoteApplicationID string
Key string
Version int32
Definition json.RawMessage
}
RemoteAppAttributeDef is a remote_application's registered attribute definition: the full inline value a REFERENCE-mode delegated-token attribute resolves to (#75). Definition is opaque JSON the consuming app interprets.
type RemoteAppKey ¶
type RemoteAppKey struct {
KID string `json:"kid,omitempty" yaml:"kid,omitempty"`
PublicKeyPEM string `json:"public_key_pem" yaml:"public_key_pem"`
}
RemoteAppKey is one entry of a static-mode principal's human-managed key list (stored as jsonb; edited like an authorized_keys file).
type RemoteApplication ¶
type RemoteApplication struct {
ID string
Slug string
PermissionGroupID string // controlling permission-group id
Issuer string // OIDC iss
JWKSURI string // OIDC jwks_uri (jwks mode only)
// Mode is the trust source: RemoteAppModeJWKS (fetch from JWKSURI) XOR
// RemoteAppModeStatic (human-managed PublicKeys list). Never both.
Mode string
// PublicKeys is the static-mode key list (empty in jwks mode).
PublicKeys []RemoteAppKey
Enabled bool
CreatedAt time.Time
UpdatedAt time.Time
}
RemoteApplication is a registered federation principal: an external issuer authkit trusts to mint delegated/remote-application tokens. It is a plain data view; persistence and lifecycle live in core.
type RemoteApplicationAccessParams ¶
type RemoteApplicationAccessParams struct {
// Issuer becomes the `iss` claim: the remote_application's OIDC issuer,
// registered with the validating resource server. Required when minting via
// the free function; the *Service mint method defaults it to the Service's
// configured Issuer when empty.
Issuer string
// Audiences becomes the `aud` claim: the target resource API(s).
Audiences []string
// TTL is the token lifetime. Defaults to 15m when zero.
TTL time.Duration
// JTI, when set, becomes the `jti` claim. Optional.
JTI string
// NotBefore, when set, becomes the `nbf` claim. Optional.
NotBefore time.Time
// Permissions, when non-nil, becomes the `permissions` claim: a DOWN-SCOPING
// request for least-privilege (#76 amendment). The stored grant is the
// ceiling; effective = this claim, but EVERY claimed perm must be within the
// stored grant — an out-of-grant claimed perm REJECTS the token at verify (a
// remote application access token can never widen). nil/absent => no claim
// => full stored ceiling (backward-compatible with v0.28.0 tokens).
Permissions []string
}
type RemoteApps ¶ added in v0.72.0
type RemoteApps interface {
UpsertRemoteApplication(ctx context.Context, in RemoteApplication) (*RemoteApplication, error)
GetRemoteApplication(ctx context.Context, issuer string) (*RemoteApplication, error)
DeleteRemoteApplication(ctx context.Context, issuer string) error
ListRemoteApplications(ctx context.Context, activeOnly bool) ([]RemoteApplication, error)
ResolveRemoteApplicationAuthority(ctx context.Context, appID string) ([]string, error)
ResolveRemoteAppAttributeDef(ctx context.Context, appID, key string, version int32) (*RemoteAppAttributeDef, error)
}
RemoteApps manages trusted remote applications (federation issuers) and resolves their stored authority.
type ResolvedAPIKey ¶
type ResolvedAPIKey struct {
APIKeyID string
KeyID string
// PermissionGroupID is the controlling permission-group id.
PermissionGroupID string
Role string
Permissions []string
}
ResolvedAPIKey is the API-key resolution result. Permissions is the key's role resolved to its effective permission set AT VERIFY TIME (so a role edit is reflected immediately — perms are never frozen into the key).
type Roles ¶ added in v0.72.0
type Roles interface {
AssignRoleBySlug(ctx context.Context, userID, slug string) error
AssignRoleBySlugAs(ctx context.Context, actorUserID, userID, slug string) error
RemoveRoleBySlug(ctx context.Context, userID, slug string) error
RemoveRoleBySlugAs(ctx context.Context, actorUserID, userID, slug string) error
UpsertRoleBySlug(ctx context.Context, name, slug string, description *string) error
ListRoleSlugsByUser(ctx context.Context, userID string) []string
ListRoleSlugsByUserErr(ctx context.Context, userID string) ([]string, error)
}
Roles is global root-role assignment. The *As variants are the actor-checked (no-escalation) path; the plain ones are the bootstrap path.
type Senders ¶ added in v0.72.0
type Senders interface {
HasEmailSender() bool
HasSMSSender() bool
SMSAvailable() bool
CheckSMSHealth(ctx context.Context) error
}
Senders reports whether the configured message senders are available and healthy.
type ServiceJWTClaims ¶
type ServiceJWTClaims struct {
Issuer string
Subject string
Audiences []string
IssuedAt time.Time
NotBefore time.Time
ExpiresAt time.Time
JTI string
TokenUse string
Permissions []string
Scope []string
}
ServiceJWTClaims is the canonical AuthKit claim shape for caller-minted machine-to-machine JWTs. Permissions are requested capabilities; receiving services must still intersect them with server-side grants.
type ServiceJWTMintOptions ¶
type Session ¶
type Session struct {
ID string
FamilyID string
CreatedAt time.Time
LastAuthenticatedAt *time.Time
LastUsedAt time.Time
ExpiresAt *time.Time
RevokedAt *time.Time
UserAgent *string
IPAddr *string
}
Session is a sanitized session view (no tokens). Part of the wire contract.
type Sessions ¶ added in v0.72.0
type Sessions interface {
ExchangeRefreshToken(ctx context.Context, refreshToken string, ua string, ip net.IP) (string, time.Time, string, error)
ListUserSessions(ctx context.Context, userID string) ([]Session, error)
RevokeAllSessions(ctx context.Context, userID string, keepSessionID *string) error
}
Sessions is the refresh-session surface: refresh-token exchange, list, and revoke-all.
type SubjectGroupMembership ¶
type Tokens ¶ added in v0.72.0
type Tokens interface {
IssueAccessToken(ctx context.Context, userID, email string, extra map[string]any) (string, time.Time, error)
MintCustomJWT(ctx context.Context, opts CustomJWTMintOptions) (string, error)
MintDelegatedAccessToken(ctx context.Context, p DelegatedAccessParams) (string, error)
MintRemoteApplicationAccessToken(ctx context.Context, p RemoteApplicationAccessParams) (string, error)
MintServiceJWT(ctx context.Context, opts ServiceJWTMintOptions) (string, ServiceJWTClaims, error)
}
Tokens issues the app's JWTs: access, service, delegated, remote-application, and custom.
type TwoFactorMethod ¶ added in v0.72.0
type TwoFactorMethod string
TwoFactorMethod is one second-factor channel a host enables.
const ( TwoFactorEmail TwoFactorMethod = "email" TwoFactorSMS TwoFactorMethod = "sms" TwoFactorTOTP TwoFactorMethod = "totp" )
type TwoFactorMode ¶ added in v0.72.0
type TwoFactorMode string
TwoFactorMode is the host's account-wide 2FA enrollment policy.
const ( // TwoFactorDisabled turns 2FA off entirely: no user enrollment/challenge/ // verify routes are usable. TwoFactorDisabled TwoFactorMode = "disabled" // TwoFactorOptional lets users enroll a second factor if they choose; an // un-enrolled user is not blocked from normal session use. TwoFactorOptional TwoFactorMode = "optional" // TwoFactorRequired forces every user to enroll a second factor before normal // session use. Existing un-enrolled users are challenged on their next // authenticated request (the session, not just signup, is gated). TwoFactorRequired TwoFactorMode = "required" )
type User ¶
type User struct {
ID string
Email *string // Nullable - phone-only users have NULL email
PhoneNumber *string
Username *string
DiscordUsername *string
EmailVerified bool
PhoneVerified bool
BannedAt *time.Time
BannedUntil *time.Time
BanReason *string
BannedBy *string
DeletedAt *time.Time
Biography *string
CreatedAt time.Time
UpdatedAt time.Time
LastLogin *time.Time
}
User is the public user view returned by AuthKit lookups. Plain data — part of the wire contract shared by the embedded engine and (Phase 2) the remote SDK. See #138 (contract inversion): definitions live here in the lean, pgx-free contract package; internal/authcore aliases back to these.
type UserRef ¶ added in v0.66.0
UserRef is a slim user projection (id + display fields) returned by batch lookups like Client.UsersByIDs — resolving many user IDs to display data in one query, without N+1 single fetches. Part of the wire contract.
type Users ¶ added in v0.72.0
type Users interface {
CreateUser(ctx context.Context, email, username string) (*User, error)
GetEmailByUserID(ctx context.Context, id string) (string, error)
GetUserByEmail(ctx context.Context, email string) (*User, error)
GetUserByPhone(ctx context.Context, phone string) (*User, error)
GetUserBySolanaAddress(ctx context.Context, address string) (*User, error)
GetUserByUsername(ctx context.Context, username string) (*User, error)
GetUserMetadata(ctx context.Context, userID string) (map[string]any, error)
PatchUserMetadata(ctx context.Context, userID string, patch map[string]any) error
HardDeleteUser(ctx context.Context, userID string) error
SoftDeleteUser(ctx context.Context, id string) error
RestoreUser(ctx context.Context, id string) error
SetEmailVerified(ctx context.Context, id string, v bool) error
UpdateBiography(ctx context.Context, id string, bio *string) error
UpdateEmail(ctx context.Context, id, email string) error
UpdateUsername(ctx context.Context, id, username string) error
UpdateImportedUser(ctx context.Context, userID string, input ImportUserInput) (*User, error)
ImportUsers(ctx context.Context, inputs []ImportUserInput) (ImportUsersResult, error)
ListUsersDeletedBefore(ctx context.Context, cutoff time.Time, limit int) ([]string, error)
TimeUntilUsernameRenameAvailable(ctx context.Context, userID string, now time.Time) (int64, error)
IsUserAllowed(ctx context.Context, userID string) (bool, error)
// UsersByIDs resolves many user IDs to slim display projections (id +
// username/email) in ONE query: the batch read for "render N authors"
// without N+1. Missing IDs are simply absent from the result. (Replaces the
// removed authkit/identity store; writes go through UpdateUsername/UpdateEmail,
// which enforce the rename cooldown + validation raw table writes skip.)
UsersByIDs(ctx context.Context, ids []string) ([]UserRef, error)
}
Users is account create/read/update/delete, identity lookups, metadata, and bulk import/read.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
adapters
|
|
|
Package testing provides utilities for testing applications that use authkit.
|
Package testing provides utilities for testing applications that use authkit. |
|
cmd
|
|
|
authkit-devserver
command
|
|
|
authkit-server
command
Command authkit-server is the standalone, self-hostable AuthKit server (#142).
|
Command authkit-server is the standalone, self-hostable AuthKit server (#142). |
|
Re-exports of the public types, constants, sentinel errors, and helper functions implemented in internal/authcore.
|
Re-exports of the public types, constants, sentinel errors, and helper functions implemented in internal/authcore. |
|
internal
|
|
|
db
Schema indirection (authkit issue 69).
|
Schema indirection (authkit issue 69). |
|
genremote
command
Command genremote generates the AuthKit remote SDK (authkit/remote) and the management-API method registry (authkit/server) from the authkit.Client interface in client.go — ONE source of truth for both transports (#142).
|
Command genremote generates the AuthKit remote SDK (authkit/remote) and the management-API method registry (authkit/server) from the authkit.Client interface in client.go — ONE source of truth for both transports (#142). |
|
migrations
|
|
|
postgres
Package migrations embeds AuthKit's Postgres schema migrations.
|
Package migrations embeds AuthKit's Postgres schema migrations. |
|
Package remote is the AuthKit remote SDK: a Go client that talks to a standalone AuthKit server's management API over HTTP and satisfies the SAME authkit.Client contract an in-process embedded.Client does (#142), so a host swaps embedded↔remote with one construction line:
|
Package remote is the AuthKit remote SDK: a Go client that talks to a standalone AuthKit server's management API over HTTP and satisfies the SAME authkit.Client contract an in-process embedded.Client does (#142), so a host swaps embedded↔remote with one construction line: |
|
Package server hosts the AuthKit management HTTP API — the wire contract a standalone AuthKit server exposes and the authkit/remote SDK consumes (#142).
|
Package server hosts the AuthKit management HTTP API — the wire contract a standalone AuthKit server exposes and the authkit/remote SDK consumes (#142). |
|
Package siws implements Sign In With Solana (SIWS) authentication.
|
Package siws implements Sign In With Solana (SIWS) authentication. |
|
storage
|
|