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 DescribeCode(code Code) (status int, message string, ok bool)
- func ErrorTypeForStatus(status int) string
- func FormatAPIKey(prefix, keyID, secret string) string
- func HasAPIKeyPrefix(prefix, token string) bool
- func LanguageFromContext(ctx context.Context) (string, bool)
- func ParseAPIKey(prefix, token string) (keyID, secret string, ok bool)
- func PublicDisplayName(refs map[string]PublicUserRef, id string) string
- func ValidRemoteApplicationIssuer(iss string) bool
- func WithLanguage(ctx context.Context, language string) context.Context
- func WriteError(w http.ResponseWriter, err error)
- type APIKey
- type APIKeyMintOptions
- type AccountRegistrationInvite
- type AccountRegistrationInviteCreated
- type ActionAvailability
- type AdminListUsersResult
- type AdminUser
- type AdminUserListOptions
- type AdminUserSort
- type AdminUserStatus
- type ApplicationDocument
- type BootstrapManifest
- type BootstrapManifestDev
- type BootstrapManifestRemoteApplication
- type BootstrapManifestResult
- type BootstrapManifestUser
- type BootstrapReconcileOptions
- type BootstrapUserPassword
- type Client
- type Code
- type CreateAccountRegistrationInviteRequest
- type CreateGroupInviteLinkRequest
- type CreatePermissionGroupRequest
- type CustomRoleDef
- type DelegatedAccessParams
- type DelegationAuthorizer
- type DelegationGrant
- type DelegationRequest
- type DeletePermissionGroupOptions
- type DocumentEnvelope
- type DocumentReference
- type Error
- type ErrorEnvelope
- type ErrorObject
- type ErrorOption
- type FormerNameRetentionConfig
- type FormerNameRetentionMode
- type GroupInstance
- type GroupInstanceUpdate
- type GroupInviteLink
- type GroupInviteLinkCreated
- type GroupMember
- type GroupRef
- type ImportUnverifiedSolanaLinkInput
- type ImportUnverifiedSolanaLinkResult
- type ImportUnverifiedSolanaLinkStatus
- type ImportUnverifiedSolanaLinksResult
- type ImportUserInput
- type ImportUserResult
- type ImportUserStatus
- type ImportUsersResult
- type InstanceCreationDef
- type ListPage
- type MFAStatus
- type NameAdmissionRequest
- type NameAlias
- type NameOperation
- type NameResolution
- type NamingConfig
- type NamingPolicy
- type NamingState
- type OpResult
- type PasswordlessConfirmResult
- type PasswordlessStartRequest
- type PasswordlessStartResult
- type Perm
- type Persona
- type PersonaCapabilities
- type PreferredLanguage
- type Principal
- type PrincipalKind
- type PublicUserRef
- type RedeemGroupInviteLinkResult
- type RegisteredApplication
- type RegistrationMode
- type RegistrationVerificationPolicy
- type RemoteAppAttributeDef
- type RemoteAppKey
- type RemoteApplication
- type RemoteApplicationAccessParams
- type RemoteApplicationAuthority
- type ResolvedAPIKey
- type Role
- type ServiceJWTClaims
- type ServiceJWTMintOptions
- type Session
- type SignedDocument
- type SolanaLinkedAccount
- type StepUpTwoFactorOption
- type StepUpTwoFactorOptions
- type Subject
- type SubjectGroupMembership
- type SubjectKind
- type TokenSet
- type TwoFactorMethod
- type TwoFactorMode
- type User
- type UserLiveness
- type UserProfile
- type UserRef
- type UserSecurity
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 ( SubjectKindUser SubjectKind = "user" SubjectKindRemoteApp SubjectKind = "remote_application" // RootPersona is the single built-in persona: every deployment has exactly // one root group, the parentless ancestor of every other group. RootPersona Persona = "root" // OwnerRole is the role every persona ships: it holds the persona's whole // namespace (`<persona>:*`) and nothing else. OwnerRole Role = "owner" )
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 ( ApplicationTierRegistered = "registered" ApplicationTierApproved = "approved" )
Application capability tiers (#264).
const ( ApplicationTrustRootManual = "manual" ApplicationTrustRootDomain = "domain" ApplicationTrustRootUser = "user" )
Application trust roots (#264): the authority that rotates keys.
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 ( ActionUpdateUsername = "update_username" ActionRequestPasswordReset = "request_password_reset" ActionRequestVerification = "request_verification" )
ActionAvailability reports whether a cooldown-gated action is currently allowed; it rides on GET /me and on 429 error metadata. Action names carried by ActionAvailability.
const ApplicationWellKnownPath = "/.well-known/authkit/application.json"
ApplicationWellKnownPath is where a domain-registered application serves its ApplicationDocument. Fetching it over HTTPS IS the domain-control proof.
const MaxRemoteApplicationIssuerLen = 512
MaxRemoteApplicationIssuerLen bounds a remote-application issuer identifier. Registration refuses longer values and the verifier never consults the store for them (ak#297).
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 = E(CodeInvalidToken) // ErrAccessTokenRevoked indicates the API key was explicitly revoked. ErrAccessTokenRevoked = E(CodeAccessTokenRevoked) // ErrAccessTokenExpired indicates the API key is past its expires_at. ErrAccessTokenExpired = E(CodeAccessTokenExpired) )
var ( CodeTwoFAChallengeFailed = def("2fa_challenge_failed", 500, "The two-factor challenge could not be created.") CodeTwoFAEnrollmentRequired = def("2fa_enrollment_required", 403, "Two-factor authentication must be enrolled to continue.") CodeTwoFAFactorExists = def("2fa_factor_exists", 409, "A two-factor authentication method is already enrolled. Remove it before enrolling a replacement.") CodeTwoFARequired = def("2fa_required", 403, "Two-factor authentication is required.") CodeTwoFASendFailed = def("2fa_send_failed", 500, "The two-factor code could not be sent.") CodeAbandonFailed = def("abandon_failed", 500, "The registration could not be abandoned.") CodeAccessTokenCreateFailed = def("access_token_create_failed", 500, "The access token could not be created.") CodeAccessTokenHasSub = def("access_token_has_sub", 401, "An access token must not carry a subject.") CodeAccessTokenWrongTyp = def("access_token_wrong_typ", 401, "The token type is wrong for an access token.") CodeAccountAuthorityEscalation = def("account_authority_escalation", 403, "That account holds authority you do not.") CodeAccountDisabled = def("account_disabled", 401, "This account is disabled.") CodeAccountExistsLinkRequired = def("account_exists_link_required", 409, "An account with this email already exists. Sign in and link the provider.") CodeAccountRegistrationInviteConsumed = def("account_registration_invite_consumed", 410, "The registration invite has already been used.") CodeAccountRegistrationInviteExpired = def("account_registration_invite_expired", 410, "The registration invite has expired.") CodeAccountRegistrationInviteNotFound = def("account_registration_invite_not_found", 404, "The registration invite was not found.") CodeAccountRegistrationInviteRevoked = def("account_registration_invite_revoked", 410, "The registration invite was revoked.") CodeAddressMismatch = def("address_mismatch", 400, "The address does not match.") CodeAddressRequired = def("address_required", 400, "An address is required.") CodeApplicationDocumentFetchFailed = def("application_document_fetch_failed", 502, "The application document could not be fetched.") CodeApplicationDocumentInvalid = def("application_document_invalid", 400, "The application document is invalid.") CodeApplicationDomainConflict = def("application_domain_conflict", 409, "That domain already belongs to another application.") CodeApplicationDomainInvalid = def("application_domain_invalid", 400, "The application domain is invalid.") CodeApplicationIssuerConflict = def("application_issuer_conflict", 409, "That issuer already belongs to another application.") CodeApplicationRegistrationDisabled = def("application_registration_disabled", 403, "Application registration is disabled.") CodeApplicationSlugConflict = def("application_slug_conflict", 409, "That application slug is taken.") CodeAttributeDefNotFound = def("attribute_def_not_found", 404, "The attribute definition was not found.") CodeAuthRequiredForLink = def("auth_required_for_link", 401, "Sign in before linking a provider.") CodeAuthenticationFailed = def("authentication_failed", 401, "Authentication failed.") CodeAuthenticationRequired = def("authentication_required", 401, "Authentication is required.") CodeAvatarURLInvalid = def("avatar_url_invalid", 400, "The avatar URL is invalid.") CodeBadAudience = def("bad_audience", 401, "The token audience is not accepted.") CodeBadIssuer = def("bad_issuer", 401, "The token issuer is not trusted.") CodeBootstrapDatabaseNotEmpty = def("bootstrap_database_not_empty", 409, "The database is not empty; bootstrap refused.") CodeCannotRemoveLastAdminRole = def("cannot_remove_last_admin_role", 409, "The last owner cannot be removed.") CodeCannotRemoveLastOwner = def("cannot_remove_last_owner", 409, "The last owner cannot be removed.") CodeCannotUnlinkLastLoginMethod = def("cannot_unlink_last_login_method", 400, "The last login method cannot be unlinked.") CodeChallengeExpired = def("challenge_expired", 401, "The challenge has expired.") CodeChallengeFailed = def("challenge_failed", 500, "The challenge could not be created.") CodeChallengeVerifyFailed = def("challenge_verify_failed", 500, "The challenge could not be verified.") CodeConfirmationWrongTokenType = def("confirmation_wrong_token_type", 401, "This token type does not accept a confirmation claim.") CodeConflictingSubject = def("conflicting_subject", 401, "The token carries conflicting subjects.") CodeCustomRoleGrantCrossPersona = def("custom_role_grant_cross_persona", 400, "A custom role may only grant permissions in its own persona.") CodeCustomRoleGrantOutsideCatalog = def("custom_role_grant_outside_catalog", 400, "A custom role grant is outside the persona catalog.") CodeCustomRoleIsCatalogRole = def("custom_role_is_catalog_role", 400, "A catalog role cannot be redefined as a custom role.") CodeCustomRoleNameInvalid = def("custom_role_name_invalid", 400, "The custom role name is invalid.") CodeCustomRolesNotSupported = def("custom_roles_not_supported", 400, "This persona does not support custom roles.") CodeDatabaseError = def("database_error", 500, "An internal error occurred. Please try again.") CodeDelegatedAccessHasRoles = def("delegated_access_has_roles", 401, "A delegated token must not carry roles.") CodeDelegatedAccessHasUserTier = def("delegated_access_has_user_tier", 401, "A delegated token must not carry a user tier.") CodeDelegatedAccessWrongTyp = def("delegated_access_wrong_typ", 401, "The token type is wrong for delegated access.") CodeDelegatedMintFailed = def("delegated_mint_failed", 500, "The delegated token could not be minted.") CodeDelegatedTokenTooLarge = def("delegated_token_too_large", 500, "The delegated token is too large.") CodeDelegationRefused = def("delegation_refused", 403, "The delegation was refused.") CodeDeviceKeysDisabled = def("device_keys_disabled", 403, "Device keys are disabled.") CodeDisableTwoFAFailed = def("disable_2fa_failed", 500, "Two-factor authentication could not be disabled.") CodeDuplicateClaim = def("duplicate_claim", 401, "The document carries a duplicate claim.") CodeEmailAlreadyVerified = def("email_already_verified", 409, "The email address is already verified.") CodeEmailDeliveryFailed = def("email_delivery_failed", 502, "The email could not be delivered.") CodeEmailInUse = def("email_in_use", 400, "That email address is already in use.") CodeEmailUnchanged = def("email_unchanged", 400, "The email address is unchanged.") CodeEmailVerificationSendFailed = def("email_verification_failed", 500, "The verification email could not be sent.") CodeTwoFAEnableFailed = def("enable_2fa_failed", 500, "Two-factor authentication could not be enabled.") CodeExternalInvitesDisabled = def("external_invites_disabled", 403, "Invite links are disabled.") CodeFailedToBan = def("failed_to_ban", 500, "The user could not be banned.") CodeFailedToDelete = def("failed_to_delete", 500, "The delete failed.") CodeFailedToList = def("failed_to_list", 500, "The list could not be loaded.") CodeFailedToListSignins = def("failed_to_list_signins", 500, "Sign-ins could not be listed.") CodeFailedToListUsers = def("failed_to_list_users", 500, "Users could not be listed.") CodeFailedToLogout = def("failed_to_logout", 500, "Logout failed.") CodeFailedToRequestEmailChange = def("failed_to_request_email_change", 400, "The email change could not be requested.") CodeFailedToRequestPhoneChange = def("failed_to_request_phone_change", 400, "The phone change could not be requested.") CodeFailedToRevoke = def("failed_to_revoke", 500, "The revoke failed.") CodeFailedToRevokeAll = def("failed_to_revoke_all", 500, "The sessions could not be revoked.") CodeFailedToRevokeSessions = def("failed_to_revoke_sessions", 500, "The sessions could not be revoked.") CodeFailedToUnban = def("failed_to_unban", 500, "The user could not be unbanned.") CodeFailedToUnlink = def("failed_to_unlink", 500, "The provider could not be unlinked.") CodeFailedToUpdatePreferredLanguage = def("failed_to_update_preferred_language", 400, "The preferred language could not be updated.") CodeFailedToUpdateUsername = def("failed_to_update_username", 400, "The username could not be updated.") CodeForbidden = def("forbidden", 403, "You do not have permission to perform this action.") CodeGroupCreationRefused = def("group_creation_refused", 403, "Group creation was refused.") CodeInviteLinkExpired = def("group_invite_link_expired", 400, "The invite link has expired.") CodeInviteLinkNotFound = def("group_invite_link_not_found", 404, "The invite link was not found.") CodeInviteLinkRevoked = def("group_invite_link_revoked", 400, "The invite link was revoked.") CodeGroupSlugApplicationManaged = def("group_slug_application_managed", 409, "The group slug is managed by its application.") CodeGroupSlugInvalid = def("group_slug_invalid", 400, "The group slug is invalid.") CodeGroupSlugReserved = def("group_slug_reserved", 403, "That group slug is reserved.") CodeGroupSlugTaken = def("group_slug_taken", 409, "That group slug is taken.") CodeHashFailed = def("hash_failed", 500, "The password could not be hashed.") CodeInsufficientRoleAuthority = def("insufficient_role_authority", 403, "You do not have the authority for this role change.") CodeInternalError = def("internal_error", 500, "An internal error occurred. Please try again.") CodeInvalidAddress = def("invalid_address", 400, "The address is invalid.") CodeInvalidAttributeDef = def("invalid_attribute_def", 400, "The attribute definition is invalid.") CodeInvalidAudiences = def("invalid_audiences", 400, "The audiences are invalid.") CodeInvalidBaseURL = def("invalid_base_url", 500, "The frontend base URL is invalid.") CodeInvalidBootstrapManifest = def("invalid_bootstrap_manifest", 400, "The bootstrap manifest is invalid.") CodeInvalidChallenge = def("invalid_challenge", 401, "The challenge is invalid.") CodeInvalidCode = def("invalid_code", 400, "The code is invalid.") CodeInvalidConfirmation = def("invalid_confirmation", 401, "The confirmation claim is invalid.") CodeInvalidCredentials = def("invalid_credentials", 401, "Invalid credentials.") CodeInvalidDelegateCertificate = def("invalid_delegate_certificate", 400, "The delegate certificate is invalid.") CodeInvalidEmail = defParam("invalid_email", 400, "email", "The email address is invalid.") CodeInvalidExpiry = def("invalid_expiry", 400, "The expiry is invalid.") CodeInvalidIdentifier = def("invalid_identifier", 400, "The identifier must be an email address or a phone number.") CodeInvalidInvite = def("invalid_invite", 400, "The invite is invalid.") CodeInvalidMessageEncoding = def("invalid_message_encoding", 400, "The message encoding is invalid.") CodeInvalidTwoFAMethod = def("invalid_method", 400, "The method is invalid.") CodeInvalidOrExpiredCode = def("invalid_or_expired_code", 400, "The code is invalid or has expired.") CodeInvalidOrExpiredToken = def("invalid_or_expired_token", 400, "The token is invalid or has expired.") CodeInvalidPassword = def("invalid_password", 401, "The password is incorrect.") CodeInvalidPhoneNumber = defParam("invalid_phone_number", 400, "phone_number", "The phone number is invalid.") CodeInvalidPreferredLanguage = defParam("invalid_preferred_language", 400, "preferred_language", "The preferred language is invalid.") CodeInvalidProvider = def("invalid_provider", 400, "The provider is invalid.") CodeInvalidRefreshToken = def("invalid_refresh_token", 401, "The refresh token is invalid.") CodeInvalidRemoteApplication = def("invalid_remote_application", 400, "The remote application is invalid.") CodeInvalidRequest = def("invalid_request", 400, "The request is invalid.") CodeInvalidRequestedGrant = def("invalid_requested_grant", 400, "The requested grant is invalid.") CodeInvalidRole = def("invalid_role", 400, "The role is invalid.") CodeInvalidServiceJWT = def("invalid_service_jwt", 401, "The service token is invalid.") CodeInvalidSignature = def("invalid_signature", 401, "The signature is invalid.") CodeInvalidSignatureEncoding = def("invalid_signature_encoding", 400, "The signature encoding is invalid.") CodeInvalidState = def("invalid_state", 400, "The state is invalid.") CodeInvalidToken = def("invalid_token", 401, "The authentication token is invalid.") CodeInvalidUI = def("invalid_ui", 400, "The ui parameter is invalid.") CodeInvalidUntil = def("invalid_until", 400, "The until value is invalid.") CodeLinkFailed = def("link_failed", 400, "The link failed.") CodeMalformedPayload = def("malformed_payload", 401, "The token payload is malformed.") CodeMalformedPermissions = def("malformed_permissions", 401, "The permissions claim is malformed.") CodeMissingAudience = def("missing_audience", 401, "The token carries no audience.") CodeMissingDelegatedSub = def("missing_delegated_sub", 401, "The delegated token carries no subject.") CodeMissingExp = def("missing_exp", 401, "The token carries no expiry.") CodeMissingFields = def("missing_fields", 400, "Required fields are missing.") CodeMissingIAT = def("missing_iat", 401, "The token carries no issued-at.") CodeMissingKID = def("missing_kid", 401, "The token names no key.") CodeMissingName = def("missing_name", 400, "A name is required.") CodeMissingNBF = def("missing_nbf", 401, "The token carries no not-before.") CodeMissingSessionID = def("missing_session_id", 400, "A session id is required.") CodeMissingSidClaim = def("missing_sid_claim", 400, "The token carries no session.") CodeMissingSigner = def("missing_signer", 500, "No signing key is configured.") CodeMissingSub = def("missing_sub", 401, "The token carries no subject.") CodeMissingToken = def("missing_token", 401, "A bearer token is required.") CodeMissingTokenTyp = def("missing_token_typ", 401, "The token carries no type.") CodeNameAdmissionRefused = def("name_admission_refused", 403, "That name was refused.") CodeNilToken = def("nil_token", 500, "No token was presented.") CodeNotAuthenticated = def("not_authenticated", 401, "Authentication is required.") CodeNotDelegatedAccessToken = def("not_delegated_access_token", 401, "The token is not a delegated access token.") CodeNotFound = def("not_found", 404, "The requested resource was not found.") CodeNotGroupMember = def("not_group_member", 403, "The subject is not a member of the group.") CodeNotImplemented = def("not_implemented", 501, "Not implemented.") CodeOIDCBeginFailed = def("oidc_begin_failed", 400, "The provider login could not be started.") CodeOIDCExchangeFailed = def("oidc_exchange_failed", 401, "The provider login could not be completed.") CodeOwnerSlugTaken = defParam("owner_slug_taken", 400, "username", "That name is taken.") CodePasskeyCloneDetected = def("passkey_clone_detected", 401, "The passkey appears to have been cloned.") CodePasskeyFailed = def("passkey_failed", 500, "The passkey operation failed.") CodePasskeyNotFound = def("passkey_not_found", 404, "The passkey was not found.") CodePasskeyUserVerificationRequired = def("passkey_user_verification_required", 401, "The passkey must verify the user.") CodePasswordChangeFailed = def("password_change_failed", 400, "The password could not be changed.") CodePasswordResetRequestFailed = def("password_reset_request_failed", 500, "The password reset could not be requested.") CodePasswordResetRequired = def("password_reset_required", 401, "A password reset is required before you can sign in.") CodePasswordTooShort = defParam("password_too_short", 400, "password", "The password is too short.") CodePasswordlessDisabled = def("passwordless_disabled", 403, "Passwordless login is disabled.") CodePendingRegistrationNotFound = def("pending_registration_not_found", 404, "No pending registration was found.") CodeGroupNotFound = def("permission_group_not_found", 404, "The permission group was not found.") CodePermissionNotGranted = def("permission_not_granted", 403, "The token claims a permission it was not granted.") CodePhoneAlreadyVerified = def("phone_already_verified", 409, "The phone number is already verified.") CodePhoneNumberRequired = def("phone_and_code_required", 400, "A phone number is required.") CodePhoneInUse = def("phone_in_use", 400, "That phone number is already in use.") CodePhoneNumberMustBeE164 = def("phone_number_must_be_e164", 400, "The phone number must be in E.164 format.") CodePhoneUnchanged = def("phone_unchanged", 400, "The phone number is unchanged.") CodePhoneVerificationSendFailed = def("phone_verification_failed", 500, "The verification SMS could not be sent.") CodePKCEGenerationFailed = def("pkce_generation_failed", 500, "The login could not be started.") CodePreferredLanguageLookupFailed = def("preferred_language_lookup_failed", 500, "The preferred language could not be read.") CodeProviderAlreadyLinked = def("provider_already_linked", 409, "That provider identity is already linked to another account.") CodeProviderChangeRequiresUnlink = def("provider_change_requires_unlink", 409, "Unlink the current provider account before linking another.") CodeProviderError = def("provider_error", 400, "The provider returned an error.") CodeProviderLinkFailed = def("provider_link_failed", 500, "The provider could not be linked.") CodeProviderNotLinked = def("provider_not_linked", 400, "That provider is not linked.") CodeRateLimited = def("rate_limited", 429, "Too many requests. Please try again later.") CodeRegenerateCodesFailed = def("regenerate_codes_failed", 500, "Backup codes could not be regenerated.") CodeRegistrationDisabled = def("registration_disabled", 403, "Registration is currently disabled.") CodeRegistrationFailed = def("registration_failed", 500, "Registration failed.") CodeRemoteApplicationAccessHasSubject = def("remote_application_access_has_subject", 401, "A remote-application token must not carry a subject.") CodeRemoteApplicationIssuerConflict = def("remote_application_issuer_conflict", 409, "That issuer already belongs to another remote application.") CodeRemoteApplicationNotFound = def("remote_application_not_found", 404, "The remote application was not found.") CodeRenameRateLimited = def("rename_rate_limited", 429, "Too many renames. Please try again later.") CodeRenamesDisabled = def("renames_disabled", 403, "Renames are disabled.") CodeResendFailed = def("resend_failed", 500, "The code could not be resent.") CodeReservedIssuer = def("reserved_issuer", 400, "That issuer is reserved.") CodeRoleAssignmentEscalation = def("role_assignment_escalation", 403, "That role confers authority you do not hold.") CodeRoleNotAssignable = def("role_not_assignable", 400, "The role cannot be assigned in this group.") CodeTwoFASetupCodeSendFailed = def("send_code_failed", 500, "The code could not be sent.") CodeSenderProofRequired = def("sender_proof_required", 401, "The token requires sender proof.") CodeServiceJWTLifetimeExceeded = def("service_jwt_lifetime_exceeded", 401, "The service token lifetime is too long.") CodeSessionCreationFailed = def("session_creation_failed", 500, "The session could not be created.") CodeSessionIssueFailed = def("session_issue_failed", 500, "The session could not be created.") CodeSIWSAddressMismatch = def("siws_address_mismatch", 400, "The wallet address does not match the challenge.") CodeSIWSChallengeExpired = def("siws_challenge_expired", 401, "The sign-in challenge has expired.") CodeSIWSChallengeMismatch = def("siws_challenge_mismatch", 401, "The sign-in challenge does not match.") CodeSIWSChallengeNotFound = def("siws_challenge_not_found", 401, "The sign-in challenge was not found.") CodeSIWSDomainInvalid = def("siws_domain_invalid", 401, "The sign-in domain is invalid.") CodeSIWSSignatureInvalid = def("siws_signature_invalid", 401, "The wallet signature is invalid.") CodeSIWSTimestampInvalid = def("siws_timestamp_invalid", 401, "The sign-in timestamp is invalid.") CodeSMSDeliveryFailed = def("sms_delivery_failed", 502, "The SMS could not be delivered.") CodeStateStoreFailed = def("state_store_failed", 500, "The login state could not be stored.") CodeStepUpFailed = def("step_up_failed", 500, "Step-up verification failed.") CodeStepUpRequired = def("step_up_required", 403, "Additional verification is required to continue.") CodeAccessTokenExpired = def("token_expired", 401, "The authentication token has expired.") CodeTokenIssueFailed = def("token_issue_failed", 500, "The token could not be issued.") CodeTokenNotYetValid = def("token_not_yet_valid", 401, "The token is not yet valid.") CodeAccessTokenRevoked = def("token_revoked", 401, "The access token has been revoked.") CodeTTLExceedsDelegateCertificate = def("ttl_exceeds_delegate_certificate", 400, "The TTL exceeds the delegate certificate.") CodeUnauthenticated = def("unauthenticated", 401, "Authentication is required.") CodeUnknownGroupPersona = def("unknown_group_persona", 400, "Unknown group persona.") CodeUnknownKID = def("unknown_kid", 401, "The token names an unknown key.") CodeUnknownProvider = def("unknown_provider", 400, "Unknown provider.") CodeUnknownRole = def("unknown_role", 400, "Unknown role.") CodeUnsupportedTokenTyp = def("unsupported_token_typ", 401, "The token type is not supported.") CodeUserBanned = def("user_banned", 401, "This account is banned.") CodeUserCreationFailed = def("user_creation_failed", 500, "The user could not be created.") CodeUserLookupFailed = def("user_lookup_failed", 500, "The user could not be loaded.") CodeUserNotFound = def("user_not_found", 404, "User not found.") CodeUserReferenced = def("user_referenced", 409, "The user is still referenced.") CodeUserRoleNotFound = def("user_role_not_found", 404, "The user does not hold that role.") CodeUsernameCannotContainAt = defParam("username_cannot_contain_at", 400, "username", "The username cannot contain @.") CodeUsernameCannotStartWithPlus = defParam("username_cannot_start_with_plus", 400, "username", "The username cannot start with +.") CodeUsernameInUse = def("username_in_use", 400, "That username is already in use.") CodeUsernameInvalidCharacters = defParam("username_invalid_characters", 400, "username", "The username contains invalid characters.") CodeUsernameMustStartWithLetter = defParam("username_must_start_with_letter", 400, "username", "The username must start with a letter.") CodeUsernameNotAllowed = defParam("username_not_allowed", 400, "username", "That username is not allowed.") CodeUsernameTooLong = defParam("username_too_long", 400, "username", "The username is too long.") CodeUsernameTooShort = defParam("username_too_short", 400, "username", "The username is too short.") CodeVerificationLinkExpired = def("verification_link_expired", 410, "The verification link has expired.") CodeVerificationRequestFailed = def("verification_request_failed", 500, "The verification could not be requested.") CodeVerificationRequired = def("verification_required", 403, "Verify your contact details to continue.") CodeWalletAlreadyLinked = def("wallet_already_linked", 409, "That wallet is already linked to another account.") CodeWalletChangeRequiresUnlink = def("wallet_change_requires_unlink", 409, "Unlink your current wallet before connecting another.") )
The catalog: every wire code with its HTTP status and message.
var ( ErrApplicationDocumentFetchFailed = E(CodeApplicationDocumentFetchFailed) ErrApplicationDocumentInvalid = E(CodeApplicationDocumentInvalid) ErrApplicationDomainConflict = E(CodeApplicationDomainConflict) ErrApplicationDomainInvalid = E(CodeApplicationDomainInvalid) ErrApplicationIssuerConflict = E(CodeApplicationIssuerConflict) ErrApplicationRegistrationDisabled = E(CodeApplicationRegistrationDisabled) ErrApplicationSlugConflict = E(CodeApplicationSlugConflict) ErrBootstrapDatabaseNotEmpty = E(CodeBootstrapDatabaseNotEmpty) ErrGroupSlugApplicationManaged = E(CodeGroupSlugApplicationManaged) ErrGroupSlugTaken = E(CodeGroupSlugTaken) ErrGroupSlugReserved = E(CodeGroupSlugReserved) ErrGroupSlugInvalid = E(CodeGroupSlugInvalid) ErrGroupCreationRefused = E(CodeGroupCreationRefused) ErrAvatarURLInvalid = E(CodeAvatarURLInvalid) ErrCannotRemoveLastAdminRole = E(CodeCannotRemoveLastAdminRole) ErrAccountRegistrationInviteConsumed = E(CodeAccountRegistrationInviteConsumed) ErrAccountRegistrationInviteExpired = E(CodeAccountRegistrationInviteExpired) ErrAccountRegistrationInviteNotFound = E(CodeAccountRegistrationInviteNotFound) ErrAccountRegistrationInviteRevoked = E(CodeAccountRegistrationInviteRevoked) ErrCustomRoleGrantCrossPersona = E(CodeCustomRoleGrantCrossPersona) ErrCustomRoleGrantOutsideCatalog = E(CodeCustomRoleGrantOutsideCatalog) ErrCustomRoleIsCatalogRole = E(CodeCustomRoleIsCatalogRole) ErrCustomRoleNameInvalid = E(CodeCustomRoleNameInvalid) ErrCustomRolesNotSupported = E(CodeCustomRolesNotSupported) ErrEmailAlreadyVerified = E(CodeEmailAlreadyVerified) ErrEmailDeliveryFailed = E(CodeEmailDeliveryFailed) ErrEmailInUse = E(CodeEmailInUse) ErrExternalInvitesDisabled = E(CodeExternalInvitesDisabled) ErrGroupNotFound = E(CodeGroupNotFound) ErrInsufficientRoleAuthority = E(CodeInsufficientRoleAuthority) ErrInvalidAttributeDef = E(CodeInvalidAttributeDef) ErrInvalidBootstrapManifest = E(CodeInvalidBootstrapManifest) ErrInvalidExpiry = E(CodeInvalidExpiry) ErrInvalidInvite = E(CodeInvalidInvite) ErrInvalidRole = E(CodeInvalidRole) ErrInvalidUntil = E(CodeInvalidUntil) ErrInviteLinkExpired = E(CodeInviteLinkExpired) ErrInviteLinkNotFound = E(CodeInviteLinkNotFound) ErrInviteLinkRevoked = E(CodeInviteLinkRevoked) ErrMissingName = E(CodeMissingName) ErrMissingSigner = E(CodeMissingSigner) ErrNotGroupMember = E(CodeNotGroupMember) ErrOwnerSlugTaken = E(CodeOwnerSlugTaken) ErrPasskeyCloneDetected = E(CodePasskeyCloneDetected) ErrPasskeyNotFound = E(CodePasskeyNotFound) ErrPasskeyUserVerificationRequired = E(CodePasskeyUserVerificationRequired) ErrPasswordlessDisabled = E(CodePasswordlessDisabled) ErrDeviceKeysDisabled = E(CodeDeviceKeysDisabled) ErrPasswordResetRequired = E(CodePasswordResetRequired) ErrPendingRegistrationNotFound = E(CodePendingRegistrationNotFound) ErrPhoneAlreadyVerified = E(CodePhoneAlreadyVerified) ErrPhoneInUse = E(CodePhoneInUse) ErrUsernameInUse = E(CodeUsernameInUse) ErrRegistrationDisabled = E(CodeRegistrationDisabled) ErrRemoteApplicationIssuerConflict = E(CodeRemoteApplicationIssuerConflict) ErrRemoteApplicationNotFound = E(CodeRemoteApplicationNotFound) ErrRenameRateLimited = E(CodeRenameRateLimited) ErrRenamesDisabled = E(CodeRenamesDisabled) ErrNameAdmissionRefused = E(CodeNameAdmissionRefused) ErrReservedIssuer = E(CodeReservedIssuer) ErrRoleAssignmentEscalation = E(CodeRoleAssignmentEscalation) ErrAccountAuthorityEscalation = E(CodeAccountAuthorityEscalation) ErrRoleNotAssignable = E(CodeRoleNotAssignable) ErrSMSDeliveryFailed = E(CodeSMSDeliveryFailed) ErrStepUpRequired = E(CodeStepUpRequired) ErrTwoFAFactorExists = E(CodeTwoFAFactorExists) ErrTwoFAEnrollmentRequired = E(CodeTwoFAEnrollmentRequired) ErrUnknownGroupPersona = E(CodeUnknownGroupPersona) ErrUnknownRole = E(CodeUnknownRole) ErrUserBanned = E(CodeUserBanned) ErrUserNotFound = E(CodeUserNotFound) ErrUserReferenced = E(CodeUserReferenced) ErrUserRoleNotFound = E(CodeUserRoleNotFound) ErrVerificationLinkExpired = E(CodeVerificationLinkExpired) ErrSIWSAddressMismatch = E(CodeSIWSAddressMismatch) ErrSIWSChallengeExpired = E(CodeSIWSChallengeExpired) ErrSIWSChallengeMismatch = E(CodeSIWSChallengeMismatch) ErrSIWSChallengeNotFound = E(CodeSIWSChallengeNotFound) ErrSIWSDomainInvalid = E(CodeSIWSDomainInvalid) ErrSIWSSignatureInvalid = E(CodeSIWSSignatureInvalid) ErrSIWSTimestampInvalid = E(CodeSIWSTimestampInvalid) ErrWalletAlreadyLinked = E(CodeWalletAlreadyLinked) ErrWalletChangeRequiresUnlink = E(CodeWalletChangeRequiresUnlink) ErrProviderAlreadyLinked = E(CodeProviderAlreadyLinked) ErrProviderChangeRequiresUnlink = E(CodeProviderChangeRequiresUnlink) ErrInvalidCredentials = E(CodeInvalidCredentials) ErrAccountExistsLinkRequired = E(CodeAccountExistsLinkRequired) ErrProviderLinkFailed = E(CodeProviderLinkFailed) ErrUserCreationFailed = E(CodeUserCreationFailed) ErrInvalidIdentifier = E(CodeInvalidIdentifier) ErrEmailVerificationSendFailed = E(CodeEmailVerificationSendFailed) ErrPhoneVerificationSendFailed = E(CodePhoneVerificationSendFailed) ErrTwoFASendFailed = E(CodeTwoFASendFailed) ErrTwoFAChallengeFailed = E(CodeTwoFAChallengeFailed) ErrSessionIssueFailed = E(CodeSessionIssueFailed) ErrInvalidTwoFAMethod = E(CodeInvalidTwoFAMethod) ErrPhoneNumberRequired = E(CodePhoneNumberRequired) ErrPhoneNumberMustBeE164 = E(CodePhoneNumberMustBeE164) ErrInvalidCode = E(CodeInvalidCode) ErrTwoFASetupCodeSendFailed = E(CodeTwoFASetupCodeSendFailed) ErrTwoFAEnableFailed = E(CodeTwoFAEnableFailed) ErrInternalError = E(CodeInternalError) ErrNotFound = E(CodeNotFound) ErrForbidden = E(CodeForbidden) ErrNotAuthenticated = E(CodeNotAuthenticated) ErrRateLimited = E(CodeRateLimited) ErrInvalidRequest = E(CodeInvalidRequest) )
Sentinels — the identities Go callers match with errors.Is.
var ErrAttributeDefNotFound = E(CodeAttributeDefNotFound)
ErrAttributeDefNotFound indicates no registered remote-application attribute definition matched.
var ErrDelegationRefused = E(CodeDelegationRefused)
ErrDelegationRefused is returned (or wrapped) by a DelegationAuthorizer to refuse a mint as a policy decision; any other error is an authorizer outage.
var ErrInvalidRemoteApplication = E(CodeInvalidRemoteApplication)
ErrInvalidRemoteApplication indicates a malformed remote_application registration payload.
var ErrInvalidServiceJWT = E(CodeInvalidServiceJWT)
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 DescribeCode ¶ added in v0.98.0
DescribeCode reports a code's catalog status and message.
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 LanguageFromContext ¶ added in v0.99.0
LanguageFromContext reads the request language attached by WithLanguage.
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 PublicDisplayName ¶ added in v0.92.0
func PublicDisplayName(refs map[string]PublicUserRef, id string) string
PublicDisplayName renders id's display name against a PublicUsersByIDs result, including for ids the batch did not resolve at all (never-existed accounts, which are absent from the map rather than tombstoned). It is the whole author-name branch a caller would otherwise write around every lookup.
func ValidRemoteApplicationIssuer ¶ added in v0.98.0
ValidRemoteApplicationIssuer reports whether iss has the shape every registered remote-application issuer has: an absolute http(s) URL with a host, at most MaxRemoteApplicationIssuerLen bytes, no whitespace or control characters. Registration enforces it; the verifier applies the same rule to a token's self-asserted `iss` before any store lookup.
func WithLanguage ¶ added in v0.99.0
WithLanguage attaches a request language to ctx.
func WriteError ¶ added in v0.98.0
func WriteError(w http.ResponseWriter, err error)
WriteError writes err as the canonical error envelope — the ONE writer behind authhttp and verify.
Types ¶
type APIKeyMintOptions ¶
type AccountRegistrationInvite ¶ added in v0.72.0
type AccountRegistrationInvite struct {
ID string
Email string
InvitedBy string
ExpiresAt time.Time
RevokedAt *time.Time
ConsumedAt *time.Time
ConsumedBy *string
// Persona/InstanceSlug/Role describe an OPTIONAL group role the code also grants
// on consume (#147 register+join). Empty for a plain registration invite.
Persona Persona
InstanceSlug string
Role Role
CreatedAt time.Time
UpdatedAt time.Time
}
type AccountRegistrationInviteCreated ¶ added in v0.72.0
type ActionAvailability ¶ added in v0.98.0
type ActionAvailability struct {
Action string `json:"action"`
Allowed bool `json:"allowed"`
Reason string `json:"reason,omitempty"`
RetryAfterSeconds int64 `json:"retry_after_seconds,omitempty"`
NextAllowedAt *time.Time `json:"next_allowed_at,omitempty"`
Limit *int `json:"limit,omitempty"`
Remaining *int `json:"remaining,omitempty"`
WindowSeconds *int64 `json:"window_seconds,omitempty"`
CooldownSeconds *int64 `json:"cooldown_seconds,omitempty"`
}
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"`
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"`
// PreferredLanguage carries the user's stored language preference through from
// the loaded user row, so callers (e.g. GET /me) need not issue a separate
// language read (#228). Omitted from JSON when unset.
PreferredLanguage *string `json:"preferred_language,omitempty"`
// AvatarURL is the host-supplied avatar URL/key string (#262).
AvatarURL *string `json:"avatar_url,omitempty"`
}
type AdminUserListOptions ¶
type AdminUserListOptions struct {
Page int
PageSize int
Search string // ILIKE over username/email/phone_number
Role Role // 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 ApplicationDocument ¶ added in v0.88.0
type ApplicationDocument struct {
// Slug is the REQUESTED handle — a free claim through the same
// availability + anti-squat gates as any org (slugs and domains are
// separate). Empty defaults to the serving domain's hostname.
Slug string `json:"slug"`
// DisplayName is free-form, non-unique metadata.
DisplayName string `json:"display_name,omitempty"`
// Issuer is the application's token `iss`; its host must be the serving
// domain outside dev-like environments.
Issuer string `json:"issuer"`
// JWKSURI XOR PublicKeys: exactly one trust source.
JWKSURI string `json:"jwks_uri,omitempty"`
PublicKeys []RemoteAppKey `json:"public_keys,omitempty"`
// DocumentEndpoint is the optional signed-document base URL.
DocumentEndpoint string `json:"document_endpoint,omitempty"`
}
ApplicationDocument is the well-known application.json a self-registering application serves at https://<domain>/.well-known/authkit/application.json. Unknown fields are ignored (forward-compatible).
type BootstrapManifest ¶
type BootstrapManifest struct {
Users []BootstrapManifestUser `json:"users" yaml:"users"`
RemoteApplications []BootstrapManifestRemoteApplication `json:"remote_applications" yaml:"remote_applications"`
// Dev carries dev-only runtime fixtures (#266). NOT part of the apply-once
// reconcile: hosts read it at every boot and honor it only in a dev
// environment (fail-closed).
Dev BootstrapManifestDev `json:"dev,omitempty" yaml:"dev,omitempty"`
}
type BootstrapManifestDev ¶ added in v0.89.0
type BootstrapManifestDev struct {
// StaticEntitlements are entitlement names seeded into every access token
// via a static EntitlementsProvider — billing/entitlement E2E fixtures as
// reviewable YAML (formerly the AUTHKIT_STATIC_ENTITLEMENTS env CSV, #266).
StaticEntitlements []string `json:"static_entitlements,omitempty" yaml:"static_entitlements,omitempty"`
}
BootstrapManifestDev is the dev-only fixture section of a bootstrap manifest.
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"`
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 ---
CreateUser(ctx context.Context, email, username string) (*User, error)
GetUserByEmail(ctx context.Context, email string) (*User, error)
GetUserByPhone(ctx context.Context, phone string) (*User, error)
GetUserByUsername(ctx context.Context, username string) (*User, error)
// {Hard,Soft}DeleteUsers are batch-native admin bulk mutations (#219/#222):
// per-item BEST-EFFORT — the returned OpResults pinpoint the failures; the
// outer error is a whole-call failure only (e.g. no store).
HardDeleteUsers(ctx context.Context, userIDs []string) ([]OpResult, error)
SoftDeleteUsers(ctx context.Context, userIDs []string) ([]OpResult, error)
MarkEmailVerified(ctx context.Context, id string) error
// UpdateAvatarURL sets (or clears, with nil) the user's avatar URL/key
// string (#262). Blob storage/validation is the host's job.
UpdateAvatarURL(ctx context.Context, id string, avatarURL *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)
// UsersByIDs resolves many user IDs to slim display projections in ONE
// query; missing IDs are absent. PRIVILEGED — the projection carries Email;
// render other users with PublicUsersByIDs.
UsersByIDs(ctx context.Context, ids []string) (map[string]UserRef, error)
// PublicUsersByIDs is the PUBLIC-SAFE twin (#268): no email; soft-deleted
// users come back as tombstones, banned users normally, unknown ids absent.
PublicUsersByIDs(ctx context.Context, ids []string) (map[string]PublicUserRef, error)
// UserLivenessByIDs is the batch account-liveness read behind verify's
// per-request liveness gate (#267). Errors PROPAGATE so authorization
// callers fail closed; unknown ids are absent and a gate treats that as a
// denial.
UserLivenessByIDs(ctx context.Context, ids []string) (map[string]UserLiveness, error)
UpsertPasswordHash(ctx context.Context, userID, hash, algo string, params []byte) error
// --- admin directory ---
AdminGetUser(ctx context.Context, id string) (*AdminUser, 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
// --- root roles (actor-checked; the unchecked genesis forms live on
// embedded.Client.Genesis(), #241) ---
// Assign/RemoveRolesBySlugAs are batch-native (#219/#222): the no-escalation
// check (#136) runs PER ITEM and each OpResult carries its own authority error.
AssignRolesBySlugAs(ctx context.Context, actorUserID string, userIDs []string, role Role) ([]OpResult, error)
RemoveRolesBySlugAs(ctx context.Context, actorUserID string, userIDs []string, role Role) ([]OpResult, error)
UpsertRoleBySlug(ctx context.Context, name string, role Role, description *string) error
// RoleSlugsByUsers returns each user's LIVE configured root role slugs in
// ONE call (#220); users with no roles are absent; errors PROPAGATE (#136).
RoleSlugsByUsers(ctx context.Context, userIDs []string) (map[string][]string, error)
// --- permission groups ---
CreatePermissionGroup(ctx context.Context, req CreatePermissionGroupRequest) (string, error)
EnsureRootGroup(ctx context.Context) (string, error)
SeedPermissionGroupContainment(ctx context.Context) error
ResolveGroupIDForSlug(ctx context.Context, group GroupRef) (string, error)
GroupInstanceForSlug(ctx context.Context, group GroupRef) (GroupInstance, error)
GroupInstanceByID(ctx context.Context, groupID string) (GroupInstance, error)
AssignGroupRoleAs(ctx context.Context, actorUserID string, group GroupRef, subject Subject, role Role) error
UnassignGroupRoleAs(ctx context.Context, actorUserID string, group GroupRef, subject Subject, role Role) error
RemoveGroupSubjectAs(ctx context.Context, actorUserID string, group GroupRef, subject Subject) error
ListGroupMembers(ctx context.Context, group GroupRef) ([]GroupMember, error)
ListSubjectGroups(ctx context.Context, subject Subject) ([]SubjectGroupMembership, error)
Can(ctx context.Context, subject Subject, group GroupRef, perm Perm) (bool, error)
CanOnGroup(ctx context.Context, subject Subject, groupID string, perm Perm) (bool, error)
ListEffectivePermissions(ctx context.Context, subject Subject, group GroupRef) ([]string, error)
CreateGroupInviteLink(ctx context.Context, req CreateGroupInviteLinkRequest) (GroupInviteLinkCreated, error)
ListGroupInviteLinks(ctx context.Context, group GroupRef) ([]GroupInviteLink, error)
RevokeGroupInviteLink(ctx context.Context, group GroupRef, linkID string) error
ExternalInvitesEnabled() bool
// --- tokens (#214: Mint* = signing a JWT; session creation is not a Mint) ---
MintAccessToken(ctx context.Context, userID string, extra map[string]any) (string, time.Time, error)
MintRemoteApplicationAccessToken(ctx context.Context, p RemoteApplicationAccessParams) (string, error)
MintServiceJWT(ctx context.Context, opts ServiceJWTMintOptions) (string, ServiceJWTClaims, error)
// --- API keys ---
MintAPIKeyWithOptions(ctx context.Context, group GroupRef, opts APIKeyMintOptions) (APIKey, string, error)
ListAPIKeys(ctx context.Context, group GroupRef) ([]APIKey, error)
RevokeAPIKey(ctx context.Context, group GroupRef, tokenID string) (bool, error)
ResolveAPIKey(ctx context.Context, keyID, secret string) (string, []string, error)
// --- identity providers ---
// ImportUnverifiedSolanaLinks preserves host migration associations without
// turning them into credentials; only a subsequent SIWS proof verifies a link.
ImportUnverifiedSolanaLinks(ctx context.Context, inputs []ImportUnverifiedSolanaLinkInput) (ImportUnverifiedSolanaLinksResult, error)
LinkProviderByIssuer(ctx context.Context, userID, issuer, providerSlug, subject string, email *string) error
// --- remote applications (federation issuers) ---
UpsertRemoteApplication(ctx context.Context, in RemoteApplication) (*RemoteApplication, error)
GetRemoteApplication(ctx context.Context, issuer string) (*RemoteApplication, error)
ResolveRemoteApplicationAuthority(ctx context.Context, appID string) (RemoteApplicationAuthority, error)
// --- bootstrap: hosts with a file load it themselves
// (embedded.LoadBootstrapManifestFile) then apply it ---
ApplyBootstrapManifest(ctx context.Context, manifest BootstrapManifest, opts BootstrapReconcileOptions) (BootstrapManifestResult, error)
// --- senders + upkeep ---
HasEmailSender() bool
HasSMSSender() bool
SMSAvailable() bool
CheckSMSHealth(ctx context.Context) error
CleanupExpiredAuthState(ctx context.Context) error
ValidateVerificationConfiguration() error
}
Client is the contract hosts hold: the in-process operations a host calls on the engine, one flat interface grounded in what the consumers actually use (ak#289). *embedded.Client implements it. Infra accessors (Postgres, JWKS, Config, Schema), the browser-flow methods the authhttp transport drives, the passkey ceremonies and the unchecked Genesis() seam are deliberately OFF this interface — they stay on the concrete *embedded.Client. Adding a method is MAJOR: consumers implement it in fakes.
c, err := embedded.New(cfg, deps) var _ authkit.Client = c
type CreateAccountRegistrationInviteRequest ¶ added in v0.72.0
type CreateAccountRegistrationInviteRequest struct {
Email string
InvitedBy string
ExpiresIn time.Duration
// Persona/InstanceSlug/Role, when all set, make this a register+join invite: the
// minted code ALSO grants the given role in that permission group on consume
// (#147). The minting actor must hold that group's members:manage (no-escalation);
// a role-carrying invite does NOT require general root:users:invite. Leave empty
// for a plain registration invite (root:users:invite gated).
Persona Persona
InstanceSlug string
Role Role
}
type CreatePermissionGroupRequest ¶
type CreatePermissionGroupRequest struct {
Persona Persona
InstanceSlug string
ParentPersona Persona
ParentInstanceSlug string
OwnerSubjectID string
// OwnerSubjectKind selects the owner principal kind: "user" (default) or
// "remote_application" (#264 service-owned orgs — an application principal
// owning its own permission group).
OwnerSubjectKind SubjectKind
// DisplayName is free-form, non-unique group metadata (#264 naming
// doctrine: vanity naming lives here, never on the slug).
DisplayName string
}
type CustomRoleDef ¶ added in v0.98.0
CustomRoleDef defines (or redefines) a per-group custom role: its grant patterns, all in the group's persona namespace, and whether holding it requires an enrolled second factor (mirrors RoleDef.RequiresMFA, #247).
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
// Documents becomes the top-level `documents` claim: versioned document
// type -> canonical sha256 digest. AuthKit transports and validates these
// references but does not resolve or interpret their payload schemas.
Documents map[string]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), `roles`
// (a uuid array; prefer the typed Roles field below), and `documents` (use the
// top-level Documents field above). 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 becomes the `jti` claim (token identifier). Optional to SET, but
// always PRESENT on the minted token: when empty the minter generates a
// fresh uuidv7, so a receiving service can revoke any delegated token by
// id without a per-issuer "does this one have a jti" carve-out.
JTI string
// NotBefore, when set, becomes the `nbf` claim. Optional.
NotBefore time.Time
// ConfirmationCertificateSHA256, when set, binds the token to the delegate's
// X.509 certificate as RFC 8705 `cnf.x5t#S256`; verification then requires
// that exact leaf as the TLS peer. Nil mints an unbound bearer token.
ConfirmationCertificateSHA256 *[32]byte
}
type DelegationAuthorizer ¶ added in v0.98.0
type DelegationAuthorizer func(context.Context, DelegationRequest) (DelegationGrant, error)
DelegationAuthorizer is the single host seam of the delegated mint route.
type DelegationGrant ¶ added in v0.98.0
type DelegationGrant struct {
Permissions []string
Attributes map[string]any
Documents map[string]string
}
DelegationGrant is the complete authority AuthKit signs for one request.
type DelegationRequest ¶ added in v0.98.0
type DelegationRequest struct {
UserID string
Audiences []string
TTL time.Duration
ConfirmationCertificateSHA256 [32]byte
DelegateCertificate *x509.Certificate
RequestedGrant json.RawMessage
}
DelegationRequest is what POST /delegated/token asks the host to authorize (ak#277). Audiences and TTL are already clamped; the certificate is parsed and validated; RequestedGrant is the client's opaque, host-schema object that AuthKit never copies into the token.
type DeletePermissionGroupOptions ¶ added in v0.88.0
type DeletePermissionGroupOptions struct {
ReleaseSlug bool
}
DeletePermissionGroupOptions controls the delete-time naming rule (#264): by DEFAULT a deleted group's slug is TOMBSTONED to its uuid forever (fail-safe — published references can never be re-claimed by someone else). ReleaseSlug frees the name (and drops the group's own tombstones) instead; that is safe ONLY for names nothing ever referenced, and the judgment is the host's: a released name re-created by a different owner is live and "live slugs win" in slug resolution, so any dangling published reference to the old group now resolves to the new owner (#308). authkit never deletes a group on its own.
type DocumentEnvelope ¶ added in v0.86.0
type DocumentReference ¶ added in v0.86.0
type Error ¶ added in v0.98.0
func E ¶ added in v0.98.0
func E(code Code, opts ...ErrorOption) *Error
E builds an Error for a catalogued code; the status comes from the catalog.
type ErrorEnvelope ¶
type ErrorEnvelope struct {
Error ErrorObject `json:"error"`
}
ErrorEnvelope is the top-level error response: {"error": {...}}.
func ErrorEnvelopeFor ¶ added in v0.98.0
func ErrorEnvelopeFor(err error) (int, ErrorEnvelope)
ErrorEnvelopeFor derives the wire status and envelope for err: an *Error keeps its status, code, param (the catalog's default when unset) and metadata; a plain 500 — and anything that is not an *Error — is emitted as internal_error, so the operation name never reaches the wire.
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 ErrorOption ¶ added in v0.98.0
func WithCause ¶ added in v0.98.0
func WithCause(cause error) ErrorOption
func WithMeta ¶ added in v0.98.0
func WithMeta(key string, value any) ErrorOption
func WithMetadata ¶ added in v0.98.0
func WithMetadata(m map[string]any) ErrorOption
func WithParam ¶ added in v0.98.0
func WithParam(param string) ErrorOption
func WithStatus ¶ added in v0.98.0
func WithStatus(status int) ErrorOption
type FormerNameRetentionConfig ¶ added in v0.98.0
type FormerNameRetentionConfig struct {
Mode FormerNameRetentionMode `json:"mode,omitempty" koanf:"mode"`
Duration *time.Duration `json:"duration,omitempty" koanf:"duration"`
}
type FormerNameRetentionMode ¶ added in v0.98.0
type FormerNameRetentionMode string
FormerNameRetentionMode controls reservation and forwarding after a rename.
const ( FormerNamesFinite FormerNameRetentionMode = "finite" FormerNamesForever FormerNameRetentionMode = "forever" FormerNamesImmediate FormerNameRetentionMode = "immediate" DefaultRenameInterval = 72 * time.Hour DefaultFormerNameRetention = 90 * 24 * time.Hour )
type GroupInstance ¶ added in v0.93.0
GroupInstance is one persona instance's own identity (#269): the addressing pair a caller already holds, plus the uuid a HOST needs to own rows about the group in its own (or a sibling service's) ledger — openrails' `customer_id` being the case that forced it. Group ids never appear in a PATH; this type is how a caller who already has authority over an instance LEARNS its id.
type GroupInstanceUpdate ¶ added in v0.98.0
type GroupInstanceUpdate struct {
Slug *string `json:"slug,omitempty"`
DisplayName *string `json:"display_name,omitempty"`
}
GroupInstanceUpdate changes group settings atomically against a captured UUID.
type GroupInviteLink ¶
type GroupInviteLink struct {
ID string
PermissionGroupID string
Role Role
InvitedBy string
// RedeemedAt is non-nil once the single-use link has been redeemed (#235;
// replaces the former Uses 0/1 counter).
RedeemedAt *time.Time
ExpiresAt *time.Time
RevokedAt *time.Time
CreatedAt time.Time
UpdatedAt time.Time
}
type GroupInviteLinkCreated ¶
type GroupMember ¶
type GroupMember struct {
SubjectID string
SubjectKind SubjectKind
Role Role
}
type GroupRef ¶ added in v0.98.0
GroupRef addresses one permission-group instance: the persona plus its instance slug. The root group has no instance.
type ImportUnverifiedSolanaLinkInput ¶ added in v0.97.1
type ImportUnverifiedSolanaLinkInput struct {
UserID string
Address string
Source string
SourceID string
SourceCreatedAt *time.Time
}
ImportUnverifiedSolanaLinkInput is a migration-only Solana identity claim. Importing reserves the address but does not make it a login method; the user must prove ownership through the normal SIWS flow before AuthKit trusts it.
type ImportUnverifiedSolanaLinkResult ¶ added in v0.97.1
type ImportUnverifiedSolanaLinkResult struct {
Index int
UserID string
Address string
Status ImportUnverifiedSolanaLinkStatus
Reason string
}
ImportUnverifiedSolanaLinkResult is the outcome for one input row.
type ImportUnverifiedSolanaLinkStatus ¶ added in v0.97.1
type ImportUnverifiedSolanaLinkStatus string
ImportUnverifiedSolanaLinkStatus is the per-row outcome of a legacy Solana identity import.
const ( ImportUnverifiedSolanaLinkInserted ImportUnverifiedSolanaLinkStatus = "inserted" ImportUnverifiedSolanaLinkSkipped ImportUnverifiedSolanaLinkStatus = "skipped" ImportUnverifiedSolanaLinkRejected ImportUnverifiedSolanaLinkStatus = "rejected" )
type ImportUnverifiedSolanaLinksResult ¶ added in v0.97.1
type ImportUnverifiedSolanaLinksResult struct {
Results []ImportUnverifiedSolanaLinkResult
Inserted int
Skipped int
Rejected int
}
ImportUnverifiedSolanaLinksResult aggregates per-row wallet import outcomes.
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 InstanceCreationDef ¶ added in v0.90.0
type InstanceCreationDef struct {
// Enabled mounts POST /<persona>. Off by default.
Enabled bool
// SlugPattern further restricts creatable slugs beyond the built-in
// instance-slug rule: an unanchored regexp source, anchored (^...$) at
// schema build. Empty = built-in rule only.
SlugPattern string
// ReservedSlugs are exact lowercase slugs creatable only by callers holding
// ReservedEscalationRole in the root group (a list is config, a route is not).
ReservedSlugs []string
// ReservedEscalationRole is the root-group role that may create reserved
// slugs. Empty = reserved slugs are not creatable through this route at all.
ReservedEscalationRole Role
}
InstanceCreationDef opts a persona into the generated creation route (#263): POST /<persona> creates an instance with the authenticated user seeded as its owner. Only root-parented personas may enable it. AuthKit owns the anti-squat velocity limits (per-IP + per-user); host COST gates plug in through WithInstanceAdmission. Zero value = no creation route (existing behavior).
type ListPage ¶ added in v0.98.0
type ListPage[T any] struct { Object string `json:"object"` Data []T `json:"data"` NextCursor string `json:"next_cursor,omitempty"` }
ListPage is the one list envelope: {object:"list", data:[...], next_cursor?}. A present next_cursor means another page exists; pass it back as ?cursor=.
func NewListPage ¶ added in v0.98.0
NewListPage wraps items (never null: an empty page marshals as []).
type NameAdmissionRequest ¶ added in v0.98.0
type NameAdmissionRequest struct {
OwnerKind string
Persona Persona
OwnerID string // Empty only before a new group/account is created.
ActorID string
CurrentName string
RequestedName string
Operation NameOperation
}
NameAdmissionRequest is the namespace admission hook's operation context. Group creation cost/enrollment hooks remain creation-only.
type NameOperation ¶ added in v0.98.0
type NameOperation string
const ( NameCreate NameOperation = "create" NameRename NameOperation = "rename" )
type NameResolution ¶ added in v0.98.0
type NameResolution struct {
ID string `json:"id"`
CanonicalName string `json:"canonical_name"`
IsAlias bool `json:"is_alias"`
AliasExpiresAt *time.Time `json:"alias_expires_at,omitempty"`
}
NameResolution always addresses one immutable owner. An alias points directly to that owner; CanonicalName reflects its current spelling, never an alias chain. AliasExpiresAt is nil for canonical names and permanent aliases; IsAlias tells them apart. Expired aliases are not resolutions.
type NamingConfig ¶ added in v0.98.0
type NamingConfig struct {
Enabled *bool `json:"enabled,omitempty" koanf:"enabled"`
RenameInterval *time.Duration `json:"rename_interval,omitempty" koanf:"rename_interval"`
FormerNames FormerNameRetentionConfig `json:"former_names,omitempty" koanf:"former_names"`
}
NamingConfig is deployment-wide policy for users and group instances. Pointers distinguish omission (defaults) from explicit false/zero. Durations use Go's time.Duration in embedded configuration; the standalone boundary parses text.
func (NamingConfig) Normalize ¶ added in v0.98.0
func (c NamingConfig) Normalize() (NamingPolicy, error)
Normalize validates once at the configuration boundary. An empty retention object or finite-without-duration selects 90 days. Duration-without-mode means finite; finite zero means immediate. Other modes cannot specify a duration.
type NamingPolicy ¶ added in v0.98.0
type NamingPolicy struct {
Enabled bool `json:"enabled"`
RenameInterval time.Duration `json:"rename_interval"`
FormerNameRetentionMode FormerNameRetentionMode `json:"former_name_retention_mode"`
FormerNameRetention time.Duration `json:"former_name_retention"`
}
NamingPolicy is the validated, immutable value used by both identity kinds. Duration values in JSON are nanoseconds, as for time.Duration in Go.
func (NamingPolicy) CheckRename ¶ added in v0.98.0
CheckRename is shared by user/group mutations under their owner lock. Callers authorize and detect a same-canonical-name no-op before checking this policy. Trusted import updates skip this check, never namespace ownership checks.
func (NamingPolicy) FormerNameExpiresAt ¶ added in v0.98.0
func (p NamingPolicy) FormerNameExpiresAt(now time.Time) *time.Time
FormerNameExpiresAt captures the promise made at rename time. Nil means forever. Immediate returns now: request-time lookup/claim use the same strict now.Before(deadline) boundary. Later policy changes never rewrite this value.
func (NamingPolicy) State ¶ added in v0.98.0
func (p NamingPolicy) State(last *time.Time, now time.Time) NamingState
type NamingState ¶ added in v0.98.0
type OpResult ¶ added in v0.80.0
OpResult is the per-item outcome of a batch mutation (#219/#222): batch writes return one OpResult per requested ID so partial failure is expressible — a bare single error on a bulk write would hide which item failed. Err == nil means the item succeeded.
As JSON, Err marshals as its wire code (#197), so errors.Is against authkit sentinels survives the round-trip; a non-Error (or a 500) degrades to internal_error.
func (OpResult) MarshalJSON ¶ added in v0.80.0
func (*OpResult) UnmarshalJSON ¶ added in v0.80.0
type PasswordlessStartResult ¶
type Perm ¶ added in v0.98.0
type Perm string
Perm is a concrete permission (`org:members:read`) or a grant pattern (`org:members:*`, `org:*`).
func (Perm) Matches ¶ added in v0.98.0
Matches reports whether grant authorizes this 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 the engine's RBAC checks and the verification layer's permission-coverage checks.
type Persona ¶ added in v0.98.0
type Persona string
Persona is a permission-group persona name (`root`, `org`, `merchant`): the first permission segment and the namespace every grant is anchored in.
func (Persona) OwnerGrant ¶ added in v0.98.0
OwnerGrant is the namespace-pure owner grant for a persona: `<persona>:*`.
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 PublicUserRef ¶ added in v0.92.0
type PublicUserRef struct {
ID string
// Username is "" when unset OR when the user is a tombstone — see Deleted.
// Prefer DisplayName over reading this directly.
Username string
AvatarURL string // "" if unset or tombstoned
CreatedAt time.Time
// Deleted marks a TOMBSTONE: the id resolved to a soft-deleted account, so
// the reference is not dangling — but every other field is zero, including
// CreatedAt. Nothing about a deleted account is published. Callers render
// DisplayName and show nothing else.
Deleted bool
}
PublicUserRef is the PUBLIC-SAFE batch user projection (#268): the display identity of a user as other users may see it. It deliberately has NO email field — the type, not an `omitempty` tag or a caller's discipline, is what makes it safe to nest inside a response body.
Every field here is public by nature: a username, an avatar and a join date. Derived assets (thumbnail sizes, CDN rewrites) stay host-owned — authkit stores one avatar string (#262) and does not know a host's image pipeline.
func (PublicUserRef) DisplayName ¶ added in v0.92.0
func (r PublicUserRef) DisplayName() string
DisplayName is the name to render for a user, with the fallback both consumer hosts had independently hand-rolled: the username when there is one, else a stable, non-identifying `user-<first 8 of id>`. Tombstoned and unnamed users take the fallback.
type RegisteredApplication ¶ added in v0.88.0
type RegisteredApplication struct {
Application RemoteApplication
OrgPersona Persona
OrgInstanceSlug string
// Created is false for an idempotent re-registration (the boot-time
// self-heal / rotation-from-root path).
Created bool
}
RegisteredApplication is the result of a (re-)registration: the application row plus its service-owned org (the permission group the application principal owns).
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 unbound account-registration
invite code.
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
// DisplayName is free-form, non-unique vanity metadata (#264). The slug is
// the public handle; the uuid is the internal join key.
DisplayName string
// Tier is the application's capability tier: ApplicationTierRegistered
// (self-registered; zero default capability — authenticate + documents
// only) or ApplicationTierApproved (an admin act on the host).
Tier string
// TrustRoot is what can rotate this application's keys (#264):
// ApplicationTrustRootManual (admin/bootstrap-managed),
// ApplicationTrustRootDomain (re-fetching Domain's application.json
// re-proves control and adopts current keys), or
// ApplicationTrustRootUser (the owning user's authenticated session).
// Never the keypair alone.
TrustRoot string
// Domain is the trust-root location for domain-rooted applications (the
// canonical registration input; empty otherwise). Domains and slugs are
// SEPARATE: the domain proves identity, the slug is a claimed handle.
Domain string
// DocumentEndpoint is the application's optional signed-document base URL
// declared in its application.json.
DocumentEndpoint string
// RootVerifiedAt is the last successful trust-root proof (zero when the
// root was never proven, e.g. manual registrations).
RootVerifiedAt time.Time
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 RemoteApplicationAuthority ¶ added in v0.83.0
type RemoteApplicationAuthority struct {
PermissionGroupID string
AuthorityIssuer string
Permissions []string
Persona Persona
InstanceSlug string
}
RemoteApplicationAuthority is a remote_application's STORED authority: its role-resolved effective permissions plus the owning permission-group INSTANCE they are bound to (#248). InstanceSlug is "" for singleton personas (root). Exact-instance binding only; descendant/walk-down authority is deliberately deferred.
type ResolvedAPIKey ¶
type ResolvedAPIKey struct {
APIKeyID string
KeyID string
// PermissionGroupID is the controlling permission-group id.
PermissionGroupID string
AuthorityIssuer string
// Persona / InstanceSlug identify the owning permission-group INSTANCE the
// key was minted on (#248). InstanceSlug is "" for singleton personas (root).
// The verify layer binds the key's token-carried permissions to this exact
// instance; descendant/walk-down authority is deliberately deferred.
Persona Persona
InstanceSlug string
Role Role
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 Role ¶ added in v0.98.0
type Role string
Role is a role slug in a persona's catalog (`owner`, `admin`) or a group's custom-role name.
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 SignedDocument ¶ added in v0.86.0
type SignedDocument = documents.SignedDocument
type SolanaLinkedAccount ¶ added in v0.98.0
type SolanaLinkedAccount struct {
Provider string `json:"provider"`
Issuer string `json:"issuer"`
Address string `json:"address"`
Verified bool `json:"verified"`
VerifiedAt *time.Time `json:"verified_at"`
PrimarySNSName *string `json:"primary_sns_name"`
SNSResolutionStatus string `json:"sns_resolution_status"`
SNSResolvedAt *time.Time `json:"sns_resolved_at"`
SNSStale bool `json:"sns_stale"`
SNSError *string `json:"sns_error"`
}
SolanaLinkedAccount is the AuthKit-owned normalized metadata for a SIWS-linked wallet.
type StepUpTwoFactorOption ¶ added in v0.98.0
type StepUpTwoFactorOptions ¶ added in v0.98.0
type StepUpTwoFactorOptions struct {
Methods []string `json:"methods,omitempty"`
DefaultMethod string `json:"default_method,omitempty"`
Options []StepUpTwoFactorOption `json:"options,omitempty"`
}
StepUpTwoFactorOptions lists the second factors a step-up can use.
type Subject ¶ added in v0.98.0
type Subject struct {
ID string
Kind SubjectKind
}
Subject is a principal that can hold roles in a permission group.
func RemoteAppSubject ¶ added in v0.98.0
func UserSubject ¶ added in v0.98.0
type SubjectGroupMembership ¶
type SubjectGroupMembership struct {
// GroupID is the instance's internal uuid (#269). It is a JOIN KEY, not an
// address — every route stays slug-addressed — and it is reported only for
// the caller's OWN memberships.
GroupID string
Persona Persona
InstanceSlug string
DisplayName string
Role Role
}
type SubjectKind ¶ added in v0.98.0
type SubjectKind string
SubjectKind discriminates who holds a role in a permission group.
type TokenSet ¶ added in v0.98.0
type TokenSet struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
ExpiresIn int64 `json:"expires_in"`
RefreshToken string `json:"refresh_token,omitempty"`
}
TokenSet is the one session-token envelope. A session-establishing route returns it as the whole body, or under "token_set" when the response says more (registration, step-up, device keys, SIWS/passwordless extras).
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
CreatedAt time.Time
UpdatedAt time.Time
LastLogin *time.Time
// PreferredLanguage is populated by the by-ID lookup (UserByID) only; other
// lookups leave it nil. Nullable — NULL/unset when the user has no stored
// language preference.
PreferredLanguage *string
// AvatarURL is the host-supplied avatar URL/key string (#262). Blob storage
// stays host-owned; authkit stores only this string. Populated by the by-ID
// lookup (UserByID) only, like PreferredLanguage.
AvatarURL *string
}
User is the public user view returned by AuthKit lookups. Plain data: see #138 (contract inversion) — definitions live here in the lean, pgx-free contract package; the embedded engine aliases back to these.
type UserLiveness ¶ added in v0.92.0
type UserLiveness struct {
ID string
// Allowed is the same account gate that guards token mint at login and
// refresh: false for deleted, banned and reserved accounts. An expired
// temporary ban is allowed (and cleared, exactly as the single-user gate
// clears it).
Allowed bool
Username string // "" if unset
Email string // "" if unset (phone-only accounts have none)
EmailVerified bool
AvatarURL string // "" if unset
}
UserLiveness is the per-request account-liveness verdict for one user, plus the identity fields that are fresh AS OF that same lookup (#267). It is what verify's liveness gate consumes, and what lets a host stop reaching for an admin-privileged read just to refresh a username or email onto a request.
The verdict is deliberately BOOLEAN: it does not report whether a denial was a ban, a deletion or a reservation. That distinction is an account-status question for an authenticated, entitled surface — not something an authentication gate should hand back to whoever presented the token.
type UserProfile ¶ added in v0.98.0
type UserProfile struct {
ID string `json:"id"`
Username string `json:"username"`
Email *string `json:"email"`
PhoneNumber *string `json:"phone_number"`
EmailVerified bool `json:"email_verified"`
PhoneVerified bool `json:"phone_verified"`
HasPassword bool `json:"has_password"`
DiscordUsername *string `json:"discord_username,omitempty"`
SolanaAddress *string `json:"solana_address,omitempty"`
SolanaLinkedAccount *SolanaLinkedAccount `json:"solana_linked_account,omitempty"`
LinkedProviders []string `json:"linked_providers,omitempty"`
EnabledProviders []string `json:"enabled_providers,omitempty"`
Roles []string `json:"roles"`
Entitlements []string `json:"entitlements"`
AvatarURL *string `json:"avatar_url,omitempty"`
UserAliases []string `json:"user_aliases,omitempty"`
PreferredLanguage *string `json:"preferred_language,omitempty"`
CreatedAt *string `json:"created_at,omitempty"`
Naming NamingState `json:"naming"`
Availability []ActionAvailability `json:"availability,omitempty"`
Security UserSecurity `json:"security"`
}
UserProfile is the caller's own account as GET /me returns it: identity, contact state, linked providers, roles/entitlements, naming state, cooldown-gated action availability, and the security view.
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.
It carries Email, so it is the PRIVILEGED batch projection: use it only where the caller is entitled to see addresses (admin surfaces, the account's own views). For anything rendered to other users — comment authors, gallery owners, public profiles — use PublicUserRef / Client.PublicUsersByIDs (#268), which has no email field at all.
type UserSecurity ¶ added in v0.98.0
type UserSecurity struct {
LastAuthenticatedAt *string `json:"last_authenticated_at,omitempty"`
TimeUntilStepUpRequired *int64 `json:"time_until_step_up_required,omitempty"`
StepUpRequiredForSensitiveActions bool `json:"step_up_required_for_sensitive_actions"`
StepUpMethods []string `json:"step_up_methods,omitempty"`
StepUp2FA *StepUpTwoFactorOptions `json:"step_up_2fa,omitempty"`
MFAEnabled bool `json:"mfa_enabled"`
MFASatisfied bool `json:"mfa_satisfied"`
MFAAllowedMethods []string `json:"mfa_allowed_methods,omitempty"`
}
UserSecurity is the session/step-up/MFA view of the caller's own account, nested under UserProfile.Security.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
adapters
|
|
|
twilio/internal/twiliocommon
Package twiliocommon holds the small helpers shared by AuthKit's Twilio email and SMS sender adapters (which are separate packages): request-language resolution, the app display label, and the default outbound HTTP client.
|
Package twiliocommon holds the small helpers shared by AuthKit's Twilio email and SMS sender adapters (which are separate packages): request-language resolution, the app display label, and the default outbound HTTP client. |
|
gin
module
|
|
|
riverjobs
module
|
|
|
Package authkitmigrate applies AuthKit's embedded Postgres migrations from host code, mirroring rivermigrate's shape:
|
Package authkitmigrate applies AuthKit's embedded Postgres migrations from host code, mirroring rivermigrate's shape: |
|
Package authprovider defines the external identity providers AuthKit's browser login flows delegate to.
|
Package authprovider defines the external identity providers AuthKit's browser login flows delegate to. |
|
Package testing provides utilities for testing applications that use authkit.
|
Package testing provides utilities for testing applications that use authkit. |
|
cmd
|
|
|
authkit-server
command
Command authkit-server is the dev/CI harness for AuthKit: the engine plus authhttp.MountHandler on one listener, with /healthz.
|
Command authkit-server is the dev/CI harness for AuthKit: the engine plus authhttp.MountHandler on one listener, with /healthz. |
|
Package documents defines AuthKit's generic immutable signed-document wire contract.
|
Package documents defines AuthKit's generic immutable signed-document wire contract. |
|
Package embedded is the AuthKit engine: the concrete *Client a host constructs with New and holds directly, and the authkit/authhttp transport mounts.
|
Package embedded is the AuthKit engine: the concrete *Client a host constructs with New and holds directly, and the authkit/authhttp transport mounts. |
|
internal
|
|
|
db
Schema indirection (authkit issue 69).
|
Schema indirection (authkit issue 69). |
|
errmodel
Package errmodel is the one error model every AuthKit package speaks (ak#290): a Code, an Error carrying that code plus its HTTP status, an optional offending param, machine metadata and a cause, and the catalog that fixes each code's status and message.
|
Package errmodel is the one error model every AuthKit package speaks (ak#290): a Code, an Error carrying that code plus its HTTP status, an optional offending param, machine metadata and a cause, and the catalog that fixes each code's status and message. |
|
netguard
Package netguard is the single outbound-network policy for AuthKit: the private/reserved address list, the resolve-then-dial SSRF guard, and the timeout-bounded HTTP client every package uses for fetches it does not fully control (JWKS, application documents, IdP endpoints).
|
Package netguard is the single outbound-network policy for AuthKit: the private/reserved address list, the resolve-then-dial SSRF guard, and the timeout-bounded HTTP client every package uses for fetches it does not fully control (JWKS, application documents, IdP endpoints). |
|
passkeytest
Package passkeytest is a software WebAuthn authenticator for passkey integration tests: it answers real registration and assertion ceremonies with a P-256 key, so tests exercise the production ceremony code paths.
|
Package passkeytest is a software WebAuthn authenticator for passkey integration tests: it answers real registration and assertion ceremonies with a P-256 key, so tests exercise the production ceremony code paths. |
|
siws
Package siws implements Sign In With Solana (SIWS) authentication.
|
Package siws implements Sign In With Solana (SIWS) authentication. |
|
testclock
Package testclock is a settable clock for tests that would otherwise sleep through a TTL, grace window or rate-limit window.
|
Package testclock is a settable clock for tests that would otherwise sleep through a TTL, grace window or rate-limit window. |
|
testdb
Package testdb owns AuthKit's Postgres integration-test harness.
|
Package testdb owns AuthKit's Postgres integration-test harness. |
|
migrations
|
|
|
postgres
Package migrations embeds AuthKit's Postgres schema migrations.
|
Package migrations embeds AuthKit's Postgres schema migrations. |
|
Package oidckit holds the browser-flow state shared by authhttp and its ephemeral stores: the pending-login record, its cache contract, and PKCE generation.
|
Package oidckit holds the browser-flow state shared by authhttp and its ephemeral stores: the pending-login record, its cache contract, and PKCE generation. |
|
memory
Package memorylimiter is the in-memory sliding-window rate limiter over ratelimit.Limit buckets.
|
Package memorylimiter is the in-memory sliding-window rate limiter over ratelimit.Limit buckets. |
|
redis
Package redislimiter is the Redis-backed sliding-window rate limiter over ratelimit.Limit buckets.
|
Package redislimiter is the Redis-backed sliding-window rate limiter over ratelimit.Limit buckets. |