Documentation
¶
Overview ¶
Package accounts owns the federated end-user account model — the data shape shared by both username/password (localauth) and provider-mediated (federatedauth) authentication flows.
What this package owns:
- User / BasicUser — the account principal as seen by the application
- Identity — a verifiable contact (email, phone) owned by one User
- Channel — per-provider credentials (local, google, github, …)
- UserStore, IdentityStore, ChannelStore, UsernameStore — store interfaces
- AuthError — structured account-level errors used by both flows
What this package deliberately does NOT own:
- OAuth access/refresh tokens, API keys, scopes — see core/
- Username/password specifics (signup policy, credentials shape) — see localauth/
- OAuth/SAML callback orchestration — see federatedauth/
Index ¶
- Constants
- func DetectUsernameType(username string) string
- func IdentityKey(identityType, identityValue string) string
- func LinkedChannels(profile map[string]any) []string
- type AuthError
- type AuthErrorHandler
- type BasicUser
- type Channel
- type ChannelStore
- type CredentialsValidator
- type HandleUserFunc
- type Identity
- type IdentityStore
- type User
- type UserStore
- type UsernameStore
Constants ¶
const ( ErrCodeEmailExists = "email_exists" ErrCodeUsernameTaken = "username_taken" ErrCodeWeakPassword = "weak_password" ErrCodeInvalidUsername = "invalid_username" ErrCodeInvalidEmail = "invalid_email" ErrCodeInvalidPhone = "invalid_phone" ErrCodeMissingField = "missing_field" ErrCodeInvalidCreds = "invalid_credentials" )
Common error codes.
Variables ¶
This section is empty.
Functions ¶
func DetectUsernameType ¶
DetectUsernameType attempts to detect what type of username was provided. Returns "email" if it contains "@", "phone" if it starts with + or a digit, otherwise "username". Used by both localauth's password validator and apiauth's password grant when the host doesn't supply an explicit type.
func IdentityKey ¶
IdentityKey creates a consistent identity key from type and value.
func LinkedChannels ¶
LinkedChannels extracts the channels list from a user profile.
The list is stored under profile["channels"] as either []string or []any (depending on how it was deserialized). Returns an empty slice if the profile is nil or the channels key is missing/of the wrong type.
Types ¶
type AuthError ¶
type AuthError struct {
Code string // "email_exists", "username_taken", "weak_password", "invalid_format", etc.
Message string // Human-readable message
Field string // Which form field has the error (e.g., "email", "username", "password")
}
AuthError represents a structured authentication error surfaced to a user. Used by both local (username/password) and federated (OAuth/SAML) flows when account-level constraints fail — taken email, duplicate username, weak password, etc.
func NewAuthError ¶
NewAuthError creates a new AuthError.
type AuthErrorHandler ¶
AuthErrorHandler is called when authentication errors occur. The handler receives the structured error and should write the response. Returns true if the error was handled (response written), false to use the default JSON response.
Example implementations:
- Redirect back to form with flash message (app uses their session library)
- Redirect with error in query params: /signup?error=email_exists
- Return JSON error response
- Log and show generic error page
type Channel ¶
type Channel struct {
Provider string `json:"provider"` // "local", "google", "github"
IdentityKey string `json:"identity_key"` // "email:john@example.com"
Credentials map[string]any `json:"credentials"` // password_hash, access_token, etc.
Profile map[string]any `json:"profile"` // optional data from provider
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
ExpiresAt time.Time `json:"expires_at"` // when channel auth expires and needs re-auth
Version int `json:"version"` // optimistic locking version
}
Channel represents an authentication mechanism/provider tied to an Identity.
type ChannelStore ¶
type ChannelStore interface {
// GetChannel gets or optionally creates a channel.
GetChannel(provider string, identityKey string, createIfMissing bool) (channel *Channel, newCreated bool, err error)
// SaveChannel creates or updates a channel (upsert).
SaveChannel(channel *Channel) error
// GetChannelsByIdentity returns all channels for an identity.
GetChannelsByIdentity(identityKey string) ([]*Channel, error)
}
ChannelStore manages authentication channels/providers.
type CredentialsValidator ¶
CredentialsValidator validates a username/password against the host's user store and returns the corresponding account. Used by both localauth's password login (NewCredentialsValidator returns this shape) and apiauth's password-grant token endpoint.
type HandleUserFunc ¶
type HandleUserFunc func(authtype string, provider string, token *oauth2.Token, userInfo map[string]any, w http.ResponseWriter, r *http.Request)
HandleUserFunc is called after successful authentication (OAuth or local) to let the host application complete its session/redirect logic.
type Identity ¶
type Identity struct {
Type string `json:"type"` // "email", "phone"
Value string `json:"value"` // "john@example.com", "+1-555-1234"
UserID string `json:"user_id"` // which user owns this identity
Verified bool `json:"verified"` // has any channel verified this identity?
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Version int `json:"version"` // optimistic locking version
}
Identity represents a contact method (email, phone) that can be verified.
type IdentityStore ¶
type IdentityStore interface {
// GetIdentity gets or optionally creates an identity.
GetIdentity(identityType, identityValue string, createIfMissing bool) (identity *Identity, newCreated bool, err error)
// SaveIdentity creates or updates an identity (upsert).
SaveIdentity(identity *Identity) error
// SetUserForIdentity associates an identity with a user.
SetUserForIdentity(identityType, identityValue string, newUserId string) error
// MarkIdentityVerified marks an identity as verified.
MarkIdentityVerified(identityType, identityValue string) error
// GetUserIdentities returns all identities for a user.
GetUserIdentities(userId string) ([]*Identity, error)
}
IdentityStore manages contact identities (email, phone).
type UserStore ¶
type UserStore interface {
// CreateUser creates a new user with the given ID and profile.
CreateUser(userId string, isActive bool, profile map[string]any) (User, error)
// GetUserById retrieves a user by their ID.
GetUserById(userId string) (User, error)
// SaveUser creates or updates a user (upsert).
SaveUser(user User) error
}
UserStore manages unified user accounts.
type UsernameStore ¶
type UsernameStore interface {
// ReserveUsername reserves a username for a user (creates username -> userID mapping).
// Returns error if username is already taken.
ReserveUsername(username string, userID string) error
// GetUserByUsername looks up a userID by username.
// Returns error if username not found.
GetUserByUsername(username string) (userID string, err error)
// ReleaseUsername removes a username reservation.
ReleaseUsername(username string) error
// ChangeUsername atomically changes a username (release old, reserve new).
// Returns error if new username is already taken.
ChangeUsername(oldUsername, newUsername, userID string) error
}
UsernameStore manages username uniqueness (optional — for apps that need username-based login).