anonymous

package
v0.27.0 Latest Latest
Warning

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

Go to latest
Published: Aug 28, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// EventSignInAnonymousBefore is dispatched prior to creating an anonymous user and session.
	EventSignInAnonymousBefore = "anonymous:sign_in:before"

	// EventSignInAnonymousAfter is dispatched immediately after successfully creating an anonymous user and session.
	EventSignInAnonymousAfter = "anonymous:sign_in:after"

	// EventDeleteAnonymousBefore is dispatched prior to purging an anonymous user and their sessions.
	EventDeleteAnonymousBefore = "anonymous:delete:before"

	// EventDeleteAnonymousAfter is dispatched immediately after purging an anonymous user and their sessions.
	EventDeleteAnonymousAfter = "anonymous:delete:after"

	// EventLinkAccountAfter is dispatched after successfully linking an anonymous account to a new permanent account.
	EventLinkAccountAfter = "anonymous:link_account:after"
)
View Source
const (
	// DefaultEmailDomain is the default domain name used when constructing temporary anonymous emails.
	DefaultEmailDomain = "anonymous.local"

	// DefaultCookieName is the standard session cookie key.
	DefaultCookieName = "modular-auth.session_token"

	// DefaultCookieMaxAge specifies the default cookie duration (30 days).
	DefaultCookieMaxAge = 30 * 24 * time.Hour
)
View Source
const PluginID = "anonymous"

PluginID is the unique string identifier for the Anonymous plugin ("anonymous").

Variables

View Source
var (
	// ErrInvalidEmailFormat is returned when an anonymous email address fails syntax validation.
	ErrInvalidEmailFormat = errors.New("anonymous: invalid email format")

	// ErrFailedToCreateUser is returned when database insertion of an anonymous user record fails.
	ErrFailedToCreateUser = errors.New("anonymous: failed to create anonymous user")

	// ErrCouldNotCreateSession is returned when database insertion of an anonymous session record fails.
	ErrCouldNotCreateSession = errors.New("anonymous: failed to create session")

	// ErrAnonymousUsersCannotSignInAgain is returned when a user with an active anonymous session attempts to sign in anonymously again.
	ErrAnonymousUsersCannotSignInAgain = errors.New("anonymous: active anonymous user cannot sign in as anonymous again")

	// ErrFailedToDeleteAnonymousUser is returned when database deletion of an anonymous user record fails.
	ErrFailedToDeleteAnonymousUser = errors.New("anonymous: failed to delete anonymous user")

	// ErrFailedToDeleteAnonymousUserSessions is returned when purging sessions for an anonymous user fails.
	ErrFailedToDeleteAnonymousUserSessions = errors.New("anonymous: failed to delete user sessions")

	// ErrUserIsNotAnonymous is returned when an operation intended for guest accounts is attempted on a non-anonymous user.
	ErrUserIsNotAnonymous = errors.New("anonymous: user is not an anonymous account")

	// ErrDeleteAnonymousUserDisabled is returned when attempting to delete an anonymous account while DisableDeleteAnonymousUser is active.
	ErrDeleteAnonymousUserDisabled = errors.New("anonymous: deletion of anonymous users is disabled by configuration")

	// ErrUserNotFound is returned when no user matches the queried user ID.
	ErrUserNotFound = errors.New("anonymous: user not found")

	// ErrRepositoryRequired is returned when initializing the Anonymous plugin without a configured Repository.
	ErrRepositoryRequired = errors.New("anonymous: repository implementation is required")
)

Functions

func ClearAnonymousSessionCookie

func ClearAnonymousSessionCookie(w http.ResponseWriter, cfg Config)

ClearAnonymousSessionCookie expires and deletes the session token cookie.

func SetAnonymousSessionCookie

func SetAnonymousSessionCookie(w http.ResponseWriter, token string, cfg Config)

SetAnonymousSessionCookie sets the session token cookie on the HTTP response writer.

Types

type Config

type Config struct {
	// EmailDomainName specifies the domain used for generated anonymous email addresses (e.g. temp-{uuid}@anonymous.local).
	// Default: "anonymous.local"
	EmailDomainName string

	// DisableDeleteAnonymousUser specifies whether automatic deletion of anonymous accounts after linking should be disabled.
	// Default: false
	DisableDeleteAnonymousUser bool

	// OnLinkAccount is a callback function invoked when a guest user links their account to a permanent user.
	OnLinkAccount LinkAccountCallback

	// GenerateName is a custom callback to generate anonymous user names. If nil, defaults to "Anonymous".
	GenerateName GenerateNameCallback

	// GenerateRandomEmail is a custom callback to generate anonymous email addresses. If nil, defaults to "temp-{uuid}@" + EmailDomainName.
	GenerateRandomEmail GenerateEmailCallback

	// CookieName specifies the session cookie key name.
	CookieName string

	// CookiePath specifies the HTTP cookie path scope. Default: "/"
	CookiePath string

	// CookieDomain specifies the HTTP cookie domain scope.
	CookieDomain string

	// CookieMaxAge specifies the session cookie duration. Default: 30 days.
	CookieMaxAge time.Duration

	// CookieSecure specifies whether the session cookie requires HTTPS. Default: false.
	CookieSecure bool

	// CookieSameSite specifies the SameSite attribute for session cookies. Default: http.SameSiteLaxMode.
	CookieSameSite http.SameSite
}

Config defines configuration parameters for the Anonymous plugin.

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns a Config struct initialized with recommended defaults.

type DeleteAnonymousEventPayload

type DeleteAnonymousEventPayload struct {
	UserID string `json:"user_id"`
}

DeleteAnonymousEventPayload represents the payload broadcasted during anonymous user deletion events.

type DeleteAnonymousUserResult

type DeleteAnonymousUserResult struct {
	Success bool `json:"success"`
}

DeleteAnonymousUserResult indicates whether the anonymous user deletion completed successfully.

type GenerateEmailCallback

type GenerateEmailCallback func(ctx context.Context) (string, error)

GenerateEmailCallback is a function signature for generating a custom random email address for an anonymous user.

type GenerateNameCallback

type GenerateNameCallback func(ctx context.Context) (string, error)

GenerateNameCallback is a function signature for generating a custom display name for an anonymous user.

type LinkAccountCallback

type LinkAccountCallback func(ctx context.Context, data *OnLinkAccountData) error

LinkAccountCallback is a function signature for custom account linking / data migration logic.

type LinkAccountEventPayload

type LinkAccountEventPayload struct {
	Data *OnLinkAccountData `json:"data"`
}

LinkAccountEventPayload represents the payload broadcasted when an anonymous account is linked to a permanent user.

type MemoryRepository

type MemoryRepository struct {
	// contains filtered or unexported fields
}

MemoryRepository provides a thread-safe, in-memory implementation of Repository for testing and lightweight usage.

func NewMemoryRepository

func NewMemoryRepository() *MemoryRepository

NewMemoryRepository initializes a fresh MemoryRepository instance.

func (*MemoryRepository) CreateAnonymousUser

func (r *MemoryRepository) CreateAnonymousUser(_ context.Context, email, name string) (*entity.User, error)

CreateAnonymousUser stores a new anonymous user record in memory.

func (*MemoryRepository) CreateSession

func (r *MemoryRepository) CreateSession(_ context.Context, params *dto.CreateSessionParams) (*entity.Session, error)

CreateSession stores a new session record in memory.

func (*MemoryRepository) DeleteUser

func (r *MemoryRepository) DeleteUser(_ context.Context, userID string) error

DeleteUser removes a user record from memory by ID.

func (*MemoryRepository) DeleteUserSessions

func (r *MemoryRepository) DeleteUserSessions(_ context.Context, userID string) error

DeleteUserSessions removes all session records matching the specified user ID from memory.

func (*MemoryRepository) GetUserByID

func (r *MemoryRepository) GetUserByID(_ context.Context, userID string) (*entity.User, error)

GetUserByID retrieves a user record from memory by ID.

type OnLinkAccountData

type OnLinkAccountData struct {
	AnonymousUser UserSessionPair `json:"anonymous_user"`
	NewUser       UserSessionPair `json:"new_user"`
}

OnLinkAccountData contains previous anonymous account details and new authenticated user details during account linking.

type Option

type Option func(*Config)

Option defines a functional option type for configuring the Anonymous plugin.

func WithCookieAttributes

func WithCookieAttributes(domain, path string, sameSite http.SameSite, secure bool) Option

WithCookieAttributes sets session cookie domain, path, SameSite, and Secure flags.

func WithCookieMaxAge

func WithCookieMaxAge(d time.Duration) Option

WithCookieMaxAge sets a custom duration for session cookies.

func WithCookieName

func WithCookieName(name string) Option

WithCookieName sets a custom cookie name for session cookies.

func WithDisableDeleteAnonymousUser

func WithDisableDeleteAnonymousUser(disable bool) Option

WithDisableDeleteAnonymousUser toggles whether anonymous users should remain in storage after account linking.

func WithEmailDomainName

func WithEmailDomainName(domain string) Option

WithEmailDomainName sets a custom domain for generated anonymous emails (e.g. "guest.app.com").

func WithGenerateName

func WithGenerateName(fn GenerateNameCallback) Option

WithGenerateName sets a custom function to generate display names for anonymous users.

