Documentation
¶
Overview ¶
Package iam holds AuthKit's shared identity and access vocabulary: users, subjects, groups, roles and permissions, actors, 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 DecodeError(resp *http.Response) error
- func PublicDisplayName(users map[string]PublicUser, id string) string
- func WriteError(w http.ResponseWriter, err error)
- type APIKey
- type APIKeyCreated
- type APIKeyPrincipal
- type AccessTokenOptions
- type AccountSessionRevocation
- type Actor
- func (a Actor) Bounded() bool
- func (a Actor) CeilingCovers(perm Perm) bool
- func (a Actor) Delegation() (DelegatedGrant, bool)
- func (a Actor) ID() string
- func (a Actor) InSession(r SessionRef) Actor
- func (a Actor) IsZero() bool
- func (a Actor) Kind() ActorKind
- func (a Actor) Session() (SessionRef, bool)
- func (a Actor) String() string
- func (a Actor) Within(perms ...Perm) Actor
- type ActorKind
- 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 DelegatedAccess
- type DelegatedGrant
- type DelegationAuthorizer
- type DelegationGrant
- type DelegationRequest
- 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 ListPage
- type MemberQuery
- type Membership
- type MessageKind
- type NameAdmissionRequest
- type NameOperation
- type NameResolution
- type NewAPIKey
- type NewGroup
- type NewInvitation
- type NewUser
- type OpResult
- type PageRequest
- type Passkey
- type PasswordHash
- type Perm
- type Persona
- type ProviderLink
- type PublicUser
- type RegistrationMode
- type RegistrationVerificationPolicy
- type RemoteApplication
- type RemoteApplicationKey
- type RemoteApplicationMode
- type Role
- type Route
- type RouteAuthTier
- type RouteGroup
- type SMSMessage
- type ServiceJWT
- type ServiceJWTClaims
- 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 ( // 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 ( // 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 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 bounds the ids (users, groups, subjects) accepted by one batch call.
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 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 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 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.
Types ¶
type APIKey ¶
type APIKey struct {
ID string `json:"id"` // the key's identity: APIKeyActor(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 APIKeyPrincipal ¶
type APIKeyPrincipal 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"`
}
APIKeyPrincipal is a resolved, live API key: the group it acts in and the permissions of its role at resolution time.
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 Actor ¶
type Actor struct {
// contains filtered or unexported fields
}
Actor is who performs an operation. Its fields are unexported: the zero Actor is invalid and every operation refuses it, and SystemActor is the only way to build the system actor. Authority is resolved live per operation; nothing is cached in the value.
func APIKeyActor ¶
APIKeyActor acts as an API key: APIKey.ID, the same value as verify Claims.APIKeyID.
func DelegatedActor ¶
func DelegatedActor(g DelegatedGrant) Actor
DelegatedActor acts under a verified delegated grant. A grant from a foreign platform carries no AuthKit authority.
func RemoteApplicationActor ¶
RemoteApplicationActor acts as a registered remote application.
func SystemActor ¶
func SystemActor() Actor
SystemActor 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 (Actor) CeilingCovers ¶
CeilingCovers reports whether every ceiling permits perm (true when unbounded).
func (Actor) Delegation ¶
func (a Actor) Delegation() (DelegatedGrant, bool)
Delegation returns the delegated grant of a delegated actor.
func (Actor) InSession ¶
func (a Actor) InSession(r SessionRef) Actor
InSession binds a user or delegated actor 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.ActorFromClaims binds every actor it builds from an AuthKit user or delegated token; an unbound actor (UserActor in trusted server code) is checked at account level only. The zero ref leaves a unchanged; any other actor, or a ref naming both, is the zero Actor.
func (Actor) Session ¶
func (a Actor) Session() (SessionRef, bool)
Session is the sign-in a is bound to (InSession).
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 ban in force: 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"`
Metadata map[string]any `json:"metadata" yaml:"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 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 DelegatedAccess ¶
type DelegatedAccess struct {
// Subject becomes delegated_sub. A user actor mints only for itself (empty
// means the actor); the system must name the subject.
Subject string
// Audiences becomes aud: the resource APIs the token is for.
Audiences []string
// Permissions becomes the permissions claim. A permission in an AuthKit
// persona's namespace must be held live by the subject on the root group;
// the host's own vocabulary is the host's decision.
Permissions []string
// Attributes carries app-specific JSON; AuthKit assigns no key a meaning.
Attributes map[string]any
// TTL is clamped to the Config.Delegated bounds (default 15m, at most 1h).
TTL time.Duration
// JTI becomes jti; empty mints a fresh one.
JTI string
NotBefore time.Time
// CertificateThumbprint binds the token to an X.509 certificate (RFC 8705
// cnf.x5t#S256), JWKThumbprint to a DPoP key (RFC 9449 cnf.jkt), each the
// unpadded base64url SHA-256. At most one; neither mints a bearer token.
CertificateThumbprint string
JWKThumbprint string
}
DelegatedAccess is a delegated access token to mint: signed by this deployment, it carries delegated_sub and never sub.
type DelegatedGrant ¶
type DelegatedGrant struct {
Issuer string
Subject string // delegated_sub
// Permissions are always a ceiling.
Permissions []Perm
// RemoteApplicationID is set when the token is bound to an application.
RemoteApplicationID string
// GroupID is that application's controlling group.
GroupID string
}
DelegatedGrant is copied from a verified delegated access token.
type DelegationAuthorizer ¶
type DelegationAuthorizer func(context.Context, DelegationRequest) (DelegationGrant, error)
DelegationAuthorizer is the single host seam of the delegated mint route.
type DelegationGrant ¶
DelegationGrant is the complete authority AuthKit signs for one request.
type DelegationRequest ¶
type DelegationRequest struct {
UserID string
Audiences []string
TTL time.Duration
// DelegateCertificate is the delegate's X.509 leaf (DER) and
// CertificateThumbprint its x5t#S256; both empty on the DPoP path.
DelegateCertificate []byte
CertificateThumbprint string
// JWKThumbprint is the DPoP key's jkt; empty on the certificate path.
JWKThumbprint string
RequestedGrant json.RawMessage
}
DelegationRequest is what POST /delegated/token asks the host to authorize (ak#277). Audiences and TTL are already clamped; the sender proof is validated; RequestedGrant is the client's opaque, host-schema object that AuthKit never copies into the token. The token is bound to exactly one of the delegate's certificate and its DPoP key.
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, deleted or reserved). 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 actor bound to a session or device key // (Actor.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) )
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 ( 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, service-JWT, delegation and remote-application sentinels live beside their types).
var ErrDelegationRefused Error = errmodel.E(errmodel.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 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 ErrInvalidServiceJWT Error = errmodel.E(errmodel.CodeInvalidServiceJWT)
ErrInvalidServiceJWT indicates a presented service JWT failed verification.
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.
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"`
// ActorKind and ActorID name who made the change (ActorID is empty for
// the system). Both 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.
ActorKind ActorKind `json:"actor_kind"`
ActorID string `json:"actor_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" // 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 an account the row is bound to: found by ID, or by an // email or phone verified on the account. It merges Metadata, keeps the // earlier CreatedAt and the later LastLogin, and fills a PreferredLanguage // or AvatarURL the account lacks. Only a row bound by ID or by a contact // verified on both sides links Providers, and stores PasswordHash when the // account has no password. It never changes identity, contacts, // verification, bans or deletion. A row that is not bound is skipped with // ImportUnboundMatch. 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 string
Phone string
Username string
EmailVerified bool
PhoneVerified bool
// 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
Metadata map[string]any
CreatedAt *time.Time
UpdatedAt *time.Time
LastLogin *time.Time
// PreferredLanguage and AvatarURL are validated as UpdateUser validates them.
PreferredLanguage string
AvatarURL 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 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, banned or reserved, 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" // 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.
ActorID 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. Verified flags are the system's assertion that the address was proven elsewhere.
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 PublicUser ¶
type PublicUser struct {
ID string `json:"id"`
Username string `json:"username"`
AvatarURL *string `json:"avatar_url"`
Deleted bool `json:"deleted"`
// Metadata holds the account's metadata keys the host made public
// (Config.PublicUserMetadata), and no others.
Metadata map[string]any `json:"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 federation principal: an external issuer AuthKit trusts to mint delegated and remote-application tokens. 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"`
}
RemoteApplicationKey is one entry of a static-mode principal's human-managed key list (stored as jsonb; edited like an authorized_keys file).
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 principals without a JWKS endpoint.
const ( RemoteApplicationModeJWKS RemoteApplicationMode = "jwks" RemoteApplicationModeStatic RemoteApplicationMode = "static" )
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 principal AuthOptional RouteAuthTier = "optional" // principal used when present AuthRequired RouteAuthTier = "required" // valid principal AuthSession RouteAuthTier = "session" // valid principal whose session or device key is still active AuthPermission RouteAuthTier = "permission" // valid principal 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" // RouteDelegated is the delegated-token mint surface (POST // /delegated/token), mounted only when Config.Delegated declares audiences. RouteDelegated RouteGroup = "delegated" )
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 ServiceJWT ¶
type ServiceJWT struct {
Subject string
Audiences []string
Permissions []string
// TTL defaults to, and is capped at, DefaultServiceJWTLifetime.
TTL time.Duration
NotBefore time.Time
IssuedAt time.Time
JTI string
}
ServiceJWT is a first-party machine-to-machine JWT to mint. It grants nothing AuthKit enforces; the receiver authorizes its permissions.
type ServiceJWTClaims ¶
type ServiceJWTClaims struct {
Issuer string `json:"issuer"`
Subject string `json:"subject"`
Audiences []string `json:"audiences"`
IssuedAt time.Time `json:"issued_at"`
NotBefore time.Time `json:"not_before"`
ExpiresAt time.Time `json:"expires_at"`
JTI string `json:"jti"`
TokenUse string `json:"token_use"`
Permissions []string `json:"permissions"`
}
ServiceJWTClaims is the claim shape of a service JWT (iss, sub, aud, iat, nbf, exp, jti, token_use, permissions). Permissions are requested capabilities; receivers intersect them with their own grants.
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 a principal that can hold roles in a permission group.
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"`
AvatarURL *string `json:"avatar_url"`
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"`
}
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, not banned UserStatusBanned UserStatus = "banned" // not deleted, banned UserStatusDeleted UserStatus = "deleted" // soft-deleted UserStatusAny UserStatus = "any" )
type UserUpdate ¶
type UserUpdate struct {
Email, Phone, Username, AvatarURL, PreferredLanguage, Password *string
EmailVerified, PhoneVerified *bool
PasswordHash *PasswordHash
}
UserUpdate changes an account; nil fields stay unchanged, and "" clears AvatarURL and 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
¶
- actor.go
- apikey.go
- assurance.go
- bootstrap.go
- contract.go
- delegation.go
- doc.go
- errors.go
- event.go
- group.go
- group_ref.go
- http.go
- httperror.go
- identifiers.go
- import.go
- invitation.go
- messages.go
- names.go
- opresult.go
- page.go
- passkey.go
- permission_group.go
- registration.go
- remoteapp.go
- servicejwt.go
- solana.go
- token.go
- twofactor.go
- user.go
- user_deletion.go
- wire.go