auth

package
v0.9.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 23, 2026 License: AGPL-3.0 Imports: 16 Imported by: 0

Documentation

Overview

Package auth handles user authentication via OTP and session management.

Index

Constants

View Source
const (
	TrialDuration = 14 * 24 * time.Hour
)

Variables

View Source
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

func NewEmailThrottle(limit int, window time.Duration, cooldown time.Duration) *EmailThrottle

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

func NewHandler(authSvc *Service, cookieDomain string, logger *slog.Logger) *Handler

NewHandler creates a new auth handler.

func (*Handler) Login

func (h *Handler) Login(w http.ResponseWriter, r *http.Request)

Login handles login requests.

func (*Handler) Logout

func (h *Handler) Logout(w http.ResponseWriter, r *http.Request)

Logout handles logout requests.

func (*Handler) VerifyOTP

func (h *Handler) VerifyOTP(w http.ResponseWriter, r *http.Request)

VerifyOTP handles OTP verification 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

func (r *Repository) DeleteSessionsByUserID(ctx context.Context, userID string) (int64, error)

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

func (r *Repository) EnsureUserAdmin(ctx context.Context, userID string) (bool, error)

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

func (r *Repository) GetSession(ctx context.Context, tokenHash string) (*DBSession, error)

GetSession retrieves a session by hashed token.

func (*Repository) GetUserByID

func (r *Repository) GetUserByID(ctx context.Context, userID string) (*User, error)

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

func (r *Repository) SetTrialStartedAt(ctx context.Context, userID string, trialStart int64) error

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

func (s *Service) CleanupExpired(ctx context.Context) error

CleanupExpired removes expired auth artifacts that no longer have operational value.

func (*Service) CreateSession

func (s *Service) CreateSession(ctx context.Context, userID string) (*Session, error)

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

func (s *Service) RevokeAllSessions(ctx context.Context, userID string) error

RevokeAllSessions revokes all sessions for the given user.

func (*Service) RevokeSession

func (s *Service) RevokeSession(ctx context.Context, userID string, sessionToken string) error

RevokeSession revokes a session for the given user.

func (*Service) SetBootstrapAdminEmail

func (s *Service) SetBootstrapAdminEmail(email string)

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

func (s *Service) UsePrivateBeta(enabled bool)

UsePrivateBeta toggles waitlist gating for the auth flow.

func (*Service) ValidateSession

func (s *Service) ValidateSession(ctx context.Context, sessionToken string) (*User, error)

ValidateSession validates a session token and returns the associated user.

func (*Service) VerifyOTP

func (s *Service) VerifyOTP(ctx context.Context, otp string, state string) (string, error)

VerifyOTP verifies an OTP code against the stored challenge.

type Session

type Session struct {
	Token     string
	ExpiresAt time.Time
}

type TopicInitializer

type TopicInitializer interface {
	CreateDefaultTopic(ctx context.Context, userID string) error
}

TopicInitializer creates default topics for new users.

type User

type User struct {
	ID             string             `db:"id"`
	Email          string             `db:"email"`
	IsAdmin        bool               `db:"is_admin"`
	AccountStatus  core.AccountStatus `db:"account_status"`
	TrialStartedAt *int64             `db:"trial_started_at"`
	CreatedAt      int64              `db:"created_at"`
	UpdatedAt      int64              `db:"updated_at"`
}

type VerifyOTPRequest

type VerifyOTPRequest struct {
	OTP   string `json:"otp"`
	State string `json:"state"`
}

func (*VerifyOTPRequest) Validate

func (r *VerifyOTPRequest) Validate() []error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL