Documentation
¶
Overview ¶
Package accounts signs users up and in, with passwords or passkeys, and keeps their accounts: email verification, password resets, and the devices they're signed in on, any of which they can sign out.
It stores users and sessions in your database (SQLite or PostgreSQL), in tables whose names start with tern_. Install it as a plugin, after creating its tables:
acc, err := accounts.New(db, accounts.Options{Send: accounts.SendWith(mailer, queue)})
err = acc.Migrate(ctx)
app, err := ternApp(kit.WithPlugins(acc))
In loads and actions, accounts.Current(ctx) is the signed-in user, and acc.SignIn, acc.SignUp and the rest change who that is.
Index ¶
- Variables
- func CheckPassword(hash, password string) (ok, rehash bool)
- func FormError(c *kit.Ctx, err error) error
- func HashPassword(password string) (string, error)
- func SendWith(mailer *mail.Mailer, q *jobs.Queue) func(ctx context.Context, e Email) error
- type Accounts
- func (a *Accounts) ChangePassword(c *kit.Ctx, current, password string) error
- func (a *Accounts) DeleteAccount(c *kit.Ctx) error
- func (a *Accounts) Migrate(ctx context.Context) error
- func (*Accounts) Name() string
- func (a *Accounts) Passkeys(c *kit.Ctx) ([]Passkey, error)
- func (a *Accounts) RemovePasskey(c *kit.Ctx, id string) error
- func (a *Accounts) RequestPasswordReset(c *kit.Ctx, email string) error
- func (a *Accounts) Require(c *kit.Ctx) (*User, error)
- func (a *Accounts) ResetPassword(c *kit.Ctx, token, password string) (*User, error)
- func (a *Accounts) Revoke(c *kit.Ctx, sessionID string) error
- func (a *Accounts) SendVerification(c *kit.Ctx) error
- func (a *Accounts) Sessions(c *kit.Ctx) ([]Session, error)
- func (a *Accounts) SetName(c *kit.Ctx, name string) error
- func (a *Accounts) Setup(s *kit.Setup) error
- func (a *Accounts) SignIn(c *kit.Ctx, email, password string, remember bool) (*User, error)
- func (a *Accounts) SignOut(c *kit.Ctx) error
- func (a *Accounts) SignOutOthers(c *kit.Ctx) error
- func (a *Accounts) SignUp(c *kit.Ctx, email, name, password string) (*User, error)
- func (a *Accounts) UserByEmail(ctx context.Context, email string) (*User, error)
- func (a *Accounts) UserByID(ctx context.Context, id int64) (*User, error)
- func (a *Accounts) VerifyEmail(c *kit.Ctx, token string) (*User, error)
- type Email
- type Options
- type Passkey
- type Session
- type User
Constants ¶
This section is empty.
Variables ¶
var ( ErrInvalidCredentials = errors.New("accounts: wrong email or password") ErrWrongPassword = errors.New("accounts: the current password is wrong") ErrEmailTaken = errors.New("accounts: an account with this email already exists") ErrInvalidToken = errors.New("accounts: the link is invalid or has expired") ErrSignedOut = errors.New("accounts: not signed in") ErrNoEmail = errors.New("accounts: Options.Send is not set, so no email can be sent") ErrPasskey = errors.New("accounts: the passkey didn't work") )
The errors that tell users what went wrong. Show them as form errors.
Functions ¶
func CheckPassword ¶
CheckPassword reports whether password matches hash, and whether the hash should be replaced with a fresh HashPassword.
func FormError ¶
FormError turns the errors users cause into form errors, in their language, for an action to return: a wrong password, a taken email, an expired link. Other errors stay as they are.
if _, err := acc.SignIn(c, in.Email, in.Password, in.Remember); err != nil {
return struct{}{}, accounts.FormError(c, err)
}
func HashPassword ¶
HashPassword hashes a password for storing.
Types ¶
type Accounts ¶
type Accounts struct {
// contains filtered or unexported fields
}
Accounts is the accounts plugin. Create it with New.
func (*Accounts) ChangePassword ¶
ChangePassword sets a new password after checking the current one, and signs out the user's other devices. An account without a password gets one with current "".
func (*Accounts) DeleteAccount ¶
DeleteAccount deletes the signed-in user, their sessions and passkeys.
func (*Accounts) RemovePasskey ¶
RemovePasskey deletes one of the signed-in user's passkeys.
func (*Accounts) RequestPasswordReset ¶
RequestPasswordReset emails a reset link if an account has this email. It returns nil either way, so the form doesn't reveal who has one.
func (*Accounts) Require ¶
Require is the signed-in user; signed out, it returns a redirect to the sign-in page that comes back here, to return from a load or action.
func (*Accounts) ResetPassword ¶
ResetPassword sets a new password with a reset link's token. It signs the user out everywhere and in here, and verifies their email: the link reached it.
func (*Accounts) SendVerification ¶
SendVerification emails the signed-in user a link that verifies their email address.
func (*Accounts) Sessions ¶
Sessions lists the devices the signed-in user is signed in on, the current one first, then the most recently used.
func (*Accounts) Setup ¶
Setup implements kit.Plugin: each request gets its user from the session cookie.
func (*Accounts) SignIn ¶
SignIn checks a password and signs the user in. remember keeps the session for Options.SessionLength instead of a day.
func (*Accounts) SignOutOthers ¶
SignOutOthers signs out every session of the user but this one.
func (*Accounts) SignUp ¶
SignUp creates an account with a password and signs it in. password may be "" for an account that signs in with passkeys only.
func (*Accounts) UserByEmail ¶
UserByEmail returns a user, or nil if there's none.
type Email ¶
type Email struct {
// Kind is "verify" or "reset".
Kind string `json:"kind"`
To string `json:"to"`
Name string `json:"name"`
Link string `json:"link"`
Subject string `json:"subject"`
Text string `json:"text"`
HTML string `json:"html"`
}
Email is a verification or password reset email for Options.Send. Its texts are Tern's own (tern.accounts.*), translated like any other into the request's locale.
type Options ¶
type Options struct {
// Send delivers the verification and password reset emails. SendWith
// sends them with a mail.Mailer through a jobs queue. Without it,
// those features fail with ErrNoEmail.
Send func(ctx context.Context, e Email) error
// BaseURL is the app's address, such as "https://example.com", for
// links in emails. The default is the request's own.
BaseURL string
// ResetPath and VerifyPath are the pages the emails link to, with the
// token in ?token=. The defaults are /reset-password and /verify-email.
ResetPath, VerifyPath string
// SignInPath is where Require sends signed-out users. The default is
// /login.
SignInPath string
// Cookie names the session cookie. The default is "tern_session".
Cookie string
// SessionLength is how long a "keep me signed in" session lasts
// without use. The default is 30 days; other sessions last a day.
SessionLength time.Duration
// PasskeyPath is where the passkey endpoints are mounted, for the
// browser's passkeys helpers. The default is /_tern/passkeys.
PasskeyPath string
// RPName is the app's name the browser shows when creating a passkey.
// The default is the host name.
RPName string
}
Options configure accounts.
type Passkey ¶
type Passkey struct {
ID string `json:"id"`
Name string `json:"name"`
CreatedAt time.Time `json:"createdAt"`
LastUsed *time.Time `json:"lastUsed"`
}
Passkey is one of a user's passkeys.
type Session ¶
type Session struct {
// ID identifies it for Revoke. It isn't the cookie's token.
ID string `json:"id"`
Device string `json:"device"` // such as "Firefox on macOS"
IP string `json:"ip"`
CreatedAt time.Time `json:"createdAt"`
LastSeen time.Time `json:"lastSeen"`
// Current is the session of this request.
Current bool `json:"current"`
}
Session is a device the user is signed in on.
type User ¶
type User struct {
ID int64 `json:"id"`
Email string `json:"email"`
Name string `json:"name"`
EmailVerified bool `json:"emailVerified"`
CreatedAt time.Time `json:"createdAt"`
// HasPassword is false for accounts made with a passkey only.
HasPassword bool `json:"hasPassword"`
}
User is an account.