Documentation
¶
Overview ¶
Package lastlogin remembers how each user last signed in — password, a provider, a magic link, a code, a passkey — so a sign-in page can put the method the person used last in front, and so an app can see which methods its users actually use.
It is one column on auth_user, written by a data hook on session creation, which every sign-in method ends with. The method's name is the plugin that ran the operation, so a method added later is recorded without knowing this plugin exists; WithResolver names it differently when the app wants the provider rather than "oauth".
Index ¶
- Constants
- Variables
- func Bindings(c *LastLoginColumns) []sqlb.Binding
- func DefaultResolver(_ context.Context, op auth.OpInfo, _ auth.Session) string
- type ConsentFunc
- type LastLoginColumns
- type MethodResult
- type Option
- type Plugin
- func (p *Plugin) Columns() []auth.ColumnExtension
- func (p *Plugin) DataHooks() auth.DataHooks
- func (p *Plugin) Init(a *auth.Auth) error
- func (p *Plugin) Name() string
- func (p *Plugin) Of(ctx context.Context, userID string) (LastLoginColumns, error)
- func (p *Plugin) OperationHooks() []auth.OperationHook
- func (p *Plugin) Operations() []auth.Operation
- func (p *Plugin) OptionErr() error
- func (p *Plugin) Validate(*auth.Config) error
- type Resolver
Constants ¶
const ( // ColMethod is the method of the last sign-in: a plugin name such as // "password", "oauth", "magiclink", "phone", "passkey", "anonymous", or // what WithResolver returned. Empty for a user who never signed in since // the column existed. //ds:def id=colmethod-7q2nv8kx owner=@khanakia stability=stable ColMethod = "last_login_method" // ColAt is when that sign-in happened. ColAt = "last_login_at" )
Columns this plugin adds to auth_user.
const ( // DefaultCookieName is the plugin cookie's name; authhttp sends it as // __Host-auth_last_used_login_method, beside the core's cookies. //ds:def id=lastlogin-defaultcookiename-5qk8nw2v owner=@khanakia stability=stable DefaultCookieName = "last_used_login_method" // DefaultCookieMaxAge is 30 days, better-auth's default: the hint is // for the next sign-in, which may be weeks away. //ds:def id=lastlogin-defaultcookiemaxage-8wr3mt6q owner=@khanakia stability=stable DefaultCookieMaxAge = 30 * 24 * time.Hour )
The cookie (WithCookie), better-auth's last_used_login_method hint.
const ( EndpointMethod auth.EndpointID = "lastlogin.method" EndpointClear auth.EndpointID = "lastlogin.clear" )
Operation ids of the cookie routes, served only under WithCookie.
const PluginName = "lastlogin"
PluginName is the plugin's permanent identity: migration owner.
Variables ¶
var ( Method = sqlb.Col[string](auth.TableUser, ColMethod) At = sqlb.Col[*time.Time](auth.TableUser, ColAt) )
Typed columns on auth_user, for an app that reports on sign-in methods in its own queries.
var ErrBadCookieAge = errors.New("lastlogin: WithCookie needs a maxAge of zero (the default) or more")
ErrBadCookieAge is a configuration error from auth.New: WithCookie was given a negative maxAge, a cookie that would be deleted as it is set.
Functions ¶
func Bindings ¶
func Bindings(c *LastLoginColumns) []sqlb.Binding
Bindings binds LastLoginColumns for a query on auth_user.
Types ¶
type ConsentFunc ¶
ConsentFunc decides, per sign-in, whether the cookie may be set, such as after the visitor accepted the app's cookie banner: better-auth's beforeStoreCookie. The database record does not ask: it is the app's own data about its user, not a cookie in the visitor's browser.
type LastLoginColumns ¶
LastLoginColumns is this plugin's flat part of a composite user (the columns are on auth_user, ADR-0001). The name stutters on purpose: Go names an embedded field after its type, and two plugins exporting Columns could not be embedded together.
type MethodResult ¶
type MethodResult struct {
Method string `json:"method"`
}
MethodResult is the answer of GET lastlogin/method: the method this browser last signed in with, or "" when its cookie is absent.
type Option ¶
type Option func(*Plugin)
Option configures the plugin; options are applied in order by New.
func WithCookie ¶
WithCookie also keeps the last method in a cookie, better-auth's default behaviour: every sign-in that records a method sets it for maxAge (0 takes DefaultCookieMaxAge), under name ("" takes DefaultCookieName; it must match auth.CookieNamePattern). A login page reads it before anyone is signed in: server-side through auth.RequestCookie, a script through GET <mount>/lastlogin/method, and POST <mount>/lastlogin/clear forgets it (better-auth's clearLastUsedLoginMethod).
One deliberate difference: better-auth's cookie is readable by page scripts (httpOnly false). Here every plugin cookie has the core's fixed attributes, HttpOnly among them, so no plugin can weaken the cookie rules; the route is how a script reads it. The value is a method name only, never an identifier, so it says how someone on this browser signed in, not who.
func WithCookieConsent ¶
func WithCookieConsent(fn ConsentFunc) Option
WithCookieConsent asks fn before each cookie is set (better-auth's beforeStoreCookie); false skips the cookie for that sign-in only.
func WithResolver ¶
WithResolver replaces DefaultResolver, for an app that wants "google" rather than "oauth", or that records nothing for some operations.
type Plugin ¶
type Plugin struct {
// contains filtered or unexported fields
}
Plugin records the last sign-in method. Build it with New.
func (*Plugin) Columns ¶
func (p *Plugin) Columns() []auth.ColumnExtension
Columns adds the method and its time to auth_user.
func (*Plugin) DataHooks ¶
DataHooks record the method on every session a sign-in operation creates.
A pending second-factor session counts: the method was used, and the promotion that follows (a PendingSecondFactor operation) is skipped so the second step does not overwrite the first. An impersonation is not the user's own sign-in, and a session created outside an operation (a. CreateSession from Go) has no method to record.
func (*Plugin) Of ¶
Of returns how and when userID last signed in; the method is "" when nothing was recorded.
func (*Plugin) OperationHooks ¶
func (p *Plugin) OperationHooks() []auth.OperationHook
OperationHooks adds, under WithCookie, the after-hook that sets the cookie for the session a sign-in issued.
func (*Plugin) Operations ¶
Operations lists the cookie routes under WithCookie, and nothing otherwise: the database record needs no route.