user

package
v0.0.0-...-b140ed9 Latest Latest
Warning

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

Go to latest
Published: May 11, 2026 License: MIT Imports: 36 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AppendPasswordHistory

func AppendPasswordHistory(user *models.User, newHash string, keepCount int)

AppendPasswordHistory prepends the new bcrypt hash to the user's password history JSONB array and trims the array to at most keepCount entries. If keepCount is 0 the function is a no-op.

func CheckPasswordHistory

func CheckPasswordHistory(newPassword string, user *models.User, historyCount int) error

CheckPasswordHistory verifies that the new plaintext password has not been used recently. It compares against the last `historyCount` hashes stored on the user record. Returns an error if the password was recently used. Returns nil when historyCount is 0 (feature disabled).

func IsPasswordExpired

func IsPasswordExpired(user *models.User, maxAgeDays int) bool

IsPasswordExpired reports whether the user's password has exceeded the application's maximum age. Returns false when maxAgeDays is 0 (disabled) or when PasswordChangedAt is nil (password was never explicitly changed — treat as never expired to avoid locking out legacy accounts on first deploy).

func ParseCSVImport

func ParseCSVImport(r io.Reader) ([]dto.UserImportRow, []dto.UserImportRowError)

ParseCSVImport reads a CSV from r and returns valid rows plus per-row errors.

Expected header row: email, name, first_name, last_name, locale (case-insensitive). Column order is flexible — unknown columns are ignored. The first row is always treated as the header. Rows with empty or invalid email are collected as errors and excluded from the returned rows slice.

func ParseJSONImport

func ParseJSONImport(r io.Reader) ([]dto.UserImportRow, []dto.UserImportRowError)

ParseJSONImport reads a JSON payload from r and returns valid rows plus per-row errors.

Accepts two formats:

  • Top-level array: [{...}, ...]
  • Object with key: {"users": [{...}, ...]}

Each object must have at minimum an "email" field. Optional fields: name, first_name, last_name, locale.

func ResolveTokenTTLs

func ResolveTokenTTLs(app *models.Application) (accessTTL, refreshTTL time.Duration)

ResolveTokenTTLs returns the effective access and refresh token TTLs for an application. When the app has non-zero per-app overrides those are used; otherwise the function falls back to the global jwt defaults.

func ValidatePasswordPolicy

func ValidatePasswordPolicy(password string, app *models.Application) error

ValidatePasswordPolicy checks a plaintext password against the application's configured password policy. Returns a descriptive error if the password does not meet requirements, or nil if it is acceptable.

Types

type AssignDefaultRoleFunc

type AssignDefaultRoleFunc func(appID, userID string) error

AssignDefaultRoleFunc is called after user registration to assign the default role.

type GroupLogoutFunc

type GroupLogoutFunc func(appID, userEmail string)

GroupLogoutFunc is called (in a goroutine) after a successful logout when the app belongs to an SSO session group with GlobalLogout enabled. It is wired from cmd/api/main.go via adminRepo to avoid an import cycle.

type Handler

type Handler struct {
	Service               *Service
	IPRuleEvaluator       *geoip.IPRuleEvaluator    // IP access control evaluator (nil = no IP rules)
	AnomalyDetector       *log.AnomalyDetector      // Anomaly detector for login monitoring (nil = disabled)
	BruteForceService     *bruteforce.Service       // Brute-force protection service (lockout, delays, CAPTCHA)
	ValidateTrustedDevice TrustedDeviceValidateFunc // Optional: skip 2FA when a valid trusted-device cookie is present
}

func NewHandler

func NewHandler(s *Service) *Handler

func (*Handler) DeleteAccount

func (h *Handler) DeleteAccount(c *gin.Context)

@Summary Delete user account @Description Delete authenticated user's account permanently. Password is required for password-based accounts; omit for social-only (OAuth) accounts. @Tags User @Security ApiKeyAuth @Accept json @Produce json @Param delete body dto.DeleteAccountRequest true "Account Deletion Data" @Success 200 {object} dto.MessageResponse @Failure 400 {object} dto.ErrorResponse @Failure 401 {object} dto.ErrorResponse @Failure 500 {object} dto.ErrorResponse @Router /profile [delete]

func (*Handler) ForgotPassword

func (h *Handler) ForgotPassword(c *gin.Context)

@Summary Request password reset @Description Initiate password reset process @Tags Auth @Accept json @Produce json @Param email body dto.ForgotPasswordRequest true "User Email" @Success 200 {object} dto.MessageResponse @Failure 400 {object} dto.ErrorResponse @Failure 429 {object} dto.ErrorResponse @Failure 500 {object} dto.ErrorResponse @Router /forgot-password [post]

func (*Handler) GetProfile

func (h *Handler) GetProfile(c *gin.Context)

@Summary Get user profile @Description Retrieve authenticated user's profile information @Tags User @Security ApiKeyAuth @Produce json @Success 200 {object} dto.UserResponse @Failure 401 {object} dto.ErrorResponse @Failure 500 {object} dto.ErrorResponse @Router /profile [get]

func (*Handler) Login

func (h *Handler) Login(c *gin.Context)

@Summary User login @Description Authenticate user and issue JWTs @Tags Auth @Accept json @Produce json @Param login body dto.LoginRequest true "User Login Data" @Success 200 {object} dto.LoginResponse @Success 202 {object} dto.TwoFARequiredResponse "2FA verification or setup required" @Failure 400 {object} dto.ErrorResponse @Failure 401 {object} dto.ErrorResponse "May include retry_after (seconds) advisory field" @Failure 403 {object} dto.CaptchaRequiredResponse "CAPTCHA verification required" @Failure 423 {object} dto.AccountLockedResponse "Account is locked" @Failure 500 {object} dto.ErrorResponse @Router /login [post]

func (*Handler) Logout

func (h *Handler) Logout(c *gin.Context)

@Summary User logout @Description Logout user and revoke refresh token @Tags Auth @Security ApiKeyAuth @Accept json @Produce json @Param logout body dto.LogoutRequest true "Logout Data" @Success 200 {object} dto.MessageResponse @Failure 400 {object} dto.ErrorResponse @Failure 401 {object} dto.ErrorResponse @Failure 500 {object} dto.ErrorResponse @Router /logout [post]

func (*Handler) RefreshToken

func (h *Handler) RefreshToken(c *gin.Context)

@Summary Refresh access token @Description Get new access token using refresh token @Tags Auth @Accept json @Produce json @Param refresh body dto.RefreshTokenRequest true "Refresh Token" @Success 200 {object} dto.LoginResponse @Failure 400 {object} dto.ErrorResponse @Failure 401 {object} dto.ErrorResponse @Failure 429 {object} dto.ErrorResponse @Failure 500 {object} dto.ErrorResponse @Router /refresh-token [post]

func (*Handler) Register

func (h *Handler) Register(c *gin.Context)

@Summary Register a new user @Description Register a new user with email and password @Tags Auth @Accept json @Produce json @Param registration body dto.RegisterRequest true "User Registration Data" @Success 201 {object} dto.UserResponse @Failure 400 {object} dto.ErrorResponse @Failure 409 {object} dto.ErrorResponse @Failure 429 {object} dto.ErrorResponse @Failure 500 {object} dto.ErrorResponse @Router /register [post]

func (h *Handler) RequestMagicLink(c *gin.Context)

@Summary Request a magic link login email @Description Send a magic link to the user's email for passwordless authentication. Always returns 200 regardless of whether the email exists (to prevent enumeration). @Tags Auth @Accept json @Produce json @Param request body dto.MagicLinkRequest true "Magic Link Request Data" @Success 200 {object} dto.MessageResponse @Failure 400 {object} dto.ErrorResponse @Failure 429 {object} dto.ErrorResponse @Failure 500 {object} dto.ErrorResponse @Router /magic-link/request [post]

