Documentation
¶
Overview ¶
Package iam holds AuthKit's shared identity and access vocabulary: users, subjects, groups, roles and permissions, identities, remote applications, credential parsing, naming policy, and the one error catalog with its wire envelope. It depends only on the standard library, so the engine, the DB-less verify package and hosts can all share it.
A persona is a type of permission group (channel, org, merchant). A permission group is one instance of a persona, addressed by ID: it holds roles for an entity the host app owns (the channel /c/golang). root is the persona with exactly one group, the whole site. A permission is `<persona>:<resource>:<action>`; `*` may replace the action or everything after the persona.
Index ¶
- Constants
- Variables
- func APIKeyIdentity(apiKeyID string) auth.Identity
- func All[T any](list func(PageRequest) (ListPage[T], error)) iter.Seq2[T, error]
- func ApplicationIdentity(appID string) auth.Identity
- func DecodeError(resp *http.Response) error
- func InSession(id auth.Identity, r SessionRef) auth.Identity
- func PinnedTo(id auth.Identity, groupID string) auth.Identity
- func PublicDisplayName(users map[string]PublicUser, id string) string
- func SystemIdentity() auth.Identity
- func UserIdentity(userID string) auth.Identity
- func Within(id auth.Identity, perms ...Perm) auth.Identity
- func WriteError(w http.ResponseWriter, err error)
- type APIKey
- type APIKeyCreated
- type AccessTokenOptions
- type AccountSessionRevocation
- type AppRef
- type ApplicationTrustRoot
- type Ban
- type BanState
- type BootstrapManifest
- type BootstrapManifestRemoteApplication
- type BootstrapManifestUser
- type BootstrapOptions
- type BootstrapResult
- type BootstrapUserPassword
- type ContactChange
- type ContactField
- type CredentialState
- func (s CredentialState) Bounded() bool
- func (s CredentialState) CeilingCovers(perm Perm) bool
- func (s CredentialState) Group() string
- func (s CredentialState) ID() string
- func (s CredentialState) IsAPIKey() bool
- func (s CredentialState) IsApplication() bool
- func (s CredentialState) IsSystem() bool
- func (s CredentialState) IsUser() bool
- func (s CredentialState) IsZero() bool
- func (s CredentialState) Session() (SessionRef, bool)
- func (s CredentialState) String() string
- func (s CredentialState) SubjectKind() auth.SubjectKind
- type DeviceKey
- type DeviceKeyNotice
- type EmailMessage
- type Error
- type ErrorEnvelope
- type ErrorObject
- type Event
- type EventKind
- type Grant
- type Group
- type GroupMember
- type GroupQuery
- type GroupRef
- type HashAlgo
- type ImportConflict
- type ImportMatch
- type ImportOptions
- type ImportReason
- type ImportResult
- type ImportRow
- type ImportSolanaLink
- type ImportSolanaLinkRow
- type ImportSolanaLinksResult
- type ImportStatus
- type ImportUser
- type Invitation
- type InvitationCreated
- type JWK
- type ListPage
- type MemberQuery
- type Membership
- type MessageKind
- type NameAdmissionRequest
- type NameOperation
- type NameResolution
- type NewAPIKey
- type NewGroup
- type NewInvitation
- type NewUser
- type OAuthAssertion
- type OAuthCapability
- type OAuthGrantAuthorizer
- type OAuthGrantDecision
- type OAuthGrantKind
- type OAuthGrantRequest
- type OpResult
- type PageRequest
- type Passkey
- type PasswordHash
- type Perm
- type Persona
- type ProviderLink
- type ProvisioningTarget
- type PublicUser
- type RegistrationMode
- type RegistrationVerificationPolicy
- type RemoteApplication
- type RemoteApplicationKey
- type RemoteApplicationMode
- type ResolvedAPIKey
- type Role
- type Route
- type RouteAuthTier
- type RouteGroup
- type SMSMessage
- type Session
- type SessionEvent
- type SessionEventKind
- type SessionEventQuery
- type SessionRef
- type SolanaNetwork
- type Subject
- type SubjectKind
- type Token
- type TokenSet
- type TwoFactorMethod
- type TwoFactorMode
- type User
- type UserDeletion
- type UserEntry
- type UserKey
- type UserQuery
- type UserRef
- type UserSort
- type UserStatus
- type UserUpdate
- type VerificationPurpose
Constants ¶
const ( AssuranceLevelPassword = "urn:authkit:loa:1" AssuranceLevelMFA = "urn:authkit:loa:2" )
const ( OpenIDConfigurationPath = "/.well-known/openid-configuration" AuthorizationServerMetadataPath = "/.well-known/oauth-authorization-server" OAuthAuthorizePath = "/oauth2/authorize" OAuthTokenPath = "/oauth2/token" OAuthUserInfoPath = "/oauth2/userinfo" OAuthRevocationPath = "/oauth2/revoke" OAuthEndSessionPath = "/oauth2/end_session" )
The authorization server's endpoints beneath the issuer's path, as its metadata (OpenIDConfigurationPath) advertises them.
const ( // RefreshCookieName is the refresh cookie on HTTPS deployments. Browsers // accept a __Host- cookie only when it is Secure, host-only and Path=/, so // a sibling subdomain can neither plant nor shadow it. RefreshCookieName = "__Host-authkit_rt" // InsecureRefreshCookieName is used only on plain-HTTP deployments (local // development), where browsers refuse __Host- cookies. InsecureRefreshCookieName = "authkit_rt" )
const ( DefaultPageLimit = 50 MaxPageLimit = 500 )
DefaultPageLimit and MaxPageLimit bound PageRequest.Limit.
const CredentialSystem auth.CredentialKind = "system"
CredentialSystem is AuthKit's credential for the host's own code: SystemIdentity, and what UserIdentity and ApplicationIdentity assert.
const JWKSPath = "/.well-known/jwks.json"
JWKSPath serves the issuer's public signing keys beneath the issuer's path (the mount's BasePath), so verifiers derive it: issuer + JWKSPath.
const MaxBatch = 500
MaxBatch is the most ids (users, groups, subjects) one AuthKit query reads. Batch reads take any number and read them MaxBatch at a time.
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.
const UserRecoveryPeriod = 30 * 24 * time.Hour
UserRecoveryPeriod is the fixed interval in which an accepted account deletion can be restored. Repeated deletion does not extend it.
Variables ¶
var ErrUnknownPermission = errors.New("iam: unknown permission")
ErrUnknownPermission reports a permission no persona catalog registers. It is a programming error, so it has no wire code.
Functions ¶
func APIKeyIdentity ¶ added in v1.7.0
APIKeyIdentity is your code acting as an API key (APIKey.ID, verify Claims.APIKeyID), with the key's live authority. Its Subject, the key's group, is resolved per operation.
func All ¶ added in v1.1.0
All yields every item of a paged list, reading MaxPageLimit at a time: list reads the page it is given. The first error ends it, yielded with a zero item.
for m, err := range iam.All(func(p iam.PageRequest) (iam.ListPage[iam.Membership], error) {
return client.ListMemberships(ctx, subject, p)
}) {
func ApplicationIdentity ¶ added in v1.7.0
ApplicationIdentity is your code acting as a registered remote application.
func DecodeError ¶
DecodeError is WriteError's inverse for an HTTP client of AuthKit: nil for a 2xx response, else the Error the response carries, with the response's status and the envelope's code, message, param and metadata. errors.Is matches it against the Err* sentinels by code. A body that is not an AuthKit envelope (a proxy's error page, an unmounted route) decodes to an Error with the status and an empty code. DecodeError reads the body but does not close it.
func InSession ¶ added in v1.7.0
func InSession(id auth.Identity, r SessionRef) auth.Identity
InSession binds a user's identity to the sign-in its token was minted from. Every authority check then also requires that session or device key to be active, in the same query as the account check, and refuses a revoked one with ErrSessionRevoked. verify binds every identity it builds from an AuthKit user token; an unbound one (UserIdentity in trusted server code) is checked at account level only. The zero ref leaves id unchanged; any other identity, or a ref naming both, is the zero Identity.
func PinnedTo ¶ added in v1.7.0
PinnedTo narrows id to the group groupID: an identity pinned to another group, the system's or one without AuthKit's state is the zero Identity.
func PublicDisplayName ¶
func PublicDisplayName(users map[string]PublicUser, id string) string
PublicDisplayName renders id against a PublicUsers result, including ids the batch did not resolve.
func SystemIdentity ¶ added in v1.7.0
SystemIdentity is your application's own code acting, with no user: trusted host authority. It skips authority rules but never invariants (last owner, MFA-required roles). Never derive it from request input.
func UserIdentity ¶ added in v1.7.0
UserIdentity is your code acting as a native user, checked at account level: no sign-in binds it, unlike a verified user's token.
func Within ¶ added in v1.7.0
Within narrows id to permissions covered by perms (an intersection with any existing ceiling). The system's or an identity without AuthKit's state is the zero Identity.
func WriteError ¶
func WriteError(w http.ResponseWriter, err error)
WriteError writes err as the error envelope with the catalog's status for its code. Anything that is not an AuthKit error, and every server failure, is written as 500 internal_error. A 401 step_up_required also carries RFC 9470's challenge, `Bearer error="insufficient_user_authentication", max_age="900"`, unless the response already has a WWW-Authenticate.
Types ¶
type APIKey ¶
type APIKey struct {
ID string `json:"id"` // the key's id: APIKeyIdentity(ID), verify Claims.APIKeyID
LookupID string `json:"lookup_id"` // the public lookup id embedded in the token
GroupID string `json:"group_id"`
Name string `json:"name"`
Role Role `json:"role"`
Permissions []Perm `json:"permissions"`
CreatedBy *string `json:"created_by"` // nil = issued by the system
CreatedAt time.Time `json:"created_at"`
LastUsedAt *time.Time `json:"last_used_at"`
ExpiresAt *time.Time `json:"expires_at"`
RevokedAt *time.Time `json:"revoked_at"`
}
APIKey is an API key's metadata; the secret is shown only by CreateAPIKey. A key holds one role of its group; Permissions is that role resolved now, so editing the role changes every key holding it.
type APIKeyCreated ¶
APIKeyCreated is a new key and its token, shown this once.
type AccessTokenOptions ¶
type AccessTokenOptions struct {
SessionID string
TTL time.Duration // 0 = the configured access-token lifetime
Claims map[string]any
}
AccessTokenOptions shapes a host-minted access token. Claims are the host's own: one named like an AuthKit claim is refused.
type AccountSessionRevocation ¶
type AccountSessionRevocation struct {
// Issuers is the exact issuer scope covered, this deployment's first.
Issuers []string `json:"issuers"`
// RevokedSessions counts revoked refresh sessions per covered issuer.
RevokedSessions map[string]int `json:"revoked_sessions"`
// RevokedDeviceKeys counts revoked device keys; they are not issuer-bound.
RevokedDeviceKeys int `json:"revoked_device_keys"`
// UnlistedIssuerSessions counts live sessions left under issuers outside
// Issuers; nonzero means the account issuer configuration is incomplete.
UnlistedIssuerSessions int `json:"unlisted_issuer_sessions"`
}
AccountSessionRevocation reports an account-wide emergency revocation across the configured account issuers (TokenConfig.AccountIssuers). Access tokens minted from the revoked sessions and device keys are refused at once by every session check (permission checks, Sensitive, account changes); plain stateless verification admits them until they expire.
type AppRef ¶
type AppRef struct {
// contains filtered or unexported fields
}
AppRef addresses one remote application: by id or by issuer. Build it with AppByID or AppByIssuer; the zero AppRef finds nothing.
func AppByIssuer ¶
type ApplicationTrustRoot ¶
type ApplicationTrustRoot string
ApplicationTrustRoot is the authority that changes an application's keys.
const ( ApplicationTrustRootManual ApplicationTrustRoot = "manual" ApplicationTrustRootUser ApplicationTrustRoot = "user" )
type Ban ¶
Ban bans an account. Until nil bans indefinitely. KeepExisting leaves a ban already in force unchanged, so a repeat call is a no-op.
type BanState ¶
type BanState struct {
At time.Time `json:"at" yaml:"at"`
Until *time.Time `json:"until" yaml:"until"`
Reason *string `json:"reason" yaml:"reason"`
By *string `json:"by" yaml:"by"`
}
BanState is a stored ban: when it began, until when (nil = indefinitely), why, and the account that banned (By, nil for the system or a machine).
type BootstrapManifest ¶
type BootstrapManifest struct {
Users []BootstrapManifestUser `json:"users" yaml:"users"`
RemoteApplications []BootstrapManifestRemoteApplication `json:"remote_applications" yaml:"remote_applications"`
}
BootstrapManifest is genesis seed data: accounts, their root roles and remote applications. ApplyBootstrapManifest applies it as a host operation; authkit.ParseBootstrapManifestYAML reads one from its YAML file format, whose keys are the yaml tags below.
type BootstrapManifestRemoteApplication ¶
type BootstrapManifestRemoteApplication struct {
Issuer string `json:"issuer" yaml:"issuer"`
JWKSURI string `json:"jwks_uri" yaml:"jwks_uri"`
PublicKeys []RemoteApplicationKey `json:"public_keys" yaml:"public_keys"`
Enabled *bool `json:"enabled" yaml:"enabled"`
RootRole Role `json:"root_role" yaml:"root_role"`
}
BootstrapManifestRemoteApplication seeds one application controlled by root.
type BootstrapManifestUser ¶
type BootstrapManifestUser struct {
Username string `json:"username" yaml:"username"`
Email string `json:"email" yaml:"email"`
Phone string `json:"phone_number" yaml:"phone_number"`
EmailVerified bool `json:"email_verified" yaml:"email_verified"`
PhoneVerified bool `json:"phone_verified" yaml:"phone_verified"`
// Ban bans a new account from now: only Until and Reason may be set.
Ban *BanState `json:"ban" yaml:"ban"`
// PublicMetadata is a new account's public metadata (iam.PublicUser).
PublicMetadata map[string]any `json:"public_metadata" yaml:"public_metadata"`
Password *BootstrapUserPassword `json:"password" yaml:"password"`
// RootRole is the account's root role (`root:admin`). The root owner role
// is seeded only while the root group has no usable owner.
RootRole Role `json:"root_role" yaml:"root_role"`
}
BootstrapManifestUser seeds one account. A new account is created as declared. An existing account is used only when the email or phone the manifest names is verified on it, and its identity, contacts, ban and public metadata are left as they are. An account found any other way (unverified contact, or username when no contact is named) is refused unless the apply would change nothing on it.
type BootstrapOptions ¶
type BootstrapOptions struct {
// DryRun validates and counts without writing.
DryRun bool
// StartupOnly applies the manifest at most once per database schema; leave
// it false for host or CLI applies.
StartupOnly bool
// Name labels the StartupOnly receipt ("" is "default"). Another name does
// not rerun genesis.
Name string
}
BootstrapOptions tunes ApplyBootstrapManifest.
type BootstrapResult ¶
type BootstrapResult struct {
DryRun bool `json:"dry_run"`
AlreadyApplied bool `json:"already_applied"`
UsersCreated int `json:"users_created"`
UsersMatched int `json:"users_matched"`
PasswordsSet int `json:"passwords_set"`
PasswordsKept int `json:"passwords_kept"`
RootRoleAssignments int `json:"root_role_assignments"`
RemoteApplications int `json:"remote_applications"`
RemoteApplicationRootRoles int `json:"remote_application_root_roles"`
}
BootstrapResult counts what an apply did.
type BootstrapUserPassword ¶
type BootstrapUserPassword struct {
Plaintext string `json:"plaintext" yaml:"plaintext"`
PasswordHash `yaml:",inline"`
// ResetRequired stores a password that never verifies: the account must
// reset it.
ResetRequired bool `json:"reset_required" yaml:"reset_required"`
// Enforce re-asserts the password on every apply. Without it the password
// is set only when the account is created, so a rotated password is never
// reverted. It cannot be combined with ResetRequired.
Enforce bool `json:"enforce" yaml:"enforce"`
}
BootstrapUserPassword is exactly one of Plaintext, a PasswordHash, or ResetRequired.
type ContactChange ¶
type ContactChange struct {
Field ContactField
// NewValue is the replacement address as stored.
NewValue string
}
ContactChange is delivered to the PREVIOUS address after a recovery identifier (email or phone) was replaced, so a hijacked change is visible to the account's real owner.
type ContactField ¶
type ContactField string
ContactField names a recovery identifier.
const ( ContactEmail ContactField = "email" ContactPhone ContactField = "phone" )
type CredentialState ¶ added in v1.7.0
type CredentialState struct {
// contains filtered or unexported fields
}
CredentialState is AuthKit's record of what an Identity's credential may do: the account it acts as, the sign-in it stays bound to, the ceilings and the group that narrow it. Its fields are unexported, so nothing outside AuthKit builds one; the zero CredentialState grants nothing.
func StateOf ¶ added in v1.7.0
func StateOf(id auth.Identity) (CredentialState, bool)
StateOf is AuthKit's state of id's credential; false when it has none.
func (CredentialState) Bounded ¶ added in v1.7.0
func (s CredentialState) Bounded() bool
Bounded reports whether a ceiling narrows s.
func (CredentialState) CeilingCovers ¶ added in v1.7.0
func (s CredentialState) CeilingCovers(perm Perm) bool
CeilingCovers reports whether every ceiling permits perm (true when unbounded).
func (CredentialState) Group ¶ added in v1.7.0
func (s CredentialState) Group() string
Group is the group s is pinned to (PinnedTo); "" when unpinned.
func (CredentialState) ID ¶ added in v1.7.0
func (s CredentialState) ID() string
ID is the user's or application's id, or the API key's; "" for the system.
func (CredentialState) IsAPIKey ¶ added in v1.7.0
func (s CredentialState) IsAPIKey() bool
IsAPIKey reports whether s is an API key's.
func (CredentialState) IsApplication ¶ added in v1.7.0
func (s CredentialState) IsApplication() bool
IsApplication reports whether s acts as a registered application.
func (CredentialState) IsSystem ¶ added in v1.7.0
func (s CredentialState) IsSystem() bool
IsSystem reports whether s is SystemIdentity's: your own code, with host authority.
func (CredentialState) IsUser ¶ added in v1.7.0
func (s CredentialState) IsUser() bool
IsUser reports whether s acts as a user.
func (CredentialState) IsZero ¶ added in v1.7.0
func (s CredentialState) IsZero() bool
IsZero reports whether s grants nothing: the zero CredentialState.
func (CredentialState) Session ¶ added in v1.7.0
func (s CredentialState) Session() (SessionRef, bool)
Session is the sign-in s is bound to (InSession).
func (CredentialState) String ¶ added in v1.7.0
func (s CredentialState) String() string
String is "<subject kind>:<id>" ("api_key:<id>" for a key, "system"), for logs.
func (CredentialState) SubjectKind ¶ added in v1.7.0
func (s CredentialState) SubjectKind() auth.SubjectKind
SubjectKind is the kind of account s acts as; "" for the system.
type DeviceKey ¶
type DeviceKey struct {
ID string `json:"id"`
Label *string `json:"label"`
PublicKey ed25519.PublicKey `json:"public_key"`
CreatedAt time.Time `json:"created_at"`
LastUsedAt *time.Time `json:"last_used_at"`
RevokedAt *time.Time `json:"revoked_at"`
Current bool `json:"current"`
}
DeviceKey is an Ed25519 key a native client signs in with. Current marks the key behind the access token of the request that listed it.
type DeviceKeyNotice ¶
DeviceKeyNotice describes a native-client device key just enrolled on an EXISTING account, so a key added through a compromised mailbox is visible to the account's real owner.
type EmailMessage ¶
type EmailMessage struct {
Kind MessageKind
To string
// Username is the account's username, "" when it has none.
Username string
// Language is a two-letter code: the account's preferred language, else
// the request's, else the configured default.
Language string
Code string
Link string
Purpose VerificationPurpose
ContactChange *ContactChange
DeviceKey *DeviceKeyNotice
}
EmailMessage is one email AuthKit asks Deps.Email to deliver. Fields a kind does not use are empty.
type Error ¶
type Error interface {
error
// Code is the stable snake_case wire code (internal_error for a server
// failure; empty for a decoded response that is not an AuthKit error).
Code() string
// Status is the HTTP status the catalog fixes for the code.
Status() int
// Param names the offending request field, when there is one.
Param() string
// Metadata is machine-readable context, such as next_rename_at.
Metadata() map[string]any
}
Error is an AuthKit error as the wire sees it. Every error AuthKit returns carries one, as does every error DecodeError reads from a response; match identities with errors.Is against the sentinels below.
var ( // ErrAPIKeyInvalid indicates an API key that is malformed, does not exist, // has a bad secret, or whose owning permission group is gone: one answer so // callers learn nothing from the error. ErrAPIKeyInvalid Error = errmodel.E(errmodel.CodeAPIKeyInvalid) // ErrAPIKeyRevoked indicates the API key was revoked, or its creator // can no longer act (banned or deleted). ErrAPIKeyRevoked Error = errmodel.E(errmodel.CodeAPIKeyRevoked) // ErrAPIKeyExpired indicates the API key is past its expires_at. ErrAPIKeyExpired Error = errmodel.E(errmodel.CodeAPIKeyExpired) // ErrAPIKeyNotFound indicates no key with the id exists in the group. ErrAPIKeyNotFound Error = errmodel.E(errmodel.CodeAPIKeyNotFound) )
var ( ErrUserNotFound Error = errmodel.E(errmodel.CodeUserNotFound) ErrGroupNotFound Error = errmodel.E(errmodel.CodeGroupNotFound) ErrRemoteApplicationNotFound Error = errmodel.E(errmodel.CodeRemoteApplicationNotFound) )
Lookup.
var ( ErrInsufficientAuthority Error = errmodel.E(errmodel.CodeInsufficientAuthority) ErrRoleAssignmentEscalation Error = errmodel.E(errmodel.CodeRoleAssignmentEscalation) ErrAccountAuthorityEscalation Error = errmodel.E(errmodel.CodeAccountAuthorityEscalation) ErrLastOwner Error = errmodel.E(errmodel.CodeLastOwner) ErrCannotTargetSelf Error = errmodel.E(errmodel.CodeCannotTargetSelf) ErrTwoFAEnrollmentRequired Error = errmodel.E(errmodel.CodeTwoFAEnrollmentRequired) // ErrSubjectMFARequired refuses a role that needs MFA for an account with // no usable second factor. ErrSubjectMFARequired Error = errmodel.E(errmodel.CodeSubjectMFARequired) // ErrSessionRevoked refuses an identity bound to a session or device key // (InSession) that was revoked or expired, as logout, revoke-all, a // password change, a ban and deletion all do. ErrSessionRevoked Error = errmodel.E(errmodel.CodeSessionRevoked) // ErrTokenExpired refuses a token past its exp (beyond the verifier's // clock skew). ErrTokenExpired Error = errmodel.E(errmodel.CodeTokenExpired) )
Authority.
var ( ErrRenameRateLimited Error = errmodel.E(errmodel.CodeRenameRateLimited) ErrRenamesDisabled Error = errmodel.E(errmodel.CodeRenamesDisabled) ErrRoleNotAssignable Error = errmodel.E(errmodel.CodeRoleNotAssignable) ErrUnknownGroupPersona Error = errmodel.E(errmodel.CodeUnknownGroupPersona) ErrExternalInvitesDisabled Error = errmodel.E(errmodel.CodeExternalInvitesDisabled) // ErrInvitationsDisabled: Config.Invitations.Disabled is set. ErrInvitationsDisabled Error = errmodel.E(errmodel.CodeInvitationsDisabled) )
Groups and naming.
var ( ErrEmailInUse Error = errmodel.E(errmodel.CodeEmailInUse) ErrUsernameInUse Error = errmodel.E(errmodel.CodeUsernameInUse) ErrPhoneInUse Error = errmodel.E(errmodel.CodePhoneInUse) ErrInvalidUntil Error = errmodel.E(errmodel.CodeInvalidUntil) ErrAccountRecoveryExpired Error = errmodel.E(errmodel.CodeAccountRecoveryExpired) ErrContactNotVerified Error = errmodel.E(errmodel.CodeContactNotVerified) // host set no Deps.EntitlementHolders. ErrEntitlementFilterUnavailable Error = errmodel.E(errmodel.CodeEntitlementFilterUnavailable) )
Users.
var ( // ErrTooManyAccounts refuses another account from a device past // AccountsPerDevice (or AccountsPerAddress). ErrTooManyAccounts Error = errmodel.ErrTooManyAccounts // ErrTooManyDevices refuses a new device past NewDevicesPerAccount when // the account has no proven email or phone to send it a code. ErrTooManyDevices Error = errmodel.ErrTooManyDevices )
Sign-in limits (Config.SignIn), both 429 with Retry-After.
var ( ErrSigningNotConfigured Error = errmodel.Internal("signing_not_configured", nil) ErrDeviceKeysDisabled Error = errmodel.E(errmodel.CodeDeviceKeysDisabled) ErrRemoteApplicationIssuerConflict Error = errmodel.E(errmodel.CodeRemoteApplicationIssuerConflict) ErrReservedIssuer Error = errmodel.E(errmodel.CodeReservedIssuer) )
Credentials and applications (the API-key and remote-application sentinels live beside their types).
var ErrGroupConflict Error = errmodel.E(errmodel.CodeGroupConflict)
ErrGroupConflict refuses to create a group whose id is taken by a deleted group or a group of another persona.
var ErrInvalidRemoteApplication Error = errmodel.E(errmodel.CodeInvalidRemoteApplication)
ErrInvalidRemoteApplication indicates a malformed remote_application registration payload.
var ErrInvitationNotFound Error = errmodel.E(errmodel.CodeInvitationNotFound)
ErrInvitationNotFound indicates no invitation with the id or code exists in the group, or none the caller may redeem.
var ErrOAuthGrantRefused Error = errmodel.E(errmodel.CodeOAuthGrantRefused)
ErrOAuthGrantRefused is returned (or wrapped) by an OAuthGrantAuthorizer to refuse a grant as a policy decision; any other error is an outage, and the request fails without spending the capability.
type ErrorEnvelope ¶
type ErrorEnvelope struct {
Error ErrorObject `json:"error"`
}
ErrorEnvelope is every AuthKit error response: {"error": {...}}.
func ErrorResponse ¶
func ErrorResponse(err error) (int, ErrorEnvelope)
ErrorResponse is WriteError's status and envelope, for any router: Gin `c.JSON(iam.ErrorResponse(err))`, Fiber `status, body := iam.ErrorResponse(err); return c.Status(status).JSON(body)`.
type ErrorObject ¶
type ErrorObject struct {
Type string `json:"type"`
Code string `json:"code"`
Message string `json:"message"`
Param *string `json:"param"`
Metadata map[string]any `json:"metadata"`
}
ErrorObject is the error detail under the envelope's "error" key: a stable code, its type category (derived from the status), a human-readable message, the offending request field (null for none) and machine-readable metadata (null for none).
type Event ¶
type Event struct {
// ID is the same on every delivery: the host's idempotency key.
ID string `json:"id"`
Kind EventKind `json:"kind"`
OccurredAt time.Time `json:"occurred_at"`
// Who made the change (docs/identity.md): the account whose authority
// it used (SubjectKind, SubjectID), who acted (InvokerIssuer, InvokerID)
// and how it was proven (CredentialKind, CredentialID). Your own code
// is CredentialKind "system" with no subject. All are empty for a change
// AuthKit made on its own: the end of a recovery window, or a role
// retired with its grantor's cover.
SubjectKind auth.SubjectKind `json:"subject_kind"`
SubjectID string `json:"subject_id"`
InvokerIssuer string `json:"invoker_issuer"`
InvokerID string `json:"invoker_id"`
CredentialKind auth.CredentialKind `json:"credential_kind"`
CredentialID string `json:"credential_id"`
// UserID is the account of a user event and the user subject of a role
// event.
UserID string `json:"user_id"`
// GroupID and Persona name the group of a group or role event.
GroupID string `json:"group_id"`
Persona Persona `json:"persona"`
// ApplicationID is the remote-application subject of a role event.
ApplicationID string `json:"application_id"`
// Previous and Current are the changed value before and after: the
// email, phone or username, or the role (`channel:moderator`); "" when
// none.
Previous string `json:"previous"`
Current string `json:"current"`
// Reason and Until describe a ban; Until is nil for an indefinite one.
Reason string `json:"reason"`
Until *time.Time `json:"until"`
}
Event is one committed account or group change. It is recorded in the transaction that makes the change, so a refused or rolled-back change records nothing, and delivered after commit, at least once. It names what changed and never carries a credential, hash, token or code.
type EventKind ¶
type EventKind string
EventKind names a committed change delivered to Deps.OnEvent. Hosts ignore kinds they do not know: later versions add kinds.
const ( // EventUserRegistered: an account was created (sign-up, sign-in that // creates an account, CreateUser, EnsureUserRole, a bootstrap manifest). // ImportUsers records no events. EventUserRegistered EventKind = "user.registered" // EventUserEmailChanged, EventUserPhoneChanged, EventUserUsernameChanged: // Previous and Current are the old and new value ("" when none). EventUserEmailChanged EventKind = "user.email_changed" EventUserPhoneChanged EventKind = "user.phone_changed" EventUserUsernameChanged EventKind = "user.username_changed" // EventUserBanned carries Reason and Until. EventUserUnbanned: a ban in // force was lifted; a temporary ban running out records nothing. EventUserBanned EventKind = "user.banned" EventUserUnbanned EventKind = "user.unbanned" // EventUserDeleted starts the recovery window, EventUserRestored ends it // early and EventUserPurged follows the removal of the account row. EventUserDeleted EventKind = "user.deleted" EventUserRestored EventKind = "user.restored" EventUserPurged EventKind = "user.purged" // EventUserSessionsRevoked: every session and device key of the account // was revoked at once (Client.RevokeAccountSessions, DELETE // /admin/users/{user_id}/sessions); the subject says whose call it was. EventUserSessionsRevoked EventKind = "user.sessions_revoked" // Role events carry GroupID, Persona (RootPersona for root roles), the // subject (UserID or ApplicationID) and the role as Previous → Current. EventRoleGranted EventKind = "role.granted" EventRoleChanged EventKind = "role.changed" EventRoleRevoked EventKind = "role.revoked" // Group events carry GroupID and Persona. A purge records no role events // for the assignments it removes. EventGroupCreated EventKind = "group.created" EventGroupDeleted EventKind = "group.deleted" EventGroupPurged EventKind = "group.purged" )
type Grant ¶
type Grant interface {
// contains filtered or unexported methods
}
Grant is what a role holds: a permission or pattern, or another role of the same persona whose permissions it includes. Only Perm and Role are Grants.
type Group ¶
type Group struct {
ID string `json:"id"`
Persona Persona `json:"persona"`
CreatedAt time.Time `json:"created_at"`
// DeletedAt is set on a soft-deleted group.
DeletedAt *time.Time `json:"deleted_at"`
}
Group is one permission group: an instance of a persona, or the root group, the whole site. A group has no name: it only holds roles. The entity it guards (a channel, /c/golang) lives in the host app, which stores the group's ID.
type GroupMember ¶
type GroupMember struct {
Subject Subject `json:"subject"`
Role Role `json:"role"`
User *PublicUser `json:"user"`
}
GroupMember is a subject holding a role in a group. User is the account of a user member when MemberQuery.WithUsers asked for it.
type GroupQuery ¶
type GroupQuery struct {
Persona Persona
IncludeDeleted bool
Ownerless bool
Page PageRequest
}
GroupQuery lists the groups of a persona (zero = every persona but root), oldest first. Ownerless keeps only live groups that no owner counts for under the last-owner rule: created without one, or left without one by the credential sweep at boot. An owner whose required MFA enrollment is pending does not count.
type GroupRef ¶
type GroupRef struct {
// contains filtered or unexported fields
}
GroupRef addresses one permission group: by id, or the root group. Build it with GroupByID or RootGroup; the zero GroupRef addresses nothing.
type HashAlgo ¶
type HashAlgo string
HashAlgo names a password hash algorithm.
const ( HashArgon2id HashAlgo = "argon2id" HashBcrypt HashAlgo = "bcrypt" // HashLegacyResetRequired marks a migrated password that can never verify // (DES crypt, md5-crypt, a corrupted value). The raw hash is kept for // forensics only; the account must reset its password. HashLegacyResetRequired HashAlgo = "legacy-reset-required" )
type ImportConflict ¶
type ImportConflict string
ImportConflict is what ImportUsers does with a row that finds an account.
const ( // ImportSkip leaves the account unchanged. It is the default. ImportSkip ImportConflict = "skip" // ImportMerge updates the account the row's ID names. It merges // PublicMetadata's top-level keys over the account's, keeps the earlier // CreatedAt and the later LastLogin, fills a PreferredLanguage the account // lacks, links Providers, and stores PasswordHash when the account has no // password. It never changes identity, contacts, verification, bans or // deletion. A row that finds an account only by email, phone or username // is skipped with ImportUnboundMatch: matching is not proof. ImportMerge ImportConflict = "merge" )
type ImportMatch ¶
type ImportMatch string
ImportMatch names the identifier that found an existing account, the first of id, email, phone and username that did.
const ( ImportMatchID ImportMatch = "id" ImportMatchEmail ImportMatch = "email" ImportMatchPhone ImportMatch = "phone" ImportMatchUsername ImportMatch = "username" )
type ImportOptions ¶
type ImportOptions struct {
OnConflict ImportConflict
}
ImportOptions tunes ImportUsers.
type ImportReason ¶
type ImportReason string
ImportReason explains a skipped or rejected import row: one of the constants below, or the validation code of the rejected field (such as "invalid_email").
const ( ImportAlreadyExists ImportReason = "already_exists" ImportDuplicateInBatch ImportReason = "duplicate_in_batch" ImportUnboundMatch ImportReason = "unbound_match" ImportDeleted ImportReason = "deleted" ImportIdentifierConflict ImportReason = "identifier_conflict" ImportProviderAlreadyLinked ImportReason = "provider_already_linked" ImportProviderChangeRequiresUnlink ImportReason = "provider_change_requires_unlink" ImportInvalidID ImportReason = "invalid_id" ImportInvalidText ImportReason = "invalid_text" ImportInvalidPasswordHash ImportReason = "invalid_password_hash" ImportInvalidBan ImportReason = "invalid_ban" ImportInvalidProvider ImportReason = "invalid_provider" ImportInvalidDeletedAt ImportReason = "invalid_deleted_at" // Solana link rows. ImportInvalidUserID ImportReason = "invalid_user_id" ImportInvalidAddress ImportReason = "invalid_address" ImportMissingSource ImportReason = "missing_source" ImportMissingSourceID ImportReason = "missing_source_id" ImportMissingUser ImportReason = "missing_user" ImportAddressOwnedByOtherUser ImportReason = "address_owned_by_other_user" ImportAlreadyVerified ImportReason = "already_verified" ImportAlreadyImported ImportReason = "already_imported" ImportUserHasDifferentAddress ImportReason = "user_has_different_address" ImportProviderLinkConflict ImportReason = "provider_link_conflict" )
type ImportResult ¶
type ImportResult struct {
Rows []ImportRow `json:"rows"`
Inserted int `json:"inserted"`
Skipped int `json:"skipped"`
Merged int `json:"merged"`
Rejected int `json:"rejected"`
}
ImportResult reports every row, in input order.
type ImportRow ¶
type ImportRow struct {
Index int `json:"index"`
UserID string `json:"user_id"`
MatchedBy ImportMatch `json:"matched_by"`
Status ImportStatus `json:"status"`
Reason ImportReason `json:"reason"`
}
ImportRow is one row's outcome. Every row but a rejected one has UserID; skipped and merged rows say which identifier found the account, and Reason explains skipped and rejected rows.
type ImportSolanaLink ¶
type ImportSolanaLink struct {
UserID string
Address string
Source string
SourceID string
SourceCreatedAt *time.Time
}
ImportSolanaLink reserves a legacy wallet address for an account. It is not a login method until the owner proves the wallet through Sign-In with Solana.
type ImportSolanaLinkRow ¶
type ImportSolanaLinkRow struct {
Index int `json:"index"`
UserID string `json:"user_id"`
Address string `json:"address"`
Status ImportStatus `json:"status"`
Reason ImportReason `json:"reason"`
}
ImportSolanaLinkRow is one wallet row's outcome: inserted, skipped or rejected.
type ImportSolanaLinksResult ¶
type ImportSolanaLinksResult struct {
Rows []ImportSolanaLinkRow `json:"rows"`
Inserted int `json:"inserted"`
Skipped int `json:"skipped"`
Rejected int `json:"rejected"`
}
ImportSolanaLinksResult reports every wallet row, in input order.
type ImportStatus ¶
type ImportStatus string
ImportStatus is one row's outcome.
const ( ImportInserted ImportStatus = "inserted" ImportSkipped ImportStatus = "skipped" ImportMerged ImportStatus = "merged" ImportRejected ImportStatus = "rejected" )
type ImportUser ¶
type ImportUser struct {
// ID, when set, is the new account's id, or the existing account a Merge
// updates. It must be a UUID.
ID string
// Email and Phone import unverified: another system's "verified" is not
// proof (someone may have confirmed another person's address there). The
// account is unproven until it proves one here (a code, at its first
// sign-in where registration requires verification, or a reset link): it
// adds no login method or address, and its first proof retires the
// imported credentials and other addresses. The password survives only a
// proof by its holder: the code its password sign-in sent, confirmed with
// that sign-in's password proof, or a session that signed in with it.
Email string
Phone string
Username string
// PasswordHash is validated before it is stored; HashLegacyResetRequired
// keeps any value and makes the account reset its password.
PasswordHash *PasswordHash
// Ban imports a ban as it stood: At is required, By (the banning
// account) is optional.
Ban *BanState
// PublicMetadata is the account's public metadata (iam.PublicUser):
// anyone may read it, so import only public fields.
PublicMetadata map[string]any
CreatedAt *time.Time
UpdatedAt *time.Time
LastLogin *time.Time
// PreferredLanguage is validated as UpdateUser validates it.
PreferredLanguage string
// DeletedAt, not in the future, imports the account as the system's
// DeleteUsers at that time would have left it: the 30-day recovery window
// runs from DeletedAt (RestoreUsers restores it, signing in does not), and
// once the window has passed the account is purged after Deps.OnPurge.
// Such rows need River, as DeleteUsers does.
DeletedAt *time.Time
// Providers are external identities the account signs in with, linked as
// LinkProvider links them, at most one per issuer; Solana wallets import
// only through ImportSolanaLinks. A row naming an identity another account
// holds, or an earlier row of the batch names, is rejected with
// "provider_already_linked".
Providers []ProviderLink
}
ImportUser is one account to import. It finds an existing account by ID, Email, Phone or Username (canonical or a live alias); ImportOptions decides what happens then.
type Invitation ¶
type Invitation struct {
ID string `json:"id"`
GroupID string `json:"group_id"`
Role Role `json:"role"`
Email *string `json:"email"` // nil = a link, not an email invitation
CreatedBy *string `json:"created_by"` // nil = issued by the system
CreatedAt time.Time `json:"created_at"`
ExpiresAt *time.Time `json:"expires_at"`
RedeemedAt *time.Time `json:"redeemed_at"`
RevokedAt *time.Time `json:"revoked_at"`
}
Invitation is an invitation's metadata, never its code. Email is "" for a link.
type InvitationCreated ¶
type InvitationCreated struct {
Invitation Invitation `json:"invitation"`
Code string `json:"code"`
URL string `json:"url"`
}
InvitationCreated is a new invitation with its code and link, shown this once; only the code's hash is stored.
type JWK ¶ added in v1.1.0
type JWK struct {
Kty string `json:"kty"`
Use string `json:"use,omitempty"`
Kid string `json:"kid,omitempty"`
Alg string `json:"alg,omitempty"`
// RSA
N string `json:"n,omitempty"`
E string `json:"e,omitempty"`
// EC / OKP
Crv string `json:"crv,omitempty"`
X string `json:"x,omitempty"`
Y string `json:"y,omitempty"`
}
JWK is a JSON Web Key (RFC 7517): an RSA, EC or OKP public key. keys.PublicJWK builds one and keys.ParsePublicJWK reads it.
type ListPage ¶
ListPage is one page of a list. Next is the opaque cursor of the following page; "" marks the last page. Total, when the query asked for it, counts every item of the whole list.
On the wire it is {"data": [...], "next_cursor": string|null, "total": number|null}: the one list envelope.
func (ListPage[T]) MarshalJSON ¶ added in v0.148.0
func (*ListPage[T]) UnmarshalJSON ¶ added in v0.148.0
type MemberQuery ¶
type MemberQuery struct {
Kinds []SubjectKind
Roles []Role
LiveOnly bool
WithUsers bool
Page PageRequest
}
MemberQuery filters a group's members; empty filters match everything. LiveOnly keeps members that can act now: users not deleted or banned, applications enabled in a live group. WithUsers fills GroupMember.User, which carries contact details.
type Membership ¶
Membership is a group a subject holds a role in.
type MessageKind ¶
type MessageKind string
MessageKind names what an email or SMS is for. Later versions add kinds: a sender ignores or refuses a kind it does not know.
const ( // MessageVerification proves a contact: Code, an optional Link, and // Purpose says what for. MessageVerification MessageKind = "verification" // MessageLoginCode is a second-factor sign-in Code. MessageLoginCode MessageKind = "login_code" // MessagePasswordReset carries a reset Link. MessagePasswordReset MessageKind = "password_reset" // MessageInvite invites an address to create an account (email only): // Link. MessageInvite MessageKind = "invite" // MessageWelcome follows a completed registration (email only). MessageWelcome MessageKind = "welcome" // MessageContactChanged goes to the address or number an account just // replaced: ContactChange. MessageContactChanged MessageKind = "contact_changed" // MessageDeviceKeyEnrolled tells an existing account that a device key can // now sign in as it (email only): DeviceKey. MessageDeviceKeyEnrolled MessageKind = "device_key_enrolled" // MessageNewDeviceCode is the Code a new device enters to sign in once // the account has had too many new devices today // (Config.SignIn.NewDevicesPerAccount). It tells the owner someone is // signing in, and to change the password if it was not them. MessageNewDeviceCode MessageKind = "new_device_code" // MessageMFAReset tells an account that the system removed its passkeys, // second factors and device keys and signed it out everywhere (email only). MessageMFAReset MessageKind = "mfa_reset" )
type NameAdmissionRequest ¶
type NameAdmissionRequest struct {
UserID string // Empty only before a new account is created.
// SubjectID is the account making the change: the user, or staff.
SubjectID string
CurrentName string
RequestedName string
Operation NameOperation
}
NameAdmissionRequest is the username admission hook's operation context.
type NameOperation ¶
type NameOperation string
const ( NameCreate NameOperation = "create" NameRename NameOperation = "rename" )
type NameResolution ¶
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 NewAPIKey ¶
NewAPIKey is the input of CreateAPIKey. ExpiresAt nil means no expiry, capped by Config.APIKeys.MaxTTL when set.
type NewGroup ¶
NewGroup describes a group to create. ID, when set, is the group's id: any uuid the host keys the group by, such as its own row's id or a user's id. Creating an id that exists returns that group unchanged when it is a live group of Persona, and is iam.ErrGroupConflict otherwise. Owner, when set, is seeded with the owner role of a new group: a live account, or an enabled remote application.
type NewInvitation ¶
NewInvitation is the input of CreateInvitation.
Without Email it is an invite link: a single-use code granting Role (which it needs) to the signed-in account that redeems it. With Email, AuthKit emails a registration invite to that address: with Role, registering also grants it, and an existing account that has verified the address may redeem it instead; without Role, in the root group, it only lets the address register (root:users:invite).
ExpiresAt nil is the default lifetime: 72 hours for a link, 7 days for an email invite. A link lives at most 30 days.
type NewUser ¶
NewUser creates a native account. A verified flag asserts that your code proved the address; another system's word is not proof (import such accounts with ImportUsers).
type OAuthAssertion ¶ added in v1.10.0
type OAuthAssertion struct {
// Subject (sub) is the workload as it names itself.
Subject string
// ID (jti) redeems once per key.
ID string
// IssuedAt (iat) is zero when absent.
IssuedAt time.Time
ExpiresAt time.Time
// Claims are its claims AuthKit does not define, raw JSON by name; nil
// when there are none.
Claims map[string]json.RawMessage
}
OAuthAssertion is a verified RFC 7523 assertion: signed by the key the request's JWKThumbprint names, which the token request also proved with DPoP. AuthKit checked its iss (the client), aud (the token endpoint), lifetime and jti; the rest is the workload's own say.
type OAuthCapability ¶ added in v1.10.0
type OAuthCapability struct {
// ID (jti) redeems once per device key: one token per capability.
ID string
// IssuedAt (iat) is zero when absent.
IssuedAt time.Time
ExpiresAt time.Time
// Claims are its claims AuthKit does not define (a run id, say), raw
// JSON by name; nil when there are none.
Claims map[string]json.RawMessage
}
OAuthCapability is a verified capability: a JWT the request's UserID signed offline with its live device key DeviceKeyID, letting the workload key JWKThumbprint do AuthorizationDetails on Resource until ExpiresAt.
type OAuthGrantAuthorizer ¶ added in v1.6.0
type OAuthGrantAuthorizer func(context.Context, OAuthGrantRequest) (OAuthGrantDecision, error)
OAuthGrantAuthorizer decides each jwt-bearer grant of the authorization server (Deps.OAuthGrants). It runs on the request path, so keep it fast.
type OAuthGrantDecision ¶ added in v1.6.0
type OAuthGrantDecision struct {
// AuthorizationDetails is what the token carries (RFC 9396), in the
// access token and the token response; nil keeps the capability's. It
// may only narrow: each entry must equal one of the capability's.
AuthorizationDetails json.RawMessage
// MaxLifetime caps the token from the token request; it only shortens
// the capability's lifetime. 0 adds no cap.
MaxLifetime time.Duration
// Claims are added to the access token. Each name must be
// collision-resistant: an absolute URI ("https://hub.example.com/run").
Claims map[string]any
// Invoker names the workload acting for the user (act.sub,
// verify.Claims.Invoker); empty is the workload key's JWKThumbprint.
Invoker string
}
OAuthGrantDecision is what AuthKit mints for one grant. Its zero value grants the capability's operations for its lifetime.
type OAuthGrantKind ¶ added in v1.6.0
type OAuthGrantKind string
OAuthGrantKind is the authorization-server grant an OAuthGrantAuthorizer decides.
const OAuthGrantJWTBearer OAuthGrantKind = "jwt_bearer"
OAuthGrantJWTBearer is an RFC 7523 JWT-bearer grant: a workload proves its key (Assertion, JWKThumbprint) and presents a Capability one of the user's device keys signed for that key: the operations (AuthorizationDetails) it may do for the user on Resource.
type OAuthGrantRequest ¶ added in v1.6.0
type OAuthGrantRequest struct {
Kind OAuthGrantKind
ClientID string
// UserID is the user the capability is for; DeviceKeyID the live device
// key that signed it.
UserID string
DeviceKeyID string
Resource string
// AuthorizationDetails is the capability's RFC 9396
// authorization_details array.
AuthorizationDetails json.RawMessage
// JWKThumbprint is the workload key (RFC 7638 thumbprint) that signed
// Assertion and proved itself with DPoP.
JWKThumbprint string
// Assertion and Capability are the grant's, verified.
Assertion *OAuthAssertion
Capability *OAuthCapability
}
OAuthGrantRequest is one grant decision for the host (Deps.OAuthGrants).
type OpResult ¶
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 ¶
func (*OpResult) UnmarshalJSON ¶
type PageRequest ¶
PageRequest asks for one page of a keyset-paged list. Limit 0 means DefaultPageLimit; larger values are capped at MaxPageLimit.
func (PageRequest) PageLimit ¶
func (p PageRequest) PageLimit() int
PageLimit is the effective limit.
type Passkey ¶ added in v0.148.0
type Passkey struct {
ID string `json:"id"`
Label *string `json:"label"`
Transports []string `json:"transports"`
AuthenticatorAttachment *string `json:"authenticator_attachment"`
BackupEligible bool `json:"backup_eligible"`
BackupState bool `json:"backup_state"`
CreatedAt time.Time `json:"created_at"`
LastUsedAt *time.Time `json:"last_used_at"`
}
Passkey is a WebAuthn credential an account signs in with.
type PasswordHash ¶
type PasswordHash struct {
Hash string `json:"hash" yaml:"hash"`
Algo HashAlgo `json:"algo" yaml:"algo"`
}
PasswordHash is a password hash made elsewhere (an import, a bootstrap manifest, UpdateUser), validated before it is stored.
type Perm ¶
type Perm struct {
// contains filtered or unexported fields
}
Perm is a permission `<persona>:<resource>:<action>` (`channel:posts:edit`) or a grant pattern, where `*` replaces the action (`channel:posts:*`) or everything after the persona (`channel:*`, the owner).
func (Perm) MarshalText ¶
func (Perm) Matches ¶
Matches reports whether grant authorizes p. A grant is a literal (`org:members:read`) or a pattern whose segments after the persona may be `*`: `org:*` covers every `org:` permission; any other pattern covers only permissions with as many segments (`org:members:*` covers `org:members:read`, not `org:members:read:x`). The persona is always literal, so a bare `*` matches nothing. When p is itself a pattern, Matches reports whether grant covers all of it. Malformed text on either side matches nothing, and nothing is trimmed. testdata/perm_vectors.json pins this rule.
func (*Perm) UnmarshalText ¶
UnmarshalText reads a permission or pattern: a persona, then one or more segments, each a name or `*` (catalogs use `<persona>:<resource>:<action>`; tokens from other platforms may not). Empty is the zero Perm.
type Persona ¶
type Persona struct {
// contains filtered or unexported fields
}
Persona is a type of permission group (`channel`, `org`, `merchant`). A permission group is one instance of a persona. root is the persona with exactly one group, the whole site. The persona is the first segment of every permission its groups use.
func RootPersona ¶
func RootPersona() Persona
RootPersona is the persona with exactly one group, the whole site. It always exists.
func (Persona) MarshalText ¶
func (Persona) OwnerGrant ¶
OwnerGrant is the owner's grant `<persona>:*`.
func (Persona) OwnerRole ¶
OwnerRole is the role every persona has: it holds the whole namespace, OwnerGrant. The root owner also holds every other persona's OwnerGrant.
func (*Persona) UnmarshalText ¶
UnmarshalText reads a persona name; empty is the zero Persona.
type ProviderLink ¶
ProviderLink is an external identity to sign in with: the provider's issuer and subject, its configured name, and the email it reported.
type ProvisioningTarget ¶ added in v1.12.0
type ProvisioningTarget struct {
Name string `json:"name"`
// SyncedAt is when the initial sync queued every account; nil until then.
SyncedAt *time.Time `json:"synced_at"`
// ReconciledAt is when the target's users were last compared with the
// accounts.
ReconciledAt *time.Time `json:"reconciled_at"`
// LastSuccessAt is when a run last delivered everything it sent.
LastSuccessAt *time.Time `json:"last_success_at"`
// FailingSince is when deliveries began failing; nil while none is.
FailingSince *time.Time `json:"failing_since"`
LastError *string `json:"last_error"`
// Backlog is how many users wait to be sent, retries included.
Backlog int `json:"backlog"`
}
ProvisioningTarget is the delivery status of one SCIM target (Config.Provisioning).
type PublicUser ¶
type PublicUser struct {
ID string `json:"id"`
Username string `json:"username"`
// CreatedAt is when the account was created: its "member since".
CreatedAt *time.Time `json:"created_at"`
Deleted bool `json:"deleted"`
// PublicMetadata is the JSON object only the host writes
// (Client.PatchPublicMetadata, ImportUsers) and anyone may read: GET /me,
// GET /users and every PublicUser carry it whole. It is the place for a
// profile's public fields (an avatar URL, a biography); keep anything
// private in your own tables, keyed by the account id.
PublicMetadata map[string]any `json:"public_metadata"`
}
PublicUser is what other people may see of an account: never its contacts, ban or sign-in data. A deleted account is a tombstone: Deleted is set and every other field but ID is empty.
func (PublicUser) DisplayName ¶
func (u PublicUser) DisplayName() string
DisplayName is the username, or "user-<first 8 of id>" for tombstoned and unnamed users.
type RegistrationMode ¶
type RegistrationMode string
RegistrationMode is the public self-registration policy: open (anyone), invite_only (with an account invitation) or closed. Host operations (CreateUser, bootstrap, import) create users in every mode.
const ( RegistrationModeOpen RegistrationMode = "open" RegistrationModeInviteOnly RegistrationMode = "invite_only" RegistrationModeClosed RegistrationMode = "closed" )
type RegistrationVerificationPolicy ¶
type RegistrationVerificationPolicy string
RegistrationVerificationPolicy controls whether a newly-registered contact must be verified.
const ( RegistrationVerificationNone RegistrationVerificationPolicy = "none" RegistrationVerificationOptional RegistrationVerificationPolicy = "optional" RegistrationVerificationRequired RegistrationVerificationPolicy = "required" )
type RemoteApplication ¶
type RemoteApplication struct {
ID string `json:"id"`
// GroupID is the controlling group. Registration takes it from the group
// the application is registered in, never from this field.
GroupID string `json:"group_id"`
Issuer string `json:"issuer"` // OIDC iss
JWKSURI string `json:"jwks_uri"` // OIDC jwks_uri (jwks mode only)
// Mode is the trust source; empty infers static from PublicKeys, else jwks.
Mode RemoteApplicationMode `json:"mode"`
// PublicKeys is the static-mode key list (empty in jwks mode).
PublicKeys []RemoteApplicationKey `json:"public_keys"`
Enabled bool `json:"enabled"`
// TrustRoot is what may change the application's keys: the system
// (manual) or a credentials manager of its controlling group (user).
// Never the keypair alone.
TrustRoot ApplicationTrustRoot `json:"trust_root"`
Role Role `json:"role"`
Permissions []Perm `json:"permissions"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
RemoteApplication is a registered external issuer: the registry a resource server reads to trust its access tokens, with its role as their ceiling. Role and Permissions are read, never written: its role in its controlling group and the permissions that role confers now (none when it needs MFA, which an application cannot present).
type RemoteApplicationKey ¶
type RemoteApplicationKey struct {
KID string `json:"kid,omitempty" yaml:"kid,omitempty"`
PublicKeyPEM string `json:"public_key_pem" yaml:"public_key_pem"`
JWK *JWK `json:"jwk,omitempty" yaml:"jwk,omitempty"`
}
RemoteApplicationKey is one entry of a static-mode application's human-managed key list (stored as jsonb; edited like an authorized_keys file): exactly one of PublicKeyPEM and JWK. An empty KID takes the JWK's kid.
type RemoteApplicationMode ¶
type RemoteApplicationMode string
RemoteApplicationMode is a remote application's one trust source:
jwks — keys fetched and refreshed from JWKSURI; rotation is publishing a
new kid at the same URL.
static — a human-managed PEM list for applications without a JWKS endpoint.
const ( RemoteApplicationModeJWKS RemoteApplicationMode = "jwks" RemoteApplicationModeStatic RemoteApplicationMode = "static" )
type ResolvedAPIKey ¶ added in v1.7.0
type ResolvedAPIKey struct {
ID string `json:"id"`
LookupID string `json:"lookup_id"`
Group Group `json:"group"`
Issuer string `json:"issuer"` // the issuer of the AuthKit deployment holding the key
Role Role `json:"role"`
Permissions []Perm `json:"permissions"`
ExpiresAt *time.Time `json:"expires_at"`
}
ResolvedAPIKey is a resolved, live API key: the group it acts in and the permissions of its role at resolution time.
type Role ¶
type Role struct {
// contains filtered or unexported fields
}
Role is a role of a persona: its owner role or one the app declares (`moderator`). A role bundles permissions; where it is held is its scope, and a role held on root applies in every group. Its text form is `<persona>:<name>` (`channel:moderator`).
func (Role) MarshalText ¶
func (*Role) UnmarshalText ¶
UnmarshalText reads `<persona>:<name>`; empty is the zero Role.
type Route ¶
type Route struct {
Method string
Path string
Group RouteGroup
Auth RouteAuthTier
Permission string // `<persona>` stands for the group's persona
}
Route is one mounted endpoint. Path is the full net/http pattern path ("/api/v1/admin/users/{id}"); Auth and Permission describe the route-level gate, not every handler-specific authorization check.
type RouteAuthTier ¶
type RouteAuthTier string
RouteAuthTier is the authentication a route enforces before its handler runs.
const ( AuthPublic RouteAuthTier = "public" // no credential AuthOptional RouteAuthTier = "optional" // the identity, when a credential is present AuthRequired RouteAuthTier = "required" // a verified identity AuthSession RouteAuthTier = "session" // a verified identity whose session or device key is still active AuthPermission RouteAuthTier = "permission" // a verified identity holding Route.Permission, its session checked )
type RouteGroup ¶
type RouteGroup string
RouteGroup names one capability of AuthKit's HTTP surface. Hosts mount the default groups or select exactly the ones they expose.
const ( RouteAuth RouteGroup = "auth" // RouteDeviceKeys is the refreshless native-client login surface: email // enrollment plus Ed25519 challenge authentication. RouteDeviceKeys RouteGroup = "device_keys" RouteRegistration RouteGroup = "registration" RouteAccount RouteGroup = "account" RouteAdmin RouteGroup = "admin" RoutePermissionGroups RouteGroup = "groups" RouteBrowserOIDC RouteGroup = "browser_oidc" // RouteAuthorizationServer is the OAuth 2.0 authorization server and // OpenID provider: issuer metadata, authorize, token, userinfo, // revocation and RP-initiated logout beneath the issuer's path, and the // API the SPA approves sign-in requests through. Mounted only when // Config.AuthorizationServer declares clients. RouteAuthorizationServer RouteGroup = "authorization_server" // RouteSCIM is the read-only SCIM 2.0 service provider beneath the // issuer's path (/scim/v2), for client-credentials tokens with scope // scim:read. Mounted with the authorization server. RouteSCIM RouteGroup = "scim" )
type SMSMessage ¶
type SMSMessage struct {
Kind MessageKind
To string
// Language is a two-letter code, chosen as for EmailMessage.
Language string
Code string
Link string
Purpose VerificationPurpose
ContactChange *ContactChange
}
SMSMessage is one text message AuthKit asks Deps.SMS to deliver. Fields a kind does not use are empty.
type Session ¶
type Session struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
LastUsedAt time.Time `json:"last_used_at"`
ExpiresAt *time.Time `json:"expires_at"`
UserAgent *string `json:"user_agent"`
IP *string `json:"ip"`
Current bool `json:"current"`
}
Session is one refresh session on this deployment's issuer. Current marks the session behind the access token of the request that listed it.
type SessionEvent ¶
type SessionEvent struct {
Kind SessionEventKind `json:"kind"`
OccurredAt time.Time `json:"occurred_at"`
Issuer string `json:"issuer"`
SessionID *string `json:"session_id"`
Method *string `json:"method"`
Reason *string `json:"reason"`
IP *string `json:"ip"`
UserAgent *string `json:"user_agent"`
}
SessionEvent is one entry of an account's sign-in and session history, kept for Config.SessionEventRetention. Method is how a session was created; Reason why one was revoked or a sign-in failed.
type SessionEventKind ¶
type SessionEventKind string
SessionEventKind names an entry of an account's session history.
const ( SessionEventCreated SessionEventKind = "session_created" SessionEventFailed SessionEventKind = "session_failed" SessionEventRevoked SessionEventKind = "session_revoked" SessionEventPasswordChange SessionEventKind = "password_changed" SessionEventPasswordRecovery SessionEventKind = "password_recovery" // SessionEventAccountSessionsRevoked is one account-wide revocation; each // session it ended has its own SessionEventRevoked. SessionEventAccountSessionsRevoked SessionEventKind = "account_sessions_revoked" )
type SessionEventQuery ¶
type SessionEventQuery struct {
Kinds []SessionEventKind
Page PageRequest
}
SessionEventQuery pages an account's session history, newest first. No Kinds means every kind.
type SessionRef ¶
SessionRef names the sign-in an access token was minted from: a refresh session (claim sid) or a device key (claim device_key_id), never both.
func (SessionRef) IsZero ¶
func (r SessionRef) IsZero() bool
IsZero reports whether r names no sign-in.
type SolanaNetwork ¶
type SolanaNetwork string
SolanaNetwork is the chain Sign In With Solana signs for. The zero value leaves SIWS off.
const ( SolanaMainnet SolanaNetwork = "mainnet" SolanaTestnet SolanaNetwork = "testnet" SolanaDevnet SolanaNetwork = "devnet" )
type Subject ¶
type Subject struct {
ID string `json:"id"`
Kind SubjectKind `json:"kind"`
}
Subject is an account that can hold roles in a permission group: a user or a remote application.
func UserSubject ¶
type SubjectKind ¶
type SubjectKind string
SubjectKind discriminates who holds a role in a permission group.
const ( SubjectKindUser SubjectKind = "user" SubjectKindRemoteApplication SubjectKind = "remote_application" )
type TokenSet ¶
type TokenSet struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
ExpiresIn int64 `json:"expires_in"`
RefreshToken *string `json:"refresh_token"`
}
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). RefreshToken is nil when none is issued, or when the mount's refresh cookie carries it.
type TwoFactorMethod ¶
type TwoFactorMethod string
TwoFactorMethod is one second-factor channel a host enables.
const ( TwoFactorEmail TwoFactorMethod = "email" TwoFactorSMS TwoFactorMethod = "sms" TwoFactorTOTP TwoFactorMethod = "totp" )
type TwoFactorMode ¶
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 `json:"id"`
Email *string `json:"email"`
Phone *string `json:"phone_number"`
Username string `json:"username"`
EmailVerified bool `json:"email_verified"`
PhoneVerified bool `json:"phone_verified"`
PreferredLanguage *string `json:"preferred_language"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
LastLogin *time.Time `json:"last_login"`
DeletedAt *time.Time `json:"deleted_at"`
// Ban is nil when no ban is in force.
Ban *BanState `json:"ban"`
// ExpiredBan is the stored ban once its Until has passed and no unban
// cleared it; nil otherwise. At most one of Ban and ExpiredBan is set.
ExpiredBan *BanState `json:"expired_ban"`
// PublicMetadata is the account's public metadata (see PublicUser).
PublicMetadata map[string]any `json:"public_metadata"`
}
User is an account. It is the privileged view: it carries contact details, so render other people with PublicUser. A nil field is unset.
type UserDeletion ¶
UserDeletion identifies one recoverable account-deletion generation. Hooks are delivered at least once. Use ID together with the hook name as an idempotency key; deleting again after restoration creates a new ID.
type UserEntry ¶
type UserEntry struct {
User
RootRole *Role `json:"root_role"`
Entitlements []string `json:"entitlements"`
}
UserEntry is one row of the user directory: the account and its root role (nil when it holds none), and, when the query asks, its entitlements.
type UserQuery ¶
type UserQuery struct {
Search string
Status UserStatus
RootRole Role
Entitlement string
Sort UserSort
Desc bool
Total bool
WithEntitlements bool
Page PageRequest
}
UserQuery lists accounts. Search matches text within a username, email or phone (at its start, below three characters), an account id, or exactly a linked sign-in's subject (a wallet address, a provider's user id), provider email or provider username. RootRole filters on a role in the root group; Entitlement needs an entitlements provider that can list subjects. Total counts every match into ListPage.Total; WithEntitlements fills UserEntry.Entitlements from the entitlements provider.
type UserRef ¶
type UserRef struct {
// contains filtered or unexported fields
}
UserRef addresses one account by exactly one key. Build it with UserByID, UserByEmail, UserByPhone or UserByUsername; the zero UserRef finds nobody.
func UserByEmail ¶
func UserByPhone ¶
func UserByUsername ¶
type UserStatus ¶
type UserStatus string
UserStatus filters a user list.
const ( UserStatusLive UserStatus = "" // not deleted (default) UserStatusActive UserStatus = "active" // not deleted, no ban in force UserStatusBanned UserStatus = "banned" // not deleted, a ban in force UserStatusDeleted UserStatus = "deleted" // soft-deleted UserStatusAny UserStatus = "any" )
type UserUpdate ¶
type UserUpdate struct {
Email, Phone, Username, PreferredLanguage, Password *string
EmailVerified, PhoneVerified *bool
PasswordHash *PasswordHash
}
UserUpdate changes an account; nil fields stay unchanged, and "" clears PreferredLanguage. A new Email or Phone starts unverified unless the same update sets its verified flag.
type VerificationPurpose ¶
type VerificationPurpose string
VerificationPurpose says what a MessageVerification proves the contact for.
const ( PurposeSignup VerificationPurpose = "signup" PurposeContactVerify VerificationPurpose = "contact_verify" PurposeContactChange VerificationPurpose = "contact_change" PurposePasswordlessLogin VerificationPurpose = "passwordless_login" PurposeTwoFactorSetup VerificationPurpose = "2fa_setup" PurposeDeviceKeyEnrollment VerificationPurpose = "device_key_enrollment" )
Source Files
¶
- apikey.go
- assurance.go
- bootstrap.go
- contract.go
- doc.go
- errors.go
- event.go
- group.go
- group_ref.go
- http.go
- httperror.go
- identifiers.go
- identity.go
- import.go
- invitation.go
- jwk.go
- messages.go
- names.go
- oauth_grant.go
- opresult.go
- page.go
- passkey.go
- permission_group.go
- provisioning.go
- registration.go
- remoteapp.go
- solana.go
- token.go
- twofactor.go
- user.go
- user_deletion.go
- wire.go