Documentation
¶
Index ¶
- Constants
- Variables
- func ClearAnonymousSessionCookie(w http.ResponseWriter, cfg Config)
- func SetAnonymousSessionCookie(w http.ResponseWriter, token string, cfg Config)
- type Config
- type DeleteAnonymousEventPayload
- type DeleteAnonymousUserResult
- type GenerateEmailCallback
- type GenerateNameCallback
- type LinkAccountCallback
- type LinkAccountEventPayload
- type MemoryRepository
- func (r *MemoryRepository) CreateAnonymousUser(_ context.Context, email, name string) (*entity.User, error)
- func (r *MemoryRepository) CreateSession(_ context.Context, params *dto.CreateSessionParams) (*entity.Session, error)
- func (r *MemoryRepository) DeleteUser(_ context.Context, userID string) error
- func (r *MemoryRepository) DeleteUserSessions(_ context.Context, userID string) error
- func (r *MemoryRepository) GetUserByID(_ context.Context, userID string) (*entity.User, error)
- type OnLinkAccountData
- type Option
- func WithCookieAttributes(domain, path string, sameSite http.SameSite, secure bool) Option
- func WithCookieMaxAge(d time.Duration) Option
- func WithCookieName(name string) Option
- func WithDisableDeleteAnonymousUser(disable bool) Option
- func WithEmailDomainName(domain string) Option
- func WithGenerateName(fn GenerateNameCallback) Option
- func WithGenerateRandomEmail(fn GenerateEmailCallback) Option
- func WithOnLinkAccount(fn LinkAccountCallback) Option
- type Plugin
- func (p *Plugin) Config() Config
- func (p *Plugin) DeleteAnonymousUser(ctx context.Context, session *entity.Session) (*DeleteAnonymousUserResult, error)
- func (p *Plugin) ID() string
- func (p *Plugin) Init(ctx *plugin.Context) error
- func (p *Plugin) LinkAccount(ctx context.Context, data *OnLinkAccountData) error
- func (p *Plugin) PostAuthAccountLinkHook(prevUser *entity.User, prevSess *entity.Session) func(http.Handler) http.Handler
- func (p *Plugin) ServeDeleteAnonymousUser(w http.ResponseWriter, r *http.Request)
- func (p *Plugin) ServeSignInAnonymous(w http.ResponseWriter, r *http.Request)
- func (p *Plugin) SignInAnonymous(ctx context.Context, currentSession *entity.Session, ...) (*SignInAnonymousResult, error)
- type Repository
- type SignInAnonymousEventPayload
- type SignInAnonymousParams
- type SignInAnonymousResult
- type UserSessionPair
Constants ¶
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" )
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 )
const PluginID = "anonymous"
PluginID is the unique string identifier for the Anonymous plugin ("anonymous").
Variables ¶
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 ¶
GenerateEmailCallback is a function signature for generating a custom random email address for an anonymous user.
type GenerateNameCallback ¶
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 ¶
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 ¶
WithCookieAttributes sets session cookie domain, path, SameSite, and Secure flags.
func WithCookieMaxAge ¶
WithCookieMaxAge sets a custom duration for session cookies.
func WithCookieName ¶
WithCookieName sets a custom cookie name for session cookies.
func WithDisableDeleteAnonymousUser ¶
WithDisableDeleteAnonymousUser toggles whether anonymous users should remain in storage after account linking.
func WithEmailDomainName ¶
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 ¶
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) 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) 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.