func (*Handler) ResendVerification

func (h *Handler) ResendVerification(c *gin.Context)

@Summary Resend email verification @Description Resend verification email to user. Returns a generic success message regardless of whether the email exists or is already verified (to prevent email enumeration). @Tags Auth @Accept json @Produce json @Param request body dto.ResendVerificationRequest true "Email address" @Success 200 {object} dto.MessageResponse @Failure 400 {object} dto.ErrorResponse @Failure 429 {object} dto.ErrorResponse @Failure 500 {object} dto.ErrorResponse @Router /resend-verification [post]

func (*Handler) ResetPassword

func (h *Handler) ResetPassword(c *gin.Context)

@Summary Reset password @Description Complete password reset process @Tags Auth @Accept json @Produce json @Param reset body dto.ResetPasswordRequest true "Reset Token and New Password" @Success 200 {object} dto.MessageResponse @Failure 400 {object} dto.ErrorResponse @Failure 401 {object} dto.ErrorResponse @Failure 429 {object} dto.ErrorResponse @Failure 500 {object} dto.ErrorResponse @Router /reset-password [post]

func (*Handler) SetPassword

func (h *Handler) SetPassword(c *gin.Context)

@Summary Set initial password for social-only users @Description Set a password for a user who registered via social login and has no password yet. Returns 409 if a password is already set. @Tags User @Security ApiKeyAuth @Accept json @Produce json @Param request body dto.SetPasswordRequest true "New password" @Success 200 {object} dto.MessageResponse @Failure 400 {object} dto.ErrorResponse @Failure 401 {object} dto.ErrorResponse @Failure 409 {object} dto.ErrorResponse @Failure 500 {object} dto.ErrorResponse @Router /profile/set-password [post]

func (*Handler) UpdateEmail

func (h *Handler) UpdateEmail(c *gin.Context)

@Summary Update user email @Description Update authenticated user's email address (requires password verification and email re-verification) @Tags User @Security ApiKeyAuth @Accept json @Produce json @Param email body dto.UpdateEmailRequest true "Email Update Data" @Success 200 {object} dto.MessageResponse @Failure 400 {object} dto.ErrorResponse @Failure 401 {object} dto.ErrorResponse @Failure 409 {object} dto.ErrorResponse @Failure 500 {object} dto.ErrorResponse @Router /profile/email [put]

func (*Handler) UpdatePassword

func (h *Handler) UpdatePassword(c *gin.Context)

@Summary Update user password @Description Update authenticated user's password (requires current password verification) @Tags User @Security ApiKeyAuth @Accept json @Produce json @Param password body dto.UpdatePasswordRequest true "Password Update Data" @Success 200 {object} dto.MessageResponse @Failure 400 {object} dto.ErrorResponse @Failure 401 {object} dto.ErrorResponse @Failure 500 {object} dto.ErrorResponse @Router /profile/password [put]

func (*Handler) UpdateProfile

func (h *Handler) UpdateProfile(c *gin.Context)

@Summary Update user profile @Description Update authenticated user's profile information (name, first_name, last_name, profile_picture, locale) @Tags User @Security ApiKeyAuth @Accept json @Produce json @Param profile body dto.UpdateProfileRequest true "Profile Update Data" @Success 200 {object} dto.UserResponse @Failure 400 {object} dto.ErrorResponse @Failure 401 {object} dto.ErrorResponse @Failure 500 {object} dto.ErrorResponse @Router /profile [put]

func (*Handler) ValidateToken

func (h *Handler) ValidateToken(c *gin.Context)

ValidateToken godoc @Summary Validate JWT Token @Description Validates a JWT token and returns basic user info for external services @Tags auth @Security ApiKeyAuth @Produce json @Success 200 {object} map[string]interface{} @Failure 401 {object} dto.ErrorResponse @Failure 500 {object} dto.ErrorResponse @Router /auth/validate [get]

func (*Handler) VerifyEmail

func (h *Handler) VerifyEmail(c *gin.Context)

@Summary Verify email @Description Verify user's email address @Tags Auth @Produce json @Param token query string true "Verification Token" @Success 200 {object} dto.MessageResponse @Failure 400 {object} dto.ErrorResponse @Failure 401 {object} dto.ErrorResponse @Failure 500 {object} dto.ErrorResponse @Router /verify-email [get]

func (h *Handler) VerifyMagicLink(c *gin.Context)

@Summary Verify a magic link token and log in @Description Verify the magic link token from the email and return access/refresh tokens. The token is single-use and expires after 10 minutes. @Tags Auth @Accept json @Produce json @Param request body dto.MagicLinkVerifyRequest true "Magic Link Verification Data" @Success 200 {object} dto.LoginResponse @Failure 400 {object} dto.ErrorResponse @Failure 401 {object} dto.ErrorResponse @Failure 403 {object} dto.ErrorResponse @Failure 429 {object} dto.ErrorResponse @Failure 500 {object} dto.ErrorResponse @Router /magic-link/verify [post]

type LoginResult

type LoginResult struct {
	RequiresTwoFA      bool
	RequiresTwoFASetup bool
	PasswordExpired    bool // true when the password has exceeded its max age; no tokens are issued
	UserID             uuid.UUID
	AccessToken        string // #nosec G101,G117 -- This is a result field, not a hardcoded credential
	RefreshToken       string // #nosec G101,G117 -- This is a result field, not a hardcoded credential
	SessionID          string
	TwoFAResponse      *dto.TwoFARequiredResponse
	TwoFASetupResponse *dto.TwoFASetupRequiredResponse
}

LoginResult represents the result of a login attempt

type Repository

type Repository struct {
	// contains filtered or unexported fields
}

func NewRepository

func NewRepository(pool *pgxpool.Pool) *Repository

func (*Repository) ClearBackupEmail

func (r *Repository) ClearBackupEmail(userID string) error

ClearBackupEmail removes the backup email and its verified status.

func (*Repository) ClearLockout

func (r *Repository) ClearLockout(userID string) error

ClearLockout clears the lockout fields for a user (auto-unlock on expired lockout).

func (*Repository) ClearPhone

func (r *Repository) ClearPhone(userID string) error

ClearPhone removes the phone number and its verified status.

func (*Repository) CreateUser

func (r *Repository) CreateUser(user *models.User) error

func (*Repository) DeleteUser

func (r *Repository) DeleteUser(userID string) error

DeleteUser deletes a user and all related data within a transaction. FK-constrained tables (social_accounts, user_roles) are deleted first to avoid "update or delete violates foreign key constraint" errors from NO ACTION constraints.

func (*Repository) Disable2FA

func (r *Repository) Disable2FA(userID string) error

Disable2FA disables 2FA for a user

func (*Repository) Enable2FA

func (r *Repository) Enable2FA(userID, secret, recoveryCodes string) error

Enable2FA enables 2FA for a user and stores the secret and recovery codes. Defaults to TOTP method for backward compatibility.

func (*Repository) Enable2FAWithMethod

func (r *Repository) Enable2FAWithMethod(userID, secret, recoveryCodes, method string) error

Enable2FAWithMethod enables 2FA for a user with a specific method ("totp" or "email").

func (*Repository) GetUserByEmail

func (r *Repository) GetUserByEmail(appID, email string) (*models.User, error)

func (*Repository) GetUserByID

func (r *Repository) GetUserByID(id string) (*models.User, error)

func (*Repository) RestorePreviousTwoFAMethod

func (r *Repository) RestorePreviousTwoFAMethod(userID string) error

RestorePreviousTwoFAMethod reverts a user from backup_email 2FA back to their prior method. It reads the previously saved method/secret, restores them, and clears the "previous" fields. If no prior method was saved the user ends up with 2FA disabled.

func (*Repository) SaveAndSwitchToBackupEmail2FA

func (r *Repository) SaveAndSwitchToBackupEmail2FA(userID, previousMethod, previousSecret, recoveryCodes string) error

SaveAndSwitchToBackupEmail2FA atomically saves the user's current 2FA method/secret as "previous" fields and switches the active method to backup_email. This allows DisableBackupEmail2FAMethod to fully restore the prior configuration.

func (*Repository) SetBackupEmail

func (r *Repository) SetBackupEmail(userID, backupEmail string) error

SetBackupEmail sets the pending backup email for a user (not yet verified).

func (*Repository) SetPhoneNumber

func (r *Repository) SetPhoneNumber(userID, phone string) error

SetPhoneNumber sets the phone number for a user (not yet verified).

func (*Repository) UpdateRecoveryCodes

func (r *Repository) UpdateRecoveryCodes(userID, recoveryCodes string) error

UpdateRecoveryCodes updates the recovery codes for a user

func (*Repository) UpdateUser

func (r *Repository) UpdateUser(user *models.User) error

func (*Repository) UpdateUserEmail

func (r *Repository) UpdateUserEmail(userID, newEmail string) error

UpdateUserEmail updates user email and sets email_verified to false

func (*Repository) UpdateUserEmailVerified

func (r *Repository) UpdateUserEmailVerified(userID string, verified bool) error

func (*Repository) UpdateUserPassword

func (r *Repository) UpdateUserPassword(userID, hashedPassword string) error

func (*Repository) UpdateUserPasswordWithHistory

func (r *Repository) UpdateUserPasswordWithHistory(userID, hashedPassword string, history []byte) error

UpdateUserPasswordWithHistory sets password_hash, password_history, and password_changed_at atomically.

func (*Repository) UpdateUserProfile

func (r *Repository) UpdateUserProfile(userID string, updates map[string]interface{}) error

UpdateUserProfile updates user profile fields (name, first_name, last_name, profile_picture, locale). Accepts a map for backward compatibility with the service layer's partial-update logic. Reads current values first, merges provided updates, then writes all profile fields.

func (*Repository) VerifyBackupEmail

func (r *Repository) VerifyBackupEmail(userID string) error

VerifyBackupEmail marks the backup email as verified.

func (*Repository) VerifyPhoneNumber

func (r *Repository) VerifyPhoneNumber(userID string) error

VerifyPhoneNumber marks the phone number as verified.

type RoleLookupFunc

type RoleLookupFunc func(appID, userID string) ([]string, error)

RoleLookupFunc is a function that returns role names for a user in an app. Used to populate JWT claims with roles without importing the rbac package directly.

type Service

type Service struct {
	Repo              *Repository
	EmailService      *emailpkg.Service
	AppLookup         func(appID string) (*models.Application, error) // Resolves app config by ID (wired in main.go)
	SessionService    *session.Service                                // Session management for multi-device tracking
	LookupRoles       RoleLookupFunc                                  // Optional: if nil, tokens are generated without roles
	AssignDefaultRole AssignDefaultRoleFunc                           // Optional: if nil, no default role is assigned on registration
	WebhookService    *webhook.Service                                // Optional: if nil, webhook dispatch is skipped
	SMSSender         sms.Sender                                      // Optional: if nil, SMS 2FA auto-send is skipped
	GroupLogoutFunc   GroupLogoutFunc                                 // Optional: if non-nil, called after logout for SSO group propagation
}

func NewService

func NewService(r *Repository, es *emailpkg.Service) *Service

func (*Service) ConfirmPasswordReset

func (s *Service) ConfirmPasswordReset(appID uuid.UUID, token, newPassword string) (uuid.UUID, *errors.AppError)

func (*Service) CreateSessionForUser

func (s *Service) CreateSessionForUser(appID, userID uuid.UUID, ip, userAgent string) (accessToken, refreshToken string, appErr *errors.AppError)

CreateSessionForUser creates a new authenticated session for a user by app+userID. Used by the trusted-device bypass in the Login handler to issue tokens when 2FA is skipped.

func (*Service) DeleteUserAccount

func (s *Service) DeleteUserAccount(appID uuid.UUID, userID string, req dto.DeleteAccountRequest) *errors.AppError

DeleteUserAccount deletes the user account after verifying password

func (*Service) LoginUser

func (s *Service) LoginUser(appID uuid.UUID, email, password, ip, userAgent string) (*LoginResult, *errors.AppError)

func (*Service) LogoutUser

func (s *Service) LogoutUser(appID, userID, sessionID, refreshToken, accessToken string) *errors.AppError

LogoutUser logs out a user by revoking their session and blacklisting their access token

func (*Service) RefreshUserToken

func (s *Service) RefreshUserToken(refreshToken string, accessTTL, refreshTTL time.Duration) (string, string, string, *errors.AppError)

func (*Service) RegisterUser

func (s *Service) RegisterUser(appID uuid.UUID, email, password string) (uuid.UUID, *errors.AppError)
func (s *Service) RequestMagicLink(appID uuid.UUID, email string) *errors.AppError

RequestMagicLink generates a magic link token and sends it via email. Returns nil even if the user is not found (to prevent email enumeration).

func (*Service) RequestPasswordReset

func (s *Service) RequestPasswordReset(appID uuid.UUID, email string) *errors.AppError

func (*Service) ResendVerificationEmail

func (s *Service) ResendVerificationEmail(appID uuid.UUID, email string) *errors.AppError

ResendVerificationEmail resends the email verification link for a user. Returns nil even if the user is not found or already verified (to prevent email enumeration).

func (*Service) RevokeAllUserTokens

func (s *Service) RevokeAllUserTokens(appID, userID string) *errors.AppError

RevokeAllUserTokens revokes all access and refresh tokens for a user This is used for security events like password changes, account compromise, etc.

func (*Service) SetInitialPassword

func (s *Service) SetInitialPassword(appID uuid.UUID, userID string, newPassword string) *errors.AppError

SetInitialPassword sets a password for a social-only user (no existing PasswordHash). Returns ErrConflict if the user already has a password — callers should use UpdateUserPassword instead in that case.

func (*Service) UpdateUserEmail

func (s *Service) UpdateUserEmail(appID uuid.UUID, userID string, req dto.UpdateEmailRequest) *errors.AppError

UpdateUserEmail updates the user's email address after verifying password

func (*Service) UpdateUserPassword

func (s *Service) UpdateUserPassword(appID uuid.UUID, userID string, req dto.UpdatePasswordRequest) *errors.AppError

UpdateUserPassword updates the user's password after verifying current password

func (*Service) UpdateUserProfile

func (s *Service) UpdateUserProfile(userID string, req dto.UpdateProfileRequest) *errors.AppError

UpdateUserProfile updates the user profile information (name, first_name, last_name, profile_picture, locale)

func (*Service) VerifyEmail

func (s *Service) VerifyEmail(appID uuid.UUID, token string) (uuid.UUID, *errors.AppError)
func (s *Service) VerifyMagicLink(appID uuid.UUID, token, ip, userAgent string) (*LoginResult, *errors.AppError)

VerifyMagicLink verifies a magic link token and creates a session (passwordless login). 2FA is skipped since the magic link itself serves as email-based verification.

type TrustedDeviceValidateFunc

type TrustedDeviceValidateFunc func(plainToken string) (userID uuid.UUID, appID uuid.UUID, ok bool)

TrustedDeviceValidateFunc validates a plain trusted-device token and returns the associated userID and appID on success. It is called during Login to skip 2FA when a valid trusted-device cookie is present. Wired from main.go to avoid an import cycle.

Jump to

Keyboard shortcuts

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