accounts

package
v0.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 26, 2026 License: Apache-2.0 Imports: 3 Imported by: 0

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

View Source
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

func DetectUsernameType(username string) string

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

func IdentityKey(identityType, identityValue string) string

IdentityKey creates a consistent identity key from type and value.

func LinkedChannels

func LinkedChannels(profile map[string]any) []string

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

func NewAuthError(code, message, field string) *AuthError

NewAuthError creates a new AuthError.

func (*AuthError) Error

func (e *AuthError) Error() string

type AuthErrorHandler

type AuthErrorHandler func(err *AuthError, w http.ResponseWriter, r *http.Request) bool

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 BasicUser

type BasicUser struct {
	ID          string
	ProfileData map[string]any
}

BasicUser is a simple implementation of the User interface.

func (*BasicUser) Id

func (b *BasicUser) Id() string

func (*BasicUser) Profile

func (b *BasicUser) Profile() map[string]any

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.

func (*Channel) IsExpired

func (c *Channel) IsExpired() bool

IsExpired returns true if the channel has an expiration time set and it has passed.

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

type CredentialsValidator func(username, password, usernameType string) (User, error)

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 User

type User interface {
	Id() string
	Profile() map[string]any
}

User represents a unified user account.

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).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL