anonymous

package
v1.3.0 Latest Latest
Warning

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

Go to latest
Published: Oct 23, 2026 License: MIT, MIT Imports: 20 Imported by: 0

README

gomodularauth-anonymous

An official production-ready plugin for GoModularAuth that provides anonymous user identities, session issuance, lifecycle expiration, in-place conversion to permanent authenticated accounts, and account linking/merging with explicit authentication and privilege escalation prevention.

Installation

go get github.com/BladiCreator/go-modular-auth/plugins/anonymous

Quick Start

package main

import (
	"log"
	"time"

	auth "github.com/BladiCreator/go-modular-auth"
	adaptermem "github.com/BladiCreator/go-modular-auth/adapters/memory"
	"github.com/BladiCreator/go-modular-auth/plugins/anonymous"
	"github.com/BladiCreator/go-modular-auth/plugins/anonymous/plugins"
)

func main() {
	engine, err := auth.New(auth.Config{
		Adapter: adaptermem.NewAdapter(),
		Cache:   adaptermem.NewCache(),
		Plugins: []auth.PluginModule{
			anonymous.New(
				anonymous.WithIdentityTTL(14 * 24 * time.Hour),
			),
		},
	})
	if err != nil {
		log.Fatalf("failed to initialize auth: %v", err)
	}

	// Retrieve capability
	anon, err := auth.Get[plugins.Anonymous](engine)
	if err != nil {
		log.Fatalf("failed to retrieve Anonymous plugin: %v", err)
	}
}

Detailed guide: doc/plugins/anonymous.md.

Documentation

Overview

Package anonymous provides anonymous authentication identities and lifecycle management with optional conversion and account linking to permanent authenticated users for GoModularAuth.

Package anonymous provides guest authentication and deferred account conversion.

Index

Constants

View Source
const (
	DefaultIdentityTTL       = 30 * 24 * time.Hour
	DefaultSessionTTL        = 30 * 24 * time.Hour
	DefaultCacheTTL          = 5 * time.Minute
	DefaultEmailDomain       = "anonymous.invalid"
	DefaultMinPasswordLength = 8
	DefaultMaxPasswordLength = 128
)

Default settings for anonymous identity management.

View Source
const (
	// EventAnonymousCreated is published after an anonymous user and session are successfully created.
	EventAnonymousCreated auth.EventName = "anonymous.created"

	// EventAnonymousConverted is published after an anonymous identity is converted to a permanent account.
	EventAnonymousConverted auth.EventName = "anonymous.converted"

	// EventAnonymousLinked is published after an anonymous identity is linked to an existing permanent account.
	EventAnonymousLinked auth.EventName = "anonymous.linked"

	// EventAnonymousCleanedUp is published after expired anonymous identities have been purged.
	EventAnonymousCleanedUp auth.EventName = "anonymous.cleaned_up"
)
View Source
const (
	ModelAnonymousIdentities = "anonymous_identities"
)
View Source
const PrincipalAnonymous auth.PrincipalType = "anonymous"

PrincipalAnonymous is the principal type classifying an authenticated anonymous user. Authorization policies and middleware should inspect this to deny anonymous users access to privileged operations reserved for permanent users.

View Source
const ProviderAnonymous = "anonymous"

ProviderAnonymous is the Core account provider name recorded when creating anonymous accounts.

Variables

View Source
var (
	// ErrAnonymousNotFound is returned when an anonymous identity cannot be found.
	ErrAnonymousNotFound = errors.New("anonymous: identity not found")

	// ErrAnonymousExpired is returned when the anonymous identity has passed its expiration time.
	ErrAnonymousExpired = errors.New("anonymous: identity has expired")

	// ErrAlreadyConverted is returned when an operation expects an unconverted anonymous identity,
	// but the identity has already been converted to a permanent account or linked.
	ErrAlreadyConverted = errors.New("anonymous: identity has already been converted")

	// ErrUserNotAnonymous is returned when an operation requires an anonymous user,
	// but the user is already a permanent user with no active anonymous identity.
	ErrUserNotAnonymous = errors.New("anonymous: user is not an anonymous identity")

	// ErrInvalidAnonymousToken is returned when an anonymous session token is invalid, expired, or revoked.
	ErrInvalidAnonymousToken = errors.New("anonymous: invalid or expired anonymous session token")

	// ErrInvalidCredentials is returned when verification of target permanent account credentials fails during linking.
	ErrInvalidCredentials = errors.New("anonymous: invalid permanent account credentials")

	// ErrUserAlreadyExists is returned when attempting to convert to an email address that already belongs to another user.
	ErrUserAlreadyExists = errors.New("anonymous: user with this email already exists")

	// ErrInvalidEmail is returned when the email format provided during conversion is invalid.
	ErrInvalidEmail = errors.New("anonymous: invalid email address")

	// ErrInvalidPassword is returned when the password provided during conversion does not meet complexity/length policy.
	ErrInvalidPassword = errors.New("anonymous: invalid password")

	// ErrSameAccount is returned when attempting to link an anonymous user to themselves.
	ErrSameAccount = errors.New("anonymous: cannot link account to itself")

	// ErrTargetIsAnonymous is returned when attempting to link an anonymous user to another anonymous user.
	ErrTargetIsAnonymous = errors.New("anonymous: target account must be a permanent user, not an anonymous identity")

	// ErrStoreRequired is returned when initializing the plugin without a configured Store.
	ErrStoreRequired = errors.New("anonymous: store is required")

	// ErrProviderRequired is returned when converting with an external account without specifying a provider name.
	ErrProviderRequired = errors.New("anonymous: provider is required")

	// ErrProviderIDRequired is returned when converting with an external account without specifying a provider ID.
	ErrProviderIDRequired = errors.New("anonymous: provider ID is required")
)

Functions

func RegisterSchema added in v1.3.0

func RegisterSchema(s *schema.Schema) error

RegisterSchema registers the anonymous_identities model with the unified schema.

Types

type Anonymous added in v1.3.0

type Anonymous interface {
	// Authenticate creates a new Core User, links an anonymous account, persists an anonymous identity,
	// and issues an authenticated session.
	Authenticate(ctx context.Context, in AuthenticateInput) (*AuthResult, error)

	// GetIdentity retrieves the anonymous identity associated with the given Core User ID.
	// Returns ErrAnonymousNotFound if the user does not have an anonymous record.
	GetIdentity(ctx context.Context, userID string) (*Identity, error)

	// IsAnonymous reports whether the given Core User ID corresponds to an active, unexpired,
	// and unconverted anonymous identity.
	IsAnonymous(ctx context.Context, userID string) (bool, error)

	// Principal returns an auth.Principal for the given user ID.
	// If the user has an active anonymous identity, Principal.Type is PrincipalAnonymous ("anonymous").
	// Otherwise, it returns Principal.Type as auth.PrincipalUser ("user").
	Principal(ctx context.Context, userID string) (auth.Principal, error)

	// Convert converts an active anonymous user in-place to a permanent account with email and password credentials.
	// All existing application relations to the user ID are preserved.
	Convert(ctx context.Context, in ConvertInput) (*ConvertResult, error)

	// ConvertWithAccount converts an active anonymous user by linking an external provider account (e.g. OAuth, passkey).
	ConvertWithAccount(ctx context.Context, in ConvertAccountInput) (*ConvertResult, error)

	// Link merges an anonymous identity into an existing permanent account after validating explicit authentication
	// for the permanent user. It prevents privilege escalation and triggers account link events.
	Link(ctx context.Context, in LinkInput) (*LinkResult, error)

	// Cleanup deletes expired and unconverted anonymous identities and their associated core users and sessions.
	// Passing a zero Time defaults to the current clock time.
	Cleanup(ctx context.Context, before time.Time) (CleanupResult, error)
}

Anonymous is the strongly typed capability interface exposed by the anonymous plugin module. Applications retrieve it using:

anon, err := auth.Get[plugins.Anonymous](engine)
// or
anon, err := auth.Get[anonymous.Anonymous](engine)

type AuthResult added in v1.3.0

type AuthResult struct {
	// User is the created GoModularAuth Core user entity.
	User *user.User

	// Session is the created GoModularAuth Core session entity.
	Session *session.Session

	// Token is the plaintext opaque session token returned once to the client.
	Token string

	// Identity is the persistent anonymous identity record.
	Identity *Identity
}

AuthResult is the result returned upon successful anonymous authentication.

type AuthenticateInput added in v1.3.0

type AuthenticateInput struct {
	// Session carries optional IPAddress and UserAgent recorded on the newly created server session.
	Session auth.SessionOptions

	// Metadata carries optional application-specific key-value pairs stored with the anonymous identity.
	Metadata map[string]string
}

AuthenticateInput holds options for creating an anonymous identity and session.

type CleanedUpPayload added in v1.3.0

type CleanedUpPayload struct {
	IdentitiesDeleted int       `json:"identities_deleted"`
	UsersDeleted      int       `json:"users_deleted"`
	SessionsRevoked   int       `json:"sessions_revoked"`
	CleanedAt         time.Time `json:"cleaned_at"`
}

CleanedUpPayload is the event payload for EventAnonymousCleanedUp.

type CleanupInput added in v1.3.0

type CleanupInput struct {
	BeforeThreshold string
}

CleanupInput passes the threshold time to before cleanup hooks.

type CleanupResult added in v1.3.0

type CleanupResult struct {
	// IdentitiesDeleted is the number of expired anonymous identity records deleted from the store.
	IdentitiesDeleted int

	// UsersDeleted is the number of orphaned Core users deleted.
	UsersDeleted int

	// SessionsRevoked is the number of active sessions revoked during cleanup.
	SessionsRevoked int
}

CleanupResult summarizes the outcome of a cleanup operation on expired anonymous identities.

type Config

type Config struct {
	// IdentityTTL is the duration after which an unconverted anonymous identity expires.
	IdentityTTL time.Duration

	// SessionTTL is the duration granted to anonymous sessions.
	SessionTTL time.Duration

	// CacheTTL is the time-to-live for cached anonymous identities.
	CacheTTL time.Duration

	// EmailDomain is the synthetic domain used for generated placeholder emails (e.g. "anonymous.invalid").
	EmailDomain string

	// MinPasswordLength is the minimum allowed password length for permanent accounts during conversion.
	MinPasswordLength int

	// MaxPasswordLength is the maximum allowed password length for permanent accounts during conversion.
	MaxPasswordLength int

	// Hasher is the password hashing implementation used during account conversion.
	// Defaults to Argon2id if nil.
	Hasher password.Hasher

	// Hooks provides lifecycle interceptors for anonymous workflows.
	Hooks *Hooks
}

Config configures the anonymous authentication plugin.

type ConvertAccountInput added in v1.3.0

type ConvertAccountInput struct {
	// AnonymousUserID is the Core User ID of the anonymous user.
	AnonymousUserID string

	// AnonymousToken is the optional current session token of the anonymous user.
	// When provided, it validates that the caller actively owns the session.
	AnonymousToken string

	// Provider is the external provider name (e.g. "google", "github", "passkey"). Required.
	Provider string

	// ProviderID is the unique user ID within the external provider. Required.
	ProviderID string

	// Email is an optional email address to associate with the user if provided by the external identity.
	Email string
}

ConvertAccountInput defines parameters for converting an anonymous user by linking an external/custom provider account (OAuth, passkey, WebAuthn).

type ConvertInput added in v1.3.0

type ConvertInput struct {
	// AnonymousUserID is the Core User ID of the anonymous user.
	AnonymousUserID string

	// AnonymousToken is the optional current session token of the anonymous user.
	// When provided, it validates that the caller actively owns the session before conversion.
	AnonymousToken string

	// Email is the permanent user's email address. Required.
	Email string

	// Password is the permanent user's plaintext password. Required.
	Password string
}

ConvertInput defines parameters for converting an anonymous user in-place into a permanent user with email and password.

type ConvertResult added in v1.3.0

type ConvertResult struct {
	// User is the updated Core user entity.
	User *user.User

	// Identity is the updated anonymous identity record with ConvertedAt set.
	Identity *Identity
}

ConvertResult holds the result of a conversion operation.

type ConvertedPayload added in v1.3.0

type ConvertedPayload struct {
	IdentityID  string    `json:"identity_id"`
	UserID      string    `json:"user_id"`
	Provider    string    `json:"provider"`
	ConvertedAt time.Time `json:"converted_at"`
}

ConvertedPayload is the event payload for EventAnonymousConverted.

type CreatedPayload added in v1.3.0

type CreatedPayload struct {
	IdentityID string    `json:"identity_id"`
	UserID     string    `json:"user_id"`
	ExpiresAt  time.Time `json:"expires_at"`
	CreatedAt  time.Time `json:"created_at"`
}

CreatedPayload is the event payload for EventAnonymousCreated.

type Hooks added in v1.3.0

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

Hooks provides lifecycle interceptors for anonymous authentication workflows. Before hooks can inspect, mutate input, or abort the workflow by returning an error. After hooks observe the results after transactions commit.

func NewHooks added in v1.3.0

func NewHooks() *Hooks

NewHooks creates an initialized Hooks registry.

func (*Hooks) AfterCleanup added in v1.3.0

func (h *Hooks) AfterCleanup(fn func(ctx context.Context, res *CleanupResult))

AfterCleanup registers a hook called after cleanup completes.

func (*Hooks) AfterConvert added in v1.3.0

func (h *Hooks) AfterConvert(fn func(ctx context.Context, res *ConvertResult))

AfterConvert registers a hook called after conversion succeeds.

func (*Hooks) AfterCreate added in v1.3.0

func (h *Hooks) AfterCreate(fn func(ctx context.Context, res *AuthResult))

AfterCreate registers a hook called after anonymous identity and session creation.

func (h *Hooks) AfterLink(fn func(ctx context.Context, res *LinkResult))

AfterLink registers a hook called after account linking succeeds.

func (*Hooks) BeforeCleanup added in v1.3.0

func (h *Hooks) BeforeCleanup(fn func(ctx context.Context, in *CleanupInput) error)

BeforeCleanup registers a hook called before cleanup runs.

func (*Hooks) BeforeConvert added in v1.3.0

func (h *Hooks) BeforeConvert(fn func(ctx context.Context, in *ConvertInput) error)

BeforeConvert registers a hook called before converting an anonymous user in-place.

func (*Hooks) BeforeCreate added in v1.3.0

func (h *Hooks) BeforeCreate(fn func(ctx context.Context, in *AuthenticateInput) error)

BeforeCreate registers a hook called before anonymous identity creation.

func (h *Hooks) BeforeLink(fn func(ctx context.Context, in *LinkInput) error)

BeforeLink registers a hook called before merging an anonymous identity into a permanent account.

type Identity added in v1.3.0

type Identity struct {
	ID           string            `json:"id"`
	UserID       string            `json:"user_id"`
	ExpiresAt    time.Time         `json:"expires_at"`
	ConvertedAt  *time.Time        `json:"converted_at,omitempty"`
	LinkedUserID string            `json:"linked_user_id,omitempty"`
	Metadata     map[string]string `json:"metadata,omitempty"`
	CreatedAt    time.Time         `json:"created_at"`
	UpdatedAt    time.Time         `json:"updated_at"`
}

Identity represents the persistent record of an anonymous identity in the system.

func (*Identity) Converted added in v1.3.0

func (i *Identity) Converted() bool

Converted reports whether the identity has been converted to permanent or linked.

func (*Identity) Expired added in v1.3.0

func (i *Identity) Expired(now time.Time) bool

Expired reports whether the anonymous identity is expired at the given instant.

func (*Identity) IsActive added in v1.3.0

func (i *Identity) IsActive(now time.Time) bool

IsActive reports whether the identity is currently valid, unexpired, and unconverted.

type LinkInput added in v1.3.0

type LinkInput struct {
	// AnonymousUserID is the Core User ID of the anonymous user. Required if AnonymousToken is empty.
	AnonymousUserID string

	// AnonymousToken is the optional session token of the anonymous user.
	// When provided, AnonymousUserID is inferred or verified against this session.
	AnonymousToken string

	// PermanentEmail and PermanentPassword authenticate the target permanent account by password credentials.
	// Alternatively, PermanentToken can be provided.
	PermanentEmail    string
	PermanentPassword string

	// PermanentToken is an active session token of the target permanent user,
	// proving that the caller is already authenticated as the permanent user.
	PermanentToken string

	// RevokeAnonymousSessions specifies whether to revoke all active sessions belonging
	// to the anonymous user upon successful linking. Default is true.
	RevokeAnonymousSessions bool
}

LinkInput defines parameters for linking/merging an anonymous identity into an existing permanent account.

type LinkResult added in v1.3.0

type LinkResult struct {
	// AnonymousUserID is the ID of the anonymous user that was linked.
	AnonymousUserID string

	// PermanentUser is the target permanent Core user entity.
	PermanentUser *user.User

	// Identity is the updated anonymous identity record reflecting the link.
	Identity *Identity
}

LinkResult holds the outcome of an account link/merge operation.

type LinkedPayload added in v1.3.0

type LinkedPayload struct {
	IdentityID      string    `json:"identity_id"`
	AnonymousUserID string    `json:"anonymous_user_id"`
	PermanentUserID string    `json:"permanent_user_id"`
	LinkedAt        time.Time `json:"linked_at"`
}

LinkedPayload is the event payload for EventAnonymousLinked.

type Module added in v1.3.0

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

Module implements auth.PluginModule for GoModularAuth using plugin-sdk.

func New

func New(opts ...Option) *Module

New constructs a new anonymous authentication plugin module.

func (*Module) ID added in v1.3.0

func (m *Module) ID() string

ID returns the module's unique identifier.

func (*Module) Init added in v1.3.0

func (m *Module) Init(h *auth.Host) error

Init initializes the plugin module on the host and registers its capability.

func (*Module) RegisterSchema added in v1.3.0

func (m *Module) RegisterSchema(s *schema.Schema) error

RegisterSchema registers the plugin's models into the schema.

type Option

type Option func(*Config)

Option configures an anonymous plugin module.

func WithCacheTTL added in v1.3.0

func WithCacheTTL(ttl time.Duration) Option

WithCacheTTL sets the cache duration for anonymous identities.

func WithEmailDomain added in v1.3.0

func WithEmailDomain(domain string) Option

WithEmailDomain configures the domain used for synthetic placeholder emails.

func WithHasher added in v1.3.0

func WithHasher(h password.Hasher) Option

WithHasher configures a custom password hasher for conversions.

func WithHooks added in v1.3.0

func WithHooks(h *Hooks) Option

WithHooks sets the lifecycle hooks registry.

func WithIdentityTTL added in v1.3.0

func WithIdentityTTL(ttl time.Duration) Option

WithIdentityTTL sets the expiration duration for anonymous identities.

func WithPasswordPolicy added in v1.3.0

func WithPasswordPolicy(minLen, maxLen int) Option

WithPasswordPolicy sets password length constraints used during conversion.

func WithSessionTTL added in v1.3.0

func WithSessionTTL(ttl time.Duration) Option

WithSessionTTL sets the session duration for anonymous user sessions.

Directories

Path Synopsis
Package plugins exposes capability aliases for GoModularAuth plugins.
Package plugins exposes capability aliases for GoModularAuth plugins.

Jump to

Keyboard shortcuts

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