Documentation
¶
Index ¶
- Constants
- Variables
- func DetectUsernameType(username string) string
- func HashVerificationToken(token string) string
- func LinkLocalCredentials(config LinkLocalCredentialsConfig, userID string, ...) error
- func NewRevokeSessionsOnReset(identities accounts.IdentityStore, refresh core.RefreshTokenStore) func(ctx context.Context, email string) error
- type AccountExistsEmailSender
- type ConsoleEmailSender
- type CreateUserFunc
- type CreateVerificationTokenRequest
- type CreateVerificationTokenResponse
- type Credentials
- type CredentialsValidator
- type DeleteSubjectVerificationTokensRequest
- type DeleteSubjectVerificationTokensResponse
- type DeleteVerificationTokenRequest
- type DeleteVerificationTokenResponse
- type GetLoggedInUserFunc
- type GetVerificationTokenRequest
- type GetVerificationTokenResponse
- type LinkCredentialsConfig
- type LinkLocalCredentialsConfig
- type LocalAuth
- func (a *LocalAuth) HandleForgotPassword(w http.ResponseWriter, r *http.Request)
- func (a *LocalAuth) HandleForgotPasswordForm(w http.ResponseWriter, r *http.Request)
- func (a *LocalAuth) HandleLinkCredentials(config LinkCredentialsConfig, getUser GetLoggedInUserFunc) http.HandlerFunc
- func (a *LocalAuth) HandleResetPassword(w http.ResponseWriter, r *http.Request)
- func (a *LocalAuth) HandleResetPasswordForm(w http.ResponseWriter, r *http.Request)
- func (a *LocalAuth) HandleSignup(w http.ResponseWriter, r *http.Request)
- func (a *LocalAuth) HandleVerifyEmail(w http.ResponseWriter, r *http.Request)
- func (a *LocalAuth) ServeHTTP(w http.ResponseWriter, r *http.Request)
- type SendEmail
- type SignupPolicy
- type SignupValidator
- type UpdatePasswordFunc
- type VerificationToken
- type VerificationTokenStore
- type VerificationType
- type VerifyEmailFunc
Constants ¶
const ( VerificationExpiryEmail = 24 * time.Hour VerificationExpiryPasswordReset = 1 * time.Hour )
Default verification token expiry durations.
Variables ¶
var PolicyEmailOnly = SignupPolicy{ RequireUsername: false, RequireEmail: true, RequirePhone: false, RequirePassword: true, EnforceUsernameUnique: true, EnforceEmailUnique: true, MinPasswordLength: 8, UsernamePattern: `^[a-zA-Z0-9_-]{3,20}$`, }
PolicyEmailOnly requires only email and password for signup (username optional).
var PolicyFlexible = SignupPolicy{ RequireUsername: false, RequireEmail: false, RequirePhone: false, RequirePassword: false, EnforceUsernameUnique: true, EnforceEmailUnique: true, MinPasswordLength: 8, UsernamePattern: `^[a-zA-Z0-9_-]{3,20}$`, }
PolicyFlexible is OAuth-friendly — email/phone optional, username optional.
var PolicyUsernameRequired = SignupPolicy{ RequireUsername: true, RequireEmail: true, RequirePhone: false, RequirePassword: true, EnforceUsernameUnique: true, EnforceEmailUnique: true, MinPasswordLength: 8, UsernamePattern: `^[a-zA-Z0-9_-]{3,20}$`, }
PolicyUsernameRequired requires username, email, and password for signup.
Functions ¶
func DetectUsernameType ¶ added in v0.1.0
DetectUsernameType is re-exported from accounts so callers that import localauth don't need a second import for this widely-used helper.
func HashVerificationToken ¶ added in v0.1.45
HashVerificationToken is how stores key a verification or reset token: the hex SHA-256 of the raw value. Stores keep only this, so read access to the store (a backup, a leaked volume) doesn't yield working links (CWE-256), and the raw token exists only in the email and the request. The token is 32 random bytes, so an unsalted fast hash is enough.
func LinkLocalCredentials ¶
func LinkLocalCredentials(config LinkLocalCredentialsConfig, userID string, username, password, email string) error
LinkLocalCredentials adds local (password) authentication to an existing OAuth-only user.
func NewRevokeSessionsOnReset ¶ added in v0.1.45
func NewRevokeSessionsOnReset(identities accounts.IdentityStore, refresh core.RefreshTokenStore) func(ctx context.Context, email string) error
NewRevokeSessionsOnReset returns an AfterPasswordReset hook that finds the account by its email identity and revokes all of its refresh tokens, so a session opened with the old password ends with it (CWE-613). Access tokens already issued run to their expiry.
An email with no identity is not an error: there is nothing to revoke.
Types ¶
type AccountExistsEmailSender ¶ added in v0.1.55
AccountExistsEmailSender is an optional extension of SendEmail. When the configured EmailSender implements it, a signup with an email that already has an account (and RequireEmailVerification on) sends the owner this notice instead of telling the requester the email is taken. link points at the forgot-password page, for an owner who has lost the password. Without it the owner hears nothing, and the requester still learns nothing.
type ConsoleEmailSender ¶ added in v0.1.0
type ConsoleEmailSender struct{}
ConsoleEmailSender is a development implementation that logs emails to console.
func (*ConsoleEmailSender) SendPasswordResetEmail ¶ added in v0.1.0
func (c *ConsoleEmailSender) SendPasswordResetEmail(to string, resetLink string) error
func (*ConsoleEmailSender) SendVerificationEmail ¶ added in v0.1.0
func (c *ConsoleEmailSender) SendVerificationEmail(to string, verificationLink string) error
type CreateUserFunc ¶ added in v0.1.0
type CreateUserFunc func(creds *Credentials) (accounts.User, error)
CreateUserFunc creates a new user with the given credentials.
func NewCreateUserFunc ¶
func NewCreateUserFunc(userStore accounts.UserStore, identityStore accounts.IdentityStore, channelStore accounts.ChannelStore) CreateUserFunc
NewCreateUserFunc creates a CreateUserFunc from stores.
type CreateVerificationTokenRequest ¶ added in v0.1.7
type CreateVerificationTokenRequest struct {
Subject string
Email string
Type VerificationType
ExpiryDuration time.Duration
}
CreateVerificationTokenRequest carries the inputs for issuing a verification token. Subject may be empty for password-reset flows where the AS does not want to reveal whether the email belongs to a registered user.
type CreateVerificationTokenResponse ¶ added in v0.1.7
type CreateVerificationTokenResponse struct {
Token *VerificationToken
}
type Credentials ¶ added in v0.1.0
type Credentials struct {
Username string // Required for signup, can be username/email/phone for login
Email *string // Optional for signup
Phone *string // Optional for signup
Password string // Required
}
Credentials represents user credentials for signup or login.
type CredentialsValidator ¶ added in v0.1.0
type CredentialsValidator = accounts.CredentialsValidator
CredentialsValidator is the same shape as accounts.CredentialsValidator — type-aliased here so existing localauth callers don't have to switch imports.
func NewCredentialsValidator ¶
func NewCredentialsValidator(identityStore accounts.IdentityStore, channelStore accounts.ChannelStore, userStore accounts.UserStore) CredentialsValidator
NewCredentialsValidator creates a CredentialsValidator from stores.
func NewCredentialsValidatorWithUsername ¶
func NewCredentialsValidatorWithUsername(identityStore accounts.IdentityStore, channelStore accounts.ChannelStore, userStore accounts.UserStore, usernameStore accounts.UsernameStore) CredentialsValidator
NewCredentialsValidatorWithUsername creates a CredentialsValidator that supports logging in with username (in addition to email/phone).
type DeleteSubjectVerificationTokensRequest ¶ added in v0.1.7
type DeleteSubjectVerificationTokensRequest struct {
Subject string
Type VerificationType
}
type DeleteSubjectVerificationTokensResponse ¶ added in v0.1.7
type DeleteSubjectVerificationTokensResponse struct{}
type DeleteVerificationTokenRequest ¶ added in v0.1.7
type DeleteVerificationTokenRequest struct {
Token string
}
type DeleteVerificationTokenResponse ¶ added in v0.1.7
type DeleteVerificationTokenResponse struct {
// Deleted is true when this call removed the token, and false when it
// was already gone. Only one of two concurrent deletes sees true, which
// is what makes a token single-use: consume it by deleting it, and act
// only when Deleted.
Deleted bool
}
type GetLoggedInUserFunc ¶
GetLoggedInUserFunc returns the currently logged-in user ID from the request. Apps must implement this based on their session/JWT handling.
type GetVerificationTokenRequest ¶ added in v0.1.7
type GetVerificationTokenRequest struct {
Token string
}
type GetVerificationTokenResponse ¶ added in v0.1.7
type GetVerificationTokenResponse struct {
Token *VerificationToken
}
type LinkCredentialsConfig ¶
type LinkCredentialsConfig struct {
UserStore accounts.UserStore
IdentityStore accounts.IdentityStore
ChannelStore accounts.ChannelStore
UsernameStore accounts.UsernameStore // Optional
}
LinkCredentialsConfig holds configuration for HandleLinkCredentials.
type LinkLocalCredentialsConfig ¶ added in v0.1.0
type LinkLocalCredentialsConfig struct {
UserStore accounts.UserStore
IdentityStore accounts.IdentityStore
ChannelStore accounts.ChannelStore
UsernameStore accounts.UsernameStore
}
LinkLocalCredentialsConfig groups the stores LinkLocalCredentials needs.
type LocalAuth ¶
type LocalAuth struct {
// Validates credentials during login
ValidateCredentials CredentialsValidator
// Validates credentials during signup (deprecated: use SignupPolicy instead)
ValidateSignup SignupValidator
// Creates a new user (for signup)
CreateUser CreateUserFunc
// Optional email sender for verification emails
EmailSender SendEmail
// Optional token store for email verification and password reset
TokenStore VerificationTokenStore
// Base URL for generating verification/reset links
BaseURL string
// Whether email verification is required before login
RequireEmailVerification bool
// Provider name (defaults to "local")
Provider string
// Form field names
UsernameField string
PasswordField string
EmailField string
PhoneField string
// Handler called after successful authentication
HandleUser accounts.HandleUserFunc
// Callback to verify email by token
VerifyEmail VerifyEmailFunc
// Callback to update password
UpdatePassword UpdatePasswordFunc
// AfterPasswordReset runs after a reset succeeds, with the account's
// email. Use it to end the account's other sessions (revoke its
// refresh tokens): whoever had the old password may still hold one
// (CWE-613). An error is logged; the password is already changed.
AfterPasswordReset func(ctx context.Context, email string) error
// SignupPolicy defines what is required for signup (overrides ValidateSignup if set)
SignupPolicy *SignupPolicy
// OnSignupError is called when signup fails. If nil, returns JSON error.
OnSignupError accounts.AuthErrorHandler
// OnLoginError is called when login fails. If nil, returns JSON error.
OnLoginError accounts.AuthErrorHandler
// SignupURL is used for redirects on error (if OnSignupError uses redirects)
SignupURL string
// LoginURL is used for redirects on error (if OnLoginError uses redirects)
LoginURL string
// Optional UsernameStore for enforcing username uniqueness
UsernameStore accounts.UsernameStore
// ForgotPasswordURL: if set, GET /forgot-password redirects here and
// POST /forgot-password redirects here with ?sent=true on success.
// If empty, GET renders a basic HTML form and POST returns JSON.
ForgotPasswordURL string
// ResetPasswordURL: if set, GET /reset-password redirects here (with ?token=...)
// and POST redirects here with ?success=true on success or ?error=... on failure.
// If empty, GET renders a basic HTML form and POST returns JSON.
ResetPasswordURL string
// RateLimiter limits login attempts per key (IP:username). Optional.
// When set, rate-limited requests receive 429 Too Many Requests.
RateLimiter core.RateLimiter
// Lockout locks accounts after consecutive failed login attempts. Optional.
// When set, locked accounts receive 429 Too Many Requests until the lockout expires.
Lockout *core.AccountLockout
// TrustedProxies are the reverse proxies whose X-Forwarded-For is
// believed when keying rate limits. Empty keys on the TCP peer, which
// is right when nothing sits in front of this process; behind a proxy
// every client would otherwise share the proxy's bucket.
TrustedProxies core.TrustedProxies
}
Allows local username/password based authentication.
func (*LocalAuth) HandleForgotPassword ¶
func (a *LocalAuth) HandleForgotPassword(w http.ResponseWriter, r *http.Request)
HandleForgotPassword handles forgot password requests (POST)
func (*LocalAuth) HandleForgotPasswordForm ¶
func (a *LocalAuth) HandleForgotPasswordForm(w http.ResponseWriter, r *http.Request)
HandleForgotPasswordForm shows the forgot password form (GET)
func (*LocalAuth) HandleLinkCredentials ¶
func (a *LocalAuth) HandleLinkCredentials(config LinkCredentialsConfig, getUser GetLoggedInUserFunc) http.HandlerFunc
HandleLinkCredentials returns an HTTP handler that adds local (password) auth to an existing OAuth-only user.
Who Calls This ¶
Mount this handler at a protected route (requires login) like POST /auth/link-credentials:
localAuth := &oneauth.LocalAuth{...}
linkConfig := oneauth.LinkCredentialsConfig{
UserStore: stores.UserStore,
IdentityStore: stores.IdentityStore,
ChannelStore: stores.ChannelStore,
UsernameStore: stores.UsernameStore, // optional
}
getUser := func(r *http.Request) (string, error) {
return getLoggedInUserIDFromSession(r), nil
}
mux.Handle("POST /auth/link-credentials", localAuth.HandleLinkCredentials(linkConfig, getUser))
Flow ¶
- OAuth-only user visits profile page, sees "Add password" form
- User submits form with password (and optionally username)
- Handler validates input, creates local channel, reserves username
- User can now login with email/password OR their OAuth provider
Form Fields ¶
- password (required): The new password
- username (optional): Username for login (if UsernameStore configured)
Responses ¶
- 200 OK: {"success": true, "message": "..."}
- 400 Bad Request: {"error": "...", "code": "...", "field": "..."}
- 401 Unauthorized: User not logged in
- 409 Conflict: Local credentials already exist
func (*LocalAuth) HandleResetPassword ¶
func (a *LocalAuth) HandleResetPassword(w http.ResponseWriter, r *http.Request)
HandleResetPassword handles password reset submissions (POST)
func (*LocalAuth) HandleResetPasswordForm ¶
func (a *LocalAuth) HandleResetPasswordForm(w http.ResponseWriter, r *http.Request)
HandleResetPasswordForm shows the reset password form (GET)
func (*LocalAuth) HandleSignup ¶
func (a *LocalAuth) HandleSignup(w http.ResponseWriter, r *http.Request)
HandleSignup processes user registration.
func (*LocalAuth) HandleVerifyEmail ¶
func (a *LocalAuth) HandleVerifyEmail(w http.ResponseWriter, r *http.Request)
HandleVerifyEmail handles email verification via token
type SendEmail ¶ added in v0.1.0
type SendEmail interface {
SendVerificationEmail(to string, verificationLink string) error
SendPasswordResetEmail(to string, resetLink string) error
}
SendEmail lets applications provide their own email-sending implementation for localauth's verification-email and password-reset flows.
type SignupPolicy ¶ added in v0.1.0
type SignupPolicy struct {
RequireUsername bool // Is username required? (default: false)
RequireEmail bool // Is email required? (default: true)
RequirePhone bool // Is phone required? (default: false)
RequirePassword bool // Is password required for local? (default: true)
EnforceUsernameUnique bool // Check UsernameStore? (default: true if username required)
EnforceEmailUnique bool // Check IdentityStore? (default: true)
MinPasswordLength int // Minimum password (default: 8)
UsernamePattern string // Regex for username (default: ^[a-zA-Z0-9_-]{3,20}$)
}
SignupPolicy defines what is required for signup.
func DefaultSignupPolicy ¶ added in v0.1.0
func DefaultSignupPolicy() SignupPolicy
DefaultSignupPolicy returns a sensible default signup policy.
func (SignupPolicy) GetMinPasswordLength ¶ added in v0.1.0
func (p SignupPolicy) GetMinPasswordLength() int
GetMinPasswordLength returns the minimum password length.
func (SignupPolicy) GetUsernamePattern ¶ added in v0.1.0
func (p SignupPolicy) GetUsernamePattern() *regexp.Regexp
GetUsernamePattern returns the compiled username regex pattern.
type SignupValidator ¶ added in v0.1.0
type SignupValidator func(creds *Credentials) error
SignupValidator validates credentials during signup.
var DefaultSignupValidator SignupValidator = func(creds *Credentials) error { if len(creds.Username) < 3 || len(creds.Username) > 20 { return fmt.Errorf("username must be 3-20 characters") } usernameRegex := regexp.MustCompile(`^[a-zA-Z0-9_-]+$`) if !usernameRegex.MatchString(creds.Username) { return fmt.Errorf("username can only contain letters, numbers, underscores, and hyphens") } if creds.Email == nil && creds.Phone == nil { return fmt.Errorf("email or phone required") } if creds.Email != nil && *creds.Email != "" { emailRegex := regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`) if !emailRegex.MatchString(*creds.Email) { return fmt.Errorf("invalid email format") } } if creds.Phone != nil && *creds.Phone != "" { cleaned := strings.ReplaceAll(*creds.Phone, "-", "") cleaned = strings.ReplaceAll(cleaned, " ", "") cleaned = strings.ReplaceAll(cleaned, "(", "") cleaned = strings.ReplaceAll(cleaned, ")", "") if len(cleaned) < 10 { return fmt.Errorf("invalid phone number") } } if len(creds.Password) < 8 { return fmt.Errorf("password must be at least 8 characters") } return nil }
DefaultSignupValidator provides sensible default validation for signup.
type UpdatePasswordFunc ¶
func NewUpdatePasswordFunc ¶
func NewUpdatePasswordFunc(identityStore accounts.IdentityStore, channelStore accounts.ChannelStore) UpdatePasswordFunc
NewUpdatePasswordFunc creates an UpdatePasswordFunc from stores.
type VerificationToken ¶ added in v0.1.0
type VerificationToken struct {
Token string `json:"token"`
Type VerificationType `json:"type"`
Subject string `json:"subject"`
Email string `json:"email"`
CreatedAt time.Time `json:"created_at"`
ExpiresAt time.Time `json:"expires_at"`
}
VerificationToken represents a short-lived email-mediated token used by localauth flows (email verification, password reset).
Renamed from the previous core.AuthToken so the type name reflects its role and doesn't collide with OAuth access/refresh tokens.
func (*VerificationToken) IsExpired ¶ added in v0.1.0
func (t *VerificationToken) IsExpired() bool
IsExpired checks if a verification token has expired.
func (*VerificationToken) IsValid ¶ added in v0.1.0
func (t *VerificationToken) IsValid(expectedType VerificationType) bool
IsValid checks if a token is valid (not expired and matches type).
type VerificationTokenStore ¶ added in v0.1.0
type VerificationTokenStore interface {
CreateToken(ctx context.Context, req *CreateVerificationTokenRequest) (*CreateVerificationTokenResponse, error)
GetToken(ctx context.Context, req *GetVerificationTokenRequest) (*GetVerificationTokenResponse, error)
DeleteToken(ctx context.Context, req *DeleteVerificationTokenRequest) (*DeleteVerificationTokenResponse, error)
DeleteSubjectTokens(ctx context.Context, req *DeleteSubjectVerificationTokensRequest) (*DeleteSubjectVerificationTokensResponse, error)
}
VerificationTokenStore manages localauth verification tokens (signup email-verify, password-reset). Renamed from core.TokenStore.
type VerificationType ¶ added in v0.1.0
type VerificationType string
VerificationType is the kind of email/phone-mediated verification token issued by localauth (e.g. signup email-verify, password-reset).
Renamed from the previous core.TokenType to avoid colliding with OAuth access/refresh-token semantics that live in apiauth/core.
const ( // String values kept stable for backwards-compatible persisted data // across the rename. VerificationTypeEmail VerificationType = "email_verification" VerificationTypePasswordReset VerificationType = "password_reset" )
type VerifyEmailFunc ¶
func NewVerifyEmailFunc ¶
func NewVerifyEmailFunc(identityStore accounts.IdentityStore, tokenStore VerificationTokenStore) VerifyEmailFunc
NewVerifyEmailFunc creates a VerifyEmailFunc from stores.