Documentation
¶
Overview ¶
Package anonymous gives a visitor an account before they have told the app who they are: a guest session for a cart, a trial or a draft, and a link to the real account the moment they sign up or sign in.
A guest is an ordinary auth_user row with no identifier and the plugin's is_anonymous column set, so every app table that references a user works for a guest unchanged. The link runs inside the sign-in's own transaction through a data hook on session creation, which is the one step every sign-in method ends with — password, code, link, provider, passkey — so no method has to know this plugin exists, and a method added later links too.
Index ¶
- Constants
- Variables
- func Bindings(c *AnonymousColumns) []sqlb.Binding
- type AnonymousColumns
- type LinkFunc
- type Option
- type Plugin
- func (p *Plugin) Columns() []auth.ColumnExtension
- func (p *Plugin) DataHooks() auth.DataHooks
- func (p *Plugin) Delete(ctx context.Context) error
- func (p *Plugin) Init(a *auth.Auth) error
- func (p *Plugin) IsAnonymous(ctx context.Context, userID string) (bool, error)
- func (p *Plugin) Name() string
- func (p *Plugin) Operations() []auth.Operation
- func (p *Plugin) OptionErr() error
- func (p *Plugin) RateLimits() []auth.RateRule
- func (p *Plugin) SignIn(ctx context.Context) (SignInResult, error)
- func (p *Plugin) SignsIn() bool
- func (p *Plugin) Sweep(ctx context.Context, cutoff time.Time) (int64, error)
- func (p *Plugin) Validate(*auth.Config) error
- type SignInRequest
- type SignInResult
Constants ¶
const ( EndpointSignIn auth.EndpointID = "anonymous.sign-in" EndpointDelete auth.EndpointID = "anonymous.delete" )
Operation ids, exported so a hook or a rate rule can name them. They are wire names, stable across releases.
const ColIsAnonymous = "is_anonymous"
ColIsAnonymous is the column this plugin adds to auth_user. It is true for a guest and false for everyone else, including a guest who later proved an identifier of their own.
const DefaultName = "Guest"
DefaultName is the display name a guest gets unless WithName says otherwise. A fixed word rather than a random one, so a guest can be told apart from a real user in any list that shows names.
const PluginName = "anonymous"
PluginName is the plugin's permanent identity: route prefix and migration owner.
Variables ¶
var ( // ErrAlreadyAnonymous refuses a guest who asks for another guest // session: the first one already holds their data, and piling up guest // rows per reload is the failure the refusal prevents. ErrAlreadyAnonymous = auth.NewError("anonymous.already", http.StatusConflict, "You already have a guest session.") // ErrNotAnonymous refuses the delete route for a real account, which // has sign-in methods and belongs with the app's own account deletion. ErrNotAnonymous = auth.NewError("anonymous.not_anonymous", http.StatusForbidden, "This is not a guest account.") )
Errors this plugin returns. Both reach the user with stable codes.
var IsAnonymous = sqlb.Col[bool](auth.TableUser, ColIsAnonymous)
IsAnonymous is the typed column on auth_user, for an app that filters guests out of its own queries.
Functions ¶
func Bindings ¶
func Bindings(c *AnonymousColumns) []sqlb.Binding
Bindings binds AnonymousColumns for a query on auth_user.
Types ¶
type AnonymousColumns ¶
type AnonymousColumns struct {
IsAnonymous bool
}
AnonymousColumns is this plugin's flat part of a composite user: the column is on auth_user, so the field is flat (ADR-0001). The name stutters on purpose: Go names an embedded field after its type, and two plugins both exporting Columns could not be embedded together.
type LinkFunc ¶
LinkFunc is the app's hook when a guest becomes, or signs in to, a real account: move the guest's rows to user, inside tx, so the move and the sign-in commit or roll back together. guest is deleted right after it returns unless KeepUsers is set; an error fails the sign-in and leaves the guest as it was.
type Option ¶
type Option func(*Plugin)
Option configures the plugin; options are applied in order by New.
func KeepUsers ¶
func KeepUsers() Option
KeepUsers leaves the guest row in place after it is linked and leaves the delete route out, for an app that keeps guest rows for its own records. Without it the guest is deleted once linked, so no orphan account remains.
func OnLink ¶
OnLink sets the app's hook for moving a guest's data to the account they signed up or in with; see LinkFunc. Without it the guest is simply deleted (or kept, with KeepUsers) and nothing moves.
func SweepStale ¶
func SweepStale() Option
SweepStale makes a.Sweep delete guests who never linked and whose every session has expired, once they are older than the sweep's retention. Off by default because deleting a user cascades to the app's rows that reference it; turn it on when guest data has no value after the session ends.
type Plugin ¶
type Plugin struct {
// contains filtered or unexported fields
}
Plugin is the anonymous sign-in method. Build it with New: the zero value has no operations.
func New ¶
New builds the plugin. With no options a guest is named DefaultName, is deleted when linked, and may delete their own account.
func (*Plugin) Columns ¶
func (p *Plugin) Columns() []auth.ColumnExtension
Columns adds is_anonymous to auth_user, default false.
func (*Plugin) DataHooks ¶
DataHooks link a guest to the account they sign in to, and turn a guest into a regular user once they prove an identifier of their own.
The link runs on session creation because every sign-in method ends there. The request's signed-in user is the guest (auth.CurrentUser reads the request's own session, which the pipeline resolved before the operation ran); the new session's user is the account. A session for the same user, an impersonation, or a request with no guest changes nothing.
func (*Plugin) Delete ¶
Delete removes the signed-in guest's account and ends the session. A real account is refused with ErrNotAnonymous.
func (*Plugin) IsAnonymous ¶
IsAnonymous reports whether userID is a guest.
func (*Plugin) Operations ¶
Operations lists sign-in, and delete unless KeepUsers left it out.
func (*Plugin) OptionErr ¶
OptionErr is every error the options recorded, such as auth.ErrNilOption naming an option given nil; auth.New refuses to start while it is not nil (auth.OptionChecker).
func (*Plugin) RateLimits ¶
RateLimits bounds guest creation per IP.
func (*Plugin) SignIn ¶
func (p *Plugin) SignIn(ctx context.Context) (SignInResult, error)
SignIn creates a guest and starts a session for them. A caller who is already a guest is refused with ErrAlreadyAnonymous.
func (*Plugin) SignsIn ¶
SignsIn reports that a guest session is a way in, so an app whose only other plugins add identifiers still passes the core's sign-in check. Such an app has guests and nothing else, which is a real shape for a demo.
type SignInRequest ¶
type SignInRequest struct {
// Remember is the "remember me" box, as on a password sign-in: nil or
// true keeps the guest session past the browser, false makes it
// transient.
Remember *bool `json:"remember,omitempty"`
}
SignInRequest is the input of sign-in. It has no identifier: a guest is nobody in particular.