accounts

package
v0.1.21 Latest Latest
Warning

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

Go to latest
Published: Jun 9, 2026 License: Apache-2.0 Imports: 4 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 ChangeUsernameRequest added in v0.1.7

type ChangeUsernameRequest struct {
	OldUsername string
	NewUsername string
	UserID      string
}

ChangeUsernameRequest is the input to UsernameStore.ChangeUsername.

type ChangeUsernameResponse added in v0.1.7

type ChangeUsernameResponse struct{}

ChangeUsernameResponse is the output of UsernameStore.ChangeUsername.

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(ctx context.Context, req *GetChannelRequest) (*GetChannelResponse, error)

	// SaveChannel creates or updates a channel (upsert).
	SaveChannel(ctx context.Context, req *SaveChannelRequest) (*SaveChannelResponse, error)

	// GetChannelsByIdentity returns all channels for an identity.
	GetChannelsByIdentity(ctx context.Context, req *GetChannelsByIdentityRequest) (*GetChannelsByIdentityResponse, error)
}

ChannelStore manages authentication channels/providers.

type CreateUserRequest added in v0.1.7

type CreateUserRequest struct {
	UserID   string
	IsActive bool
	Profile  map[string]any
}

CreateUserRequest is the input to UserStore.CreateUser.

type CreateUserResponse added in v0.1.7

type CreateUserResponse struct {
	User User
}

CreateUserResponse wraps the created user.

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 GetChannelRequest added in v0.1.7

type GetChannelRequest struct {
	Provider        string
	IdentityKey     string
	CreateIfMissing bool
}

GetChannelRequest is the input to ChannelStore.GetChannel.

type GetChannelResponse added in v0.1.7

type GetChannelResponse struct {
	Channel    *Channel
	NewCreated bool
}

GetChannelResponse wraps the looked-up Channel and whether it was newly created in this call.

type GetChannelsByIdentityRequest added in v0.1.7

type GetChannelsByIdentityRequest struct {
	IdentityKey string
}

GetChannelsByIdentityRequest is the input to ChannelStore.GetChannelsByIdentity.

type GetChannelsByIdentityResponse added in v0.1.7

type GetChannelsByIdentityResponse struct {
	Channels []*Channel
}

GetChannelsByIdentityResponse wraps the channels for an identity.

type GetIdentityRequest added in v0.1.7

type GetIdentityRequest struct {
	IdentityType    string
	IdentityValue   string
	CreateIfMissing bool
}

GetIdentityRequest is the input to IdentityStore.GetIdentity.

type GetIdentityResponse added in v0.1.7

type GetIdentityResponse struct {
	Identity   *Identity
	NewCreated bool
}

GetIdentityResponse wraps the looked-up Identity and whether it was newly created in this call.

type GetUserByIDRequest added in v0.1.7

type GetUserByIDRequest struct {
	UserID string
}

GetUserByIDRequest is the input to UserStore.GetUserById.

type GetUserByIDResponse added in v0.1.7

type GetUserByIDResponse struct {
	User User
}

GetUserByIDResponse wraps the requested user.

type GetUserByUsernameRequest added in v0.1.7

type GetUserByUsernameRequest struct {
	Username string
}

GetUserByUsernameRequest is the input to UsernameStore.GetUserByUsername.

type GetUserByUsernameResponse added in v0.1.7

type GetUserByUsernameResponse struct {
	UserID string
}

GetUserByUsernameResponse wraps the userID for the queried username.

type GetUserIdentitiesRequest added in v0.1.7

type GetUserIdentitiesRequest struct {
	UserID string
}

GetUserIdentitiesRequest is the input to IdentityStore.GetUserIdentities.

type GetUserIdentitiesResponse added in v0.1.7

type GetUserIdentitiesResponse struct {
	Identities []*Identity
}

GetUserIdentitiesResponse wraps the identities owned by the user.

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(ctx context.Context, req *GetIdentityRequest) (*GetIdentityResponse, error)

	// SaveIdentity creates or updates an identity (upsert).
	SaveIdentity(ctx context.Context, req *SaveIdentityRequest) (*SaveIdentityResponse, error)

	// SetUserForIdentity associates an identity with a user.
	SetUserForIdentity(ctx context.Context, req *SetUserForIdentityRequest) (*SetUserForIdentityResponse, error)

	// MarkIdentityVerified marks an identity as verified.
	MarkIdentityVerified(ctx context.Context, req *MarkIdentityVerifiedRequest) (*MarkIdentityVerifiedResponse, error)

	// GetUserIdentities returns all identities for a user.
	GetUserIdentities(ctx context.Context, req *GetUserIdentitiesRequest) (*GetUserIdentitiesResponse, error)
}

IdentityStore manages contact identities (email, phone).

type MarkIdentityVerifiedRequest added in v0.1.7

type MarkIdentityVerifiedRequest struct {
	IdentityType  string
	IdentityValue string
}

MarkIdentityVerifiedRequest is the input to IdentityStore.MarkIdentityVerified.

type MarkIdentityVerifiedResponse added in v0.1.7

type MarkIdentityVerifiedResponse struct{}

MarkIdentityVerifiedResponse is the output of IdentityStore.MarkIdentityVerified.

type ReleaseUsernameRequest added in v0.1.7

type ReleaseUsernameRequest struct {
	Username string
}

ReleaseUsernameRequest is the input to UsernameStore.ReleaseUsername.

type ReleaseUsernameResponse added in v0.1.7

type ReleaseUsernameResponse struct{}

ReleaseUsernameResponse is the output of UsernameStore.ReleaseUsername.

type ReserveUsernameRequest added in v0.1.7

type ReserveUsernameRequest struct {
	Username string
	UserID   string
}

ReserveUsernameRequest is the input to UsernameStore.ReserveUsername.

type ReserveUsernameResponse added in v0.1.7

type ReserveUsernameResponse struct{}

ReserveUsernameResponse is the output of UsernameStore.ReserveUsername.

type SaveChannelRequest added in v0.1.7

type SaveChannelRequest struct {
	Channel *Channel
}

SaveChannelRequest is the input to ChannelStore.SaveChannel.

type SaveChannelResponse added in v0.1.7

type SaveChannelResponse struct{}

SaveChannelResponse is the output of ChannelStore.SaveChannel.

type SaveIdentityRequest added in v0.1.7

type SaveIdentityRequest struct {
	Identity *Identity
}

SaveIdentityRequest is the input to IdentityStore.SaveIdentity.

type SaveIdentityResponse added in v0.1.7

type SaveIdentityResponse struct{}

SaveIdentityResponse is the output of IdentityStore.SaveIdentity.

type SaveUserRequest added in v0.1.7

type SaveUserRequest struct {
	User User
}

SaveUserRequest is the input to UserStore.SaveUser.

type SaveUserResponse added in v0.1.7

type SaveUserResponse struct{}

SaveUserResponse is the output of UserStore.SaveUser.

type SetUserForIdentityRequest added in v0.1.7

type SetUserForIdentityRequest struct {
	IdentityType  string
	IdentityValue string
	NewUserID     string
}

SetUserForIdentityRequest is the input to IdentityStore.SetUserForIdentity.

type SetUserForIdentityResponse added in v0.1.7

type SetUserForIdentityResponse struct{}

SetUserForIdentityResponse is the output of IdentityStore.SetUserForIdentity.

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(ctx context.Context, req *CreateUserRequest) (*CreateUserResponse, error)

	// GetUserById retrieves a user by their ID.
	GetUserById(ctx context.Context, req *GetUserByIDRequest) (*GetUserByIDResponse, error)

	// SaveUser creates or updates a user (upsert).
	SaveUser(ctx context.Context, req *SaveUserRequest) (*SaveUserResponse, 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(ctx context.Context, req *ReserveUsernameRequest) (*ReserveUsernameResponse, error)

	// GetUserByUsername looks up a userID by username.
	// Returns error if username not found.
	GetUserByUsername(ctx context.Context, req *GetUserByUsernameRequest) (*GetUserByUsernameResponse, error)

	// ReleaseUsername removes a username reservation.
	ReleaseUsername(ctx context.Context, req *ReleaseUsernameRequest) (*ReleaseUsernameResponse, error)

	// ChangeUsername atomically changes a username (release old, reserve new).
	// Returns error if new username is already taken.
	ChangeUsername(ctx context.Context, req *ChangeUsernameRequest) (*ChangeUsernameResponse, 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