Documentation
¶
Overview ¶
Package auth handles user authentication via OTP and session management.
Index ¶
- Constants
- Variables
- type AuthChallenge
- type CreateUserOptions
- type DBSession
- type EmailThrottle
- type GlobalAuthThrottle
- type Handler
- type LoginRequest
- type MessageResponse
- type Repository
- func (r *Repository) CreateAuthChallenge(ctx context.Context, userID string, state string, otpHash string, ...) (string, error)
- func (r *Repository) CreateSession(ctx context.Context, tokenHash string, userID string, expiresAt int64) error
- func (r *Repository) DeleteSession(ctx context.Context, userID string, tokenHash string) (int64, error)
- func (r *Repository) DeleteSessionsByUserID(ctx context.Context, userID string) (int64, error)
- func (r *Repository) DeleteStaleAuthChallenges(ctx context.Context) (int64, error)
- func (r *Repository) DeleteStaleSessions(ctx context.Context, expiredBefore int64, idleBefore int64) (int64, error)
- func (r *Repository) EnsureUserAdmin(ctx context.Context, userID string) (bool, error)
- func (r *Repository) GetAuthChallengeByState(ctx context.Context, state string) (*AuthChallenge, error)
- func (r *Repository) GetOrCreateUser(ctx context.Context, email string, opts ...CreateUserOptions) (*User, bool, error)
- func (r *Repository) GetSession(ctx context.Context, tokenHash string) (*DBSession, error)
- func (r *Repository) GetUserByID(ctx context.Context, userID string) (*User, error)
- func (r *Repository) IncrementAuthChallengeAttempts(ctx context.Context, challengeID string) error
- func (r *Repository) MarkAuthChallengeAsUsed(ctx context.Context, challengeID string) error
- func (r *Repository) SetTrialStartedAt(ctx context.Context, userID string, trialStart int64) error
- func (r *Repository) TouchSession(ctx context.Context, tokenHash string) error
- type Service
- func (s *Service) CleanupExpired(ctx context.Context) error
- func (s *Service) CreateSession(ctx context.Context, userID string) (*Session, error)
- func (s *Service) RequestAuth(ctx context.Context, email string, state string, reason *string) (bool, error)
- func (s *Service) RevokeAllSessions(ctx context.Context, userID string) error
- func (s *Service) RevokeSession(ctx context.Context, userID string, sessionToken string) error
- func (s *Service) SetBootstrapAdminEmail(email string)
- func (s *Service) SetEmailThrottle(throttle *EmailThrottle)
- func (s *Service) SetGlobalThrottle(throttle *GlobalAuthThrottle)
- func (s *Service) UsePrivateBeta(enabled bool)
- func (s *Service) ValidateSession(ctx context.Context, sessionToken string) (*User, error)
- func (s *Service) VerifyOTP(ctx context.Context, otp string, state string) (string, error)
- type Session
- type TopicInitializer
- type User
- type VerifyOTPRequest
Constants ¶
const (
TrialDuration = 14 * 24 * time.Hour
)
Variables ¶
var ( ErrOTPNotFound = fmt.Errorf("otp challenge not found: %w", core.ErrUnauthorized) ErrOTPExpired = fmt.Errorf("otp challenge expired: %w", core.ErrUnauthorized) ErrOTPUsed = fmt.Errorf("otp challenge already used: %w", core.ErrUnauthorized) ErrOTPInvalid = fmt.Errorf("invalid otp: %w", core.ErrUnauthorized) ErrOTPMaxAttempts = fmt.Errorf("max otp attempts exceeded: %w", core.ErrUnauthorized) ErrGlobalRateLimit = fmt.Errorf("global auth rate limit exceeded") )
Functions ¶
This section is empty.
Types ¶
type AuthChallenge ¶
type AuthChallenge struct {
ID string `db:"id"`
UserID string `db:"user_id"`
State string `db:"state"`
OTPHash string `db:"otp_hash"`
AttemptCount int `db:"attempt_count"`
ExpiresAt int64 `db:"expires_at"`
UsedAt *int64 `db:"used_at"`
CreatedAt int64 `db:"created_at"`
}
AuthChallenge represents an OTP challenge stored in the database.
type CreateUserOptions ¶
type CreateUserOptions struct {
AccountStatus core.AccountStatus
SignupReason *string
StartTrialOnCreate bool
}
type DBSession ¶
type DBSession struct {
TokenHash string `db:"token_hash"`
UserID string `db:"user_id"`
CreatedAt int64 `db:"created_at"`
ExpiresAt int64 `db:"expires_at"`
LastSeenAt int64 `db:"last_seen_at"`
}
DBSession represents a user session stored in the database.
type EmailThrottle ¶
type EmailThrottle struct {
// contains filtered or unexported fields
}
EmailThrottle silently drops abusive login requests for a single email identity. This keeps the auth response uniform while still protecting the mail channel.
func NewEmailThrottle ¶
NewEmailThrottle creates an email throttle with a rolling window and cooldown. Cooldown is clamped to the window so config changes cannot keep a key blocked longer than the rolling history used to justify the throttle.
func (*EmailThrottle) Allow ¶
func (t *EmailThrottle) Allow(email string) bool
Allow reports whether a request for the given email should proceed. Throttled attempts refresh the cooldown so repeated hammering stays quiet.
type GlobalAuthThrottle ¶
type GlobalAuthThrottle struct {
// contains filtered or unexported fields
}
GlobalAuthThrottle caps total login attempts per instance in a rolling window. This acts as a mail-channel safety valve when abuse is distributed across emails and IPs.
func NewGlobalAuthThrottle ¶
func NewGlobalAuthThrottle(limit int, window time.Duration) *GlobalAuthThrottle
NewGlobalAuthThrottle creates a rolling-window throttle for all auth login attempts.
func (*GlobalAuthThrottle) Allow ¶
func (g *GlobalAuthThrottle) Allow() bool
Allow reports whether another login attempt can proceed on this instance.
type Handler ¶
type Handler struct {
// contains filtered or unexported fields
}
Handler handles auth HTTP requests.
func NewHandler ¶
NewHandler creates a new auth handler.
func (*Handler) Login ¶
func (h *Handler) Login(w http.ResponseWriter, r *http.Request)
Login handles login requests.
type LoginRequest ¶
type LoginRequest struct {
Email string `json:"email"`
State string `json:"state"`
Reason *string `json:"reason,omitempty"`
}
func (*LoginRequest) Validate ¶
func (r *LoginRequest) Validate() []error
Validate enforces login-style email input and required request fields.
type MessageResponse ¶
type MessageResponse struct {
Message string `json:"message"`
}
type Repository ¶
type Repository struct {
// contains filtered or unexported fields
}
Repository provides data access for the auth domain.
func NewRepository ¶
func NewRepository(db *sqlx.DB) *Repository
NewRepository creates a new auth repository.
func (*Repository) CreateAuthChallenge ¶
func (r *Repository) CreateAuthChallenge(ctx context.Context, userID string, state string, otpHash string, expiresAt int64) (string, error)
CreateAuthChallenge invalidates any active challenges for the user and creates a new one. Delete and insert are executed within a single transaction to prevent race conditions.
func (*Repository) CreateSession ¶
func (r *Repository) CreateSession(ctx context.Context, tokenHash string, userID string, expiresAt int64) error
CreateSession creates a new session row for a hashed token.
func (*Repository) DeleteSession ¶
func (r *Repository) DeleteSession(ctx context.Context, userID string, tokenHash string) (int64, error)
DeleteSession revokes a session only when the hashed token belongs to the given user.
func (*Repository) DeleteSessionsByUserID ¶
DeleteSessionsByUserID removes all sessions for a user.
func (*Repository) DeleteStaleAuthChallenges ¶
func (r *Repository) DeleteStaleAuthChallenges(ctx context.Context) (int64, error)
DeleteStaleAuthChallenges removes challenges that are expired or already used.
func (*Repository) DeleteStaleSessions ¶
func (r *Repository) DeleteStaleSessions(ctx context.Context, expiredBefore int64, idleBefore int64) (int64, error)
DeleteStaleSessions removes expired or idle sessions and returns the number of deleted rows.
func (*Repository) EnsureUserAdmin ¶
EnsureUserAdmin promotes a user to admin if they are not already one. The method is idempotent so bootstrap logic can safely call it after OTP verification.
func (*Repository) GetAuthChallengeByState ¶
func (r *Repository) GetAuthChallengeByState(ctx context.Context, state string) (*AuthChallenge, error)
GetAuthChallengeByState retrieves an auth challenge by state.
func (*Repository) GetOrCreateUser ¶
func (r *Repository) GetOrCreateUser(ctx context.Context, email string, opts ...CreateUserOptions) (*User, bool, error)
GetOrCreateUser retrieves a user by email or creates a new one if it doesn't exist.
func (*Repository) GetSession ¶
GetSession retrieves a session by hashed token.
func (*Repository) GetUserByID ¶
GetUserByID retrieves a user by ID.
func (*Repository) IncrementAuthChallengeAttempts ¶
func (r *Repository) IncrementAuthChallengeAttempts(ctx context.Context, challengeID string) error
IncrementAuthChallengeAttempts increments the attempt counter for a challenge.
func (*Repository) MarkAuthChallengeAsUsed ¶
func (r *Repository) MarkAuthChallengeAsUsed(ctx context.Context, challengeID string) error
MarkAuthChallengeAsUsed marks a challenge as used.
func (*Repository) SetTrialStartedAt ¶
SetTrialStartedAt sets the trial_started_at timestamp for a user if not already set. Returns nil if RowsAffected == 0 (already set - idempotent).
func (*Repository) TouchSession ¶
func (r *Repository) TouchSession(ctx context.Context, tokenHash string) error
TouchSession updates the session last-seen timestamp to keep active sessions alive.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service provides authentication business logic.
func NewService ¶
func NewService(repo *Repository, m mailer.Mailer, baseURL string, topicInit TopicInitializer, logger *slog.Logger) *Service
NewService creates a new auth service.
func (*Service) CleanupExpired ¶
CleanupExpired removes expired auth artifacts that no longer have operational value.
func (*Service) CreateSession ¶
CreateSession creates a new authenticated session for the user.
func (*Service) RequestAuth ¶
func (s *Service) RequestAuth(ctx context.Context, email string, state string, reason *string) (bool, error)
RequestAuth initiates authentication for the given email.
func (*Service) RevokeAllSessions ¶
RevokeAllSessions revokes all sessions for the given user.
func (*Service) RevokeSession ¶
RevokeSession revokes a session for the given user.
func (*Service) SetBootstrapAdminEmail ¶
SetBootstrapAdminEmail configures the email identity allowed to bootstrap admin access. The value is normalized once so later comparisons stay consistent with auth identity lookup.
func (*Service) SetEmailThrottle ¶
func (s *Service) SetEmailThrottle(throttle *EmailThrottle)
SetEmailThrottle installs the login-email throttle used to silently absorb abuse.
func (*Service) SetGlobalThrottle ¶
func (s *Service) SetGlobalThrottle(throttle *GlobalAuthThrottle)
SetGlobalThrottle installs the instance-wide auth throttle used as a safety valve.
func (*Service) UsePrivateBeta ¶
UsePrivateBeta toggles waitlist gating for the auth flow.
func (*Service) ValidateSession ¶
ValidateSession validates a session token and returns the associated user.
type TopicInitializer ¶
TopicInitializer creates default topics for new users.
type VerifyOTPRequest ¶
func (*VerifyOTPRequest) Validate ¶
func (r *VerifyOTPRequest) Validate() []error