func WithGenerateRandomEmail

func WithGenerateRandomEmail(fn GenerateEmailCallback) Option

WithGenerateRandomEmail sets a custom function to generate email addresses for anonymous users.

func WithOnLinkAccount

func WithOnLinkAccount(fn LinkAccountCallback) Option

WithOnLinkAccount sets a custom callback function for account linking and data migration.

type Plugin

type Plugin struct {
	// contains filtered or unexported fields
}

Plugin implements the guest sessions (anonymous users) authentication plugin for go-modular-auth.

func New

func New(opts ...Option) *Plugin

New instantiates a new Anonymous plugin configured with optional functional options and MemoryRepository.

func NewWithRepository

func NewWithRepository(repo Repository, opts ...Option) *Plugin

NewWithRepository instantiates a new Anonymous plugin with a custom Repository implementation.

func (*Plugin) Config

func (p *Plugin) Config() Config

Config returns a copy of the active plugin configuration.

func (*Plugin) DeleteAnonymousUser

func (p *Plugin) DeleteAnonymousUser(ctx context.Context, session *entity.Session) (*DeleteAnonymousUserResult, error)

DeleteAnonymousUser purges an anonymous user and all their active sessions.

func (*Plugin) ID

func (p *Plugin) ID() string

ID returns the unique string identifier for the plugin ("anonymous").

func (*Plugin) Init

func (p *Plugin) Init(ctx *plugin.Context) error

Init initializes the plugin with the shared execution context.

func (*Plugin) LinkAccount

func (p *Plugin) LinkAccount(ctx context.Context, data *OnLinkAccountData) error

LinkAccount triggers account linking callbacks and purges the previous anonymous account if enabled.

func (*Plugin) PostAuthAccountLinkHook

func (p *Plugin) PostAuthAccountLinkHook(prevUser *entity.User, prevSess *entity.Session) func(http.Handler) http.Handler

PostAuthAccountLinkHook creates a net/http middleware that automatically detects when a guest user transitions to an authenticated permanent user, triggering OnLinkAccount and account cleanup.

func (*Plugin) ServeDeleteAnonymousUser

func (p *Plugin) ServeDeleteAnonymousUser(w http.ResponseWriter, r *http.Request)

ServeDeleteAnonymousUser handles HTTP POST /delete-anonymous-user requests.

func (*Plugin) ServeSignInAnonymous

func (p *Plugin) ServeSignInAnonymous(w http.ResponseWriter, r *http.Request)

ServeSignInAnonymous handles HTTP POST /sign-in/anonymous requests.

func (*Plugin) SignInAnonymous

func (p *Plugin) SignInAnonymous(ctx context.Context, currentSession *entity.Session, params SignInAnonymousParams) (*SignInAnonymousResult, error)

SignInAnonymous creates a new temporary guest user and session, or rejects if the active session is already anonymous.

type Repository

type Repository interface {
	// CreateAnonymousUser persists a new guest user entity with IsAnonymous set to true.
	//
	// Function:
	//   Called during anonymous sign-in (POST /sign-in/anonymous) to create a temporary guest user.
	//
	// Storage:
	//   Database (GORM / SQL) - Inserts a record into the users table with is_anonymous = true.
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//   - email: Generated temporary email address (e.g. temp-{uuid}@anonymous.local).
	//   - name: Display name for the anonymous user (e.g. "Anonymous").
	//
	// Returns:
	//   - *entity.User: Newly created user entity.
	//   - error: ErrFailedToCreateUser or infrastructure database error.
	//
	// Example SQL:
	//   INSERT INTO users (id, name, email, is_anonymous, created_at, updated_at) VALUES ($1, $2, $3, true, NOW(), NOW()) RETURNING *;
	CreateAnonymousUser(ctx context.Context, email, name string) (*entity.User, error)

	// CreateSession persists a new session associated with an anonymous user.
	//
	// Function:
	//   Called during anonymous sign-in to issue and store a valid session token.
	//
	// Storage:
	//   Database (GORM / SQL) - Inserts a record into the sessions table.
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//   - params: Session parameters including UserID, Token, IPAddress, UserAgent, and Expiry.
	//
	// Returns:
	//   - *entity.Session: Newly created session entity.
	//   - error: ErrCouldNotCreateSession or infrastructure database error.
	//
	// Example SQL:
	//   INSERT INTO sessions (id, user_id, token, ip_address, user_agent, expires_at, created_at) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING *;
	CreateSession(ctx context.Context, params *dto.CreateSessionParams) (*entity.Session, error)

	// GetUserByID fetches a user entity by primary key ID.
	//
	// Function:
	//   Used to verify whether a user exists and check their IsAnonymous status.
	//
	// Storage:
	//   Both (Cache-Aside Strategy) - Relational DB lookup with optional Redis caching.
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//   - userID: Target user primary key identifier.
	//
	// Returns:
	//   - *entity.User: User entity if found.
	//   - error: ErrUserNotFound if missing, or infrastructure error.
	//
	// Example SQL:
	//   SELECT id, name, email, is_anonymous, created_at, updated_at FROM users WHERE id = $1 LIMIT 1;
	GetUserByID(ctx context.Context, userID string) (*entity.User, error)

	// DeleteUser removes a user record from persistent storage by ID.
	//
	// Function:
	//   Invoked during anonymous account cleanup (after linking or explicit deletion).
	//
	// Storage:
	//   Database (GORM / SQL) - Deletes record from users table.
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//   - userID: Target user primary key identifier to purge.
	//
	// Returns:
	//   - error: Nil on success, ErrUserNotFound if missing, or ErrFailedToDeleteAnonymousUser.
	//
	// Example SQL:
	//   DELETE FROM users WHERE id = $1 AND is_anonymous = true;
	DeleteUser(ctx context.Context, userID string) error

	// DeleteUserSessions removes all active session records for a specified user ID.
	//
	// Function:
	//   Invoked prior to purging an anonymous user to invalidate all active session tokens.
	//
	// Storage:
	//   Database (GORM / SQL) - Deletes associated records from sessions table.
	//
	// Arguments:
	//   - ctx: Request cancellation context.
	//   - userID: Target user primary key identifier whose sessions should be removed.
	//
	// Returns:
	//   - error: Nil on success, or ErrFailedToDeleteAnonymousUserSessions.
	//
	// Example SQL:
	//   DELETE FROM sessions WHERE user_id = $1;
	DeleteUserSessions(ctx context.Context, userID string) error
}

Repository defines the persistent storage contract required by the Anonymous plugin. Implement this interface on your custom database adapter (e.g. PostgreSQL, MySQL, SQLite, MongoDB, GORM, Redis).

Implementation Example (GORM / database/sql):

type GormAnonymousRepository struct {
	db *gorm.DB
}

func (r *GormAnonymousRepository) CreateAnonymousUser(ctx context.Context, email, name string) (*entity.User, error) {
	u := &entity.User{
		ID:          uuid.NewString(),
		Name:        name,
		Email:       email,
		IsAnonymous: true,
		CreatedAt:   time.Now(),
		UpdatedAt:   time.Now(),
	}
	if err := r.db.WithContext(ctx).Create(u).Error; err != nil {
		return nil, anonymous.ErrFailedToCreateUser
	}
	return u, nil
}

Storage and Caching Recommendation (Redis / Cache-Aside Strategy):

Because anonymous user validation occurs frequently on guest interactions, decorating repository queries with Redis or an in-memory Cache-Aside layer optimizes performance:

type CachedAnonymousRepository struct {
	dbRepo anonymous.Repository
	redis  *redis.Client
	ttl    time.Duration
}

func (r *CachedAnonymousRepository) GetUserByID(ctx context.Context, userID string) (*entity.User, error) {
	cacheKey := "user:" + userID
	val, err := r.redis.Get(ctx, cacheKey).Bytes()
	if err == nil {
		var u entity.User
		if json.Unmarshal(val, &u) == nil {
			return &u, nil
		}
	}
	u, err := r.dbRepo.GetUserByID(ctx, userID)
	if err != nil {
		return nil, err
	}
	bytes, _ := json.Marshal(u)
	r.redis.Set(ctx, cacheKey, bytes, r.ttl)
	return u, nil
}

type SignInAnonymousEventPayload

type SignInAnonymousEventPayload struct {
	User    *entity.User    `json:"user"`
	Session *entity.Session `json:"session"`
}

SignInAnonymousEventPayload represents the payload broadcasted during anonymous sign-in events.

type SignInAnonymousParams

type SignInAnonymousParams struct {
	IPAddress string `json:"ip_address,omitempty"`
	UserAgent string `json:"user_agent,omitempty"`
}

SignInAnonymousParams holds optional request parameters when initiating an anonymous sign-in session.

type SignInAnonymousResult

type SignInAnonymousResult struct {
	User    *entity.User    `json:"user"`
	Session *entity.Session `json:"session"`
	Token   string          `json:"token"`
}

SignInAnonymousResult contains the created anonymous User, Session, and raw token.

type UserSessionPair

type UserSessionPair struct {
	User    *entity.User    `json:"user"`
	Session *entity.Session `json:"session"`
}

UserSessionPair pairs an entity.User with their active entity.Session.

Jump to

Keyboard shortcuts

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