Documentation
¶
Overview ¶
Package auth provides JWT authentication for the SOC HTTP API. Uses HMAC-SHA256 (HS256) with configurable secret. Zero external dependencies — pure Go stdlib.
Index ¶
- Constants
- Variables
- func APIKeyMiddleware(store *UserStore, next http.Handler) http.Handler
- func HandleBillingStatus(tenantStore *TenantStore) http.HandlerFunc
- func HandleCreateAPIKey(store *UserStore) http.HandlerFunc
- func HandleCreateUser(store *UserStore) http.HandlerFunc
- func HandleDeleteAPIKey(store *UserStore) http.HandlerFunc
- func HandleDeleteUser(store *UserStore) http.HandlerFunc
- func HandleDemo(userStore *UserStore, tenantStore *TenantStore, secret []byte) http.HandlerFunc
- func HandleGetTenant(tenantStore *TenantStore) http.HandlerFunc
- func HandleImpersonateTenant(tenantStore *TenantStore, jwtSecret []byte) http.HandlerFunc
- func HandleListAPIKeys(store *UserStore) http.HandlerFunc
- func HandleListPlans() http.HandlerFunc
- func HandleListTenants(tenantStore *TenantStore) http.HandlerFunc
- func HandleListUsers(store *UserStore) http.HandlerFunc
- func HandleLogin(store *UserStore, secret []byte) http.HandlerFunc
- func HandleLogout() http.HandlerFunc
- func HandleMe(store *UserStore) http.HandlerFunc
- func HandleRefresh(secret []byte) http.HandlerFunc
- func HandleRegister(userStore *UserStore, tenantStore *TenantStore, jwtSecret []byte, ...) http.HandlerFunc
- func HandleStripeWebhook(tenantStore *TenantStore) http.HandlerFunc
- func HandleUpdateTenantPlan(tenantStore *TenantStore) http.HandlerFunc
- func HandleUpdateUser(store *UserStore) http.HandlerFunc
- func HandleVerifyEmail(userStore *UserStore, tenantStore *TenantStore, jwtSecret []byte) http.HandlerFunc
- func NewAccessToken(subject, role string, secret []byte, ttl time.Duration) (string, error)
- func NewRefreshToken(subject, role string, secret []byte, ttl time.Duration) (string, error)
- func RateLimitMiddleware(rl *RateLimiter, next http.HandlerFunc) http.HandlerFunc
- func SeedDemoTenant(userStore *UserStore, tenantStore *TenantStore, socRepo domsoc.SOCRepository)
- func SetClaimsContext(ctx context.Context, claims *Claims) context.Context
- func Sign(claims Claims, secret []byte) (string, error)
- type APIKey
- type Claims
- type EmailSendFunc
- type JWTMiddleware
- type LoginRequest
- type Plan
- type RateLimiter
- type Tenant
- type TenantStore
- func (s *TenantStore) CreateTenant(name, slug, ownerUserID, planID string) (*Tenant, error)
- func (s *TenantStore) DeactivateTenant(tenantID string) error
- func (s *TenantStore) GetTenant(id string) (*Tenant, error)
- func (s *TenantStore) GetTenantBySlug(slug string) (*Tenant, error)
- func (s *TenantStore) IncrementEvents(tenantID string, count int) error
- func (s *TenantStore) ListTenants() []*Tenant
- func (s *TenantStore) SetStripeIDs(tenantID, customerID, subID string) error
- func (s *TenantStore) UpdatePlan(tenantID, planID string) error
- type TokenResponse
- type UsageInfo
- type UsageTracker
- type User
- type UserStore
- func (s *UserStore) Authenticate(email, password string) (*User, error)
- func (s *UserStore) ChangePassword(id, newPassword string) error
- func (s *UserStore) CreateAPIKey(userID, name, role string) (string, *APIKey, error)
- func (s *UserStore) CreateUser(email, displayName, password, role string) (*User, error)
- func (s *UserStore) DeleteAPIKey(keyID, userID string) error
- func (s *UserStore) DeleteUser(id string) error
- func (s *UserStore) GetByEmail(email string) (*User, error)
- func (s *UserStore) GetByID(id string) (*User, error)
- func (s *UserStore) ListAPIKeys(userID string) ([]APIKey, error)
- func (s *UserStore) ListAllAPIKeys() ([]APIKey, error)
- func (s *UserStore) ListUsers() []*User
- func (s *UserStore) SetVerifyToken(email string) (string, error)
- func (s *UserStore) UpdateUser(id, displayName, role string, active bool) error
- func (s *UserStore) ValidateAPIKey(key string) (string, string, error)
- func (s *UserStore) VerifyEmail(email, code string) error
Constants ¶
const DemoTenantID = "tnt-demo-000000"
DemoTenantID is the fixed ID for the demo tenant.
const DemoUserEmail = "demo@syntrex.pro"
DemoUserEmail is the login email for the demo account.
const DemoUserPassword = "demo"
DemoUserPassword is the demo account password (read-only viewer).
Variables ¶
var ( ErrInvalidToken = errors.New("auth: invalid token") ErrExpiredToken = errors.New("auth: token expired") ErrInvalidSecret = errors.New("auth: secret too short (min 32 bytes)") ErrWrongTokenType = errors.New("auth: wrong token type") )
Standard JWT errors.
var ( ErrTenantNotFound = errors.New("auth: tenant not found") ErrTenantExists = errors.New("auth: tenant already exists") ErrQuotaExceeded = errors.New("auth: plan quota exceeded") )
Standard tenant errors.
var ( ErrUserNotFound = errors.New("auth: user not found") ErrUserExists = errors.New("auth: user already exists") ErrInvalidPassword = errors.New("auth: invalid password") ErrUserDisabled = errors.New("auth: account disabled") ErrEmailNotVerified = errors.New("auth: email not verified") ErrInvalidVerifyCode = errors.New("auth: invalid or expired verification code") )
Standard user errors.
var DefaultPlans = map[string]Plan{ "free": { ID: "free", Name: "Free", Description: "Scanner API — 1 000 сканов/мес, все 66 движков, без SOC Dashboard", MaxUsers: 1, MaxEventsMonth: 1000, MaxIncidents: 100, MaxSensors: 1, MaxScansMonth: 1000, RetentionDays: 3, SOCEnabled: false, SLAEnabled: false, SOAREnabled: false, ComplianceEnabled: false, PriceMonthCents: 0, }, "demo": { ID: "demo", Name: "Demo Sandbox", Description: "Общая демо-песочница. Жёсткий лимит.", MaxUsers: 10, MaxEventsMonth: 1000, MaxIncidents: 100, MaxSensors: 5, MaxScansMonth: 1000, RetentionDays: 1, SOCEnabled: true, SLAEnabled: false, SOAREnabled: false, ComplianceEnabled: false, PriceMonthCents: 0, }, "starter": { ID: "starter", Name: "Starter", Description: "AI-мониторинг: до 5 сенсоров, базовая корреляция и алерты", MaxUsers: 10, MaxEventsMonth: 100000, MaxIncidents: 200, MaxSensors: 5, MaxScansMonth: 100000, RetentionDays: 30, SOCEnabled: true, SLAEnabled: true, SOAREnabled: false, ComplianceEnabled: false, PriceMonthCents: 8990000, }, "professional": { ID: "professional", Name: "Professional", Description: "Полный AI SOC: SOAR, compliance, расширенная аналитика", MaxUsers: 50, MaxEventsMonth: 500000, MaxIncidents: 1000, MaxSensors: 25, MaxScansMonth: 500000, RetentionDays: 90, SOCEnabled: true, SLAEnabled: true, SOAREnabled: true, ComplianceEnabled: true, PriceMonthCents: 14990000, }, "enterprise": { ID: "enterprise", Name: "Enterprise", Description: "On-premise / выделенный инстанс. Сертификация — на стороне заказчика", MaxUsers: -1, MaxEventsMonth: -1, MaxIncidents: -1, MaxSensors: -1, MaxScansMonth: -1, RetentionDays: 365, SOCEnabled: true, SLAEnabled: true, SOAREnabled: true, ComplianceEnabled: true, OnPremise: true, PriceMonthCents: -1, }, }
DefaultPlans defines the standard pricing tiers (prices in RUB kopecks).
Functions ¶
func APIKeyMiddleware ¶
APIKeyMiddleware checks for API key authentication alongside JWT. If Authorization header starts with "stx_", validate as API key. SEC-CRIT3: Now resolves user from DB to inject correct TenantID.
func HandleBillingStatus ¶
func HandleBillingStatus(tenantStore *TenantStore) http.HandlerFunc
HandleBillingStatus returns the billing status for the tenant. GET /api/auth/billing
func HandleCreateAPIKey ¶
func HandleCreateAPIKey(store *UserStore) http.HandlerFunc
HandleCreateAPIKey generates a new API key for the authenticated user. POST /api/auth/keys
func HandleCreateUser ¶
func HandleCreateUser(store *UserStore) http.HandlerFunc
HandleCreateUser creates a new user (admin only). POST /api/auth/users
func HandleDeleteAPIKey ¶
func HandleDeleteAPIKey(store *UserStore) http.HandlerFunc
HandleDeleteAPIKey revokes an API key. DELETE /api/auth/keys/{id}
func HandleDeleteUser ¶
func HandleDeleteUser(store *UserStore) http.HandlerFunc
HandleDeleteUser deletes a user (admin only). DELETE /api/auth/users/{id}
func HandleDemo ¶
func HandleDemo(userStore *UserStore, tenantStore *TenantStore, secret []byte) http.HandlerFunc
HandleDemo provisions a read-only demo session and logs the user in. GET /api/auth/demo
func HandleGetTenant ¶
func HandleGetTenant(tenantStore *TenantStore) http.HandlerFunc
HandleGetTenant returns the current tenant info. GET /api/auth/tenant
func HandleImpersonateTenant ¶
func HandleImpersonateTenant(tenantStore *TenantStore, jwtSecret []byte) http.HandlerFunc
HandleImpersonateTenant allows a superadmin to switch their working tenant context. POST /api/auth/impersonate
func HandleListAPIKeys ¶
func HandleListAPIKeys(store *UserStore) http.HandlerFunc
HandleListAPIKeys returns API keys for the authenticated user, or all keys for superadmin. GET /api/auth/keys
func HandleListPlans ¶
func HandleListPlans() http.HandlerFunc
HandleListPlans returns all available pricing plans. GET /api/auth/plans
func HandleListTenants ¶
func HandleListTenants(tenantStore *TenantStore) http.HandlerFunc
HandleListTenants returns a list of all tenants for superadmin dashboard. GET /api/auth/tenants
func HandleListUsers ¶
func HandleListUsers(store *UserStore) http.HandlerFunc
HandleListUsers returns users scoped to the caller's tenant (admin only). GET /api/auth/users SEC-HIGH1: Returns empty list when TenantID is empty to prevent cross-tenant leak.
func HandleLogin ¶
func HandleLogin(store *UserStore, secret []byte) http.HandlerFunc
HandleLogin creates an HTTP handler for POST /api/auth/login.
func HandleLogout ¶
func HandleLogout() http.HandlerFunc
HandleLogout clears the auth cookies. POST /api/auth/logout
func HandleMe ¶
func HandleMe(store *UserStore) http.HandlerFunc
HandleMe returns the current authenticated user profile. GET /api/auth/me
func HandleRefresh ¶
func HandleRefresh(secret []byte) http.HandlerFunc
HandleRefresh creates an HTTP handler for POST /api/auth/refresh.
func HandleRegister ¶
func HandleRegister(userStore *UserStore, tenantStore *TenantStore, jwtSecret []byte, emailFn EmailSendFunc) http.HandlerFunc
HandleRegister processes new tenant + owner registration. POST /api/auth/register { email, password, name, org_name, org_slug } Returns verification_required — user must verify email before login. If emailFn is nil, verification code is returned in response (dev mode).
func HandleStripeWebhook ¶
func HandleStripeWebhook(tenantStore *TenantStore) http.HandlerFunc
HandleStripeWebhook processes Stripe webhook events. POST /api/billing/webhook
func HandleUpdateTenantPlan ¶
func HandleUpdateTenantPlan(tenantStore *TenantStore) http.HandlerFunc
HandleUpdateTenantPlan upgrades/downgrades the tenant plan. POST /api/auth/tenant/plan { plan_id } SEC: Only allows downgrade to 'free' without payment. Paid upgrades require Stripe webhook confirmation (HandleStripeWebhook). This prevents users from clicking "Перейти" on paid plans and getting access without payment.
func HandleUpdateUser ¶
func HandleUpdateUser(store *UserStore) http.HandlerFunc
HandleUpdateUser updates a user's profile (admin only). PUT /api/auth/users/{id}
func HandleVerifyEmail ¶
func HandleVerifyEmail(userStore *UserStore, tenantStore *TenantStore, jwtSecret []byte) http.HandlerFunc
HandleVerifyEmail validates the verification code and issues JWT. POST /api/auth/verify { email, code }
func NewAccessToken ¶
NewAccessToken creates a short-lived access token (15 min default).
func NewRefreshToken ¶
NewRefreshToken creates a long-lived refresh token (7 days default).
func RateLimitMiddleware ¶
func RateLimitMiddleware(rl *RateLimiter, next http.HandlerFunc) http.HandlerFunc
RateLimitMiddleware wraps an http.HandlerFunc with rate limiting.
func SeedDemoTenant ¶
func SeedDemoTenant(userStore *UserStore, tenantStore *TenantStore, socRepo domsoc.SOCRepository)
SeedDemoTenant creates an isolated demo tenant with pre-seeded SOC data. Idempotent: skips if demo user already exists. The demo user has role "viewer" (read-only) and is pre-verified.
func SetClaimsContext ¶
SetClaimsContext injects claims into a context (used by API key auth).
Types ¶
type APIKey ¶
type APIKey struct {
ID string `json:"id"`
UserID string `json:"user_id"`
Name string `json:"name"`
Role string `json:"role"`
KeyPrefix string `json:"key_prefix"` // first 8 chars for display
CreatedAt time.Time `json:"created_at"`
LastUsed *time.Time `json:"last_used,omitempty"`
}
APIKey represents an API key for programmatic access.
type Claims ¶
type Claims struct {
Sub string `json:"sub"` // Subject (username or user ID)
Role string `json:"role"` // RBAC role: admin, operator, analyst, viewer
TenantID string `json:"tenant_id,omitempty"` // Multi-tenant isolation
TokenType string `json:"token_type,omitempty"` // "access" or "refresh"
Exp int64 `json:"exp"` // Expiration (Unix timestamp)
Iat int64 `json:"iat"` // Issued at
Iss string `json:"iss,omitempty"` // Issuer
}
Claims represents JWT payload.
type EmailSendFunc ¶
EmailSendFunc is a callback for sending verification emails. Signature: func(toEmail, userName, code string) error
type JWTMiddleware ¶
type JWTMiddleware struct {
// PublicPaths are exempt from auth (e.g., /health, /api/auth/login).
PublicPaths map[string]bool
// contains filtered or unexported fields
}
JWTMiddleware validates Bearer tokens on protected routes.
func NewJWTMiddleware ¶
func NewJWTMiddleware(secret []byte) *JWTMiddleware
NewJWTMiddleware creates JWT middleware with the given secret.
func (*JWTMiddleware) Middleware ¶
func (m *JWTMiddleware) Middleware(next http.Handler) http.Handler
Middleware wraps an http.Handler with JWT validation.
type LoginRequest ¶
LoginRequest is the POST /api/auth/login body.
type Plan ¶
type Plan struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
MaxUsers int `json:"max_users"`
MaxEventsMonth int `json:"max_events_month"` // SOC event ingestion quota (-1=unlimited)
MaxIncidents int `json:"max_incidents"`
MaxSensors int `json:"max_sensors"`
MaxScansMonth int `json:"max_scans_month"` // /api/v1/scan quota (-1=unlimited, 0=none)
RetentionDays int `json:"retention_days"`
SOCEnabled bool `json:"soc_enabled"` // SOC Dashboard access
SLAEnabled bool `json:"sla_enabled"`
SOAREnabled bool `json:"soar_enabled"`
ComplianceEnabled bool `json:"compliance_enabled"`
OnPremise bool `json:"on_premise"` // Enterprise: on-premise deployment
PriceMonthCents int `json:"price_month_cents"` // 0 = free, -1 = custom pricing
}
Plan represents a subscription tier with resource limits.
type RateLimiter ¶
type RateLimiter struct {
// contains filtered or unexported fields
}
RateLimiter tracks login attempts per IP using a sliding window.
func NewRateLimiter ¶
func NewRateLimiter(maxHits int, window time.Duration) *RateLimiter
NewRateLimiter creates a rate limiter. maxHits: max attempts per window per IP. window: sliding window duration.
func (*RateLimiter) Allow ¶
func (rl *RateLimiter) Allow(ip string) bool
Allow checks if the IP is within the rate limit. Returns true if allowed, false if rate-limited.
func (*RateLimiter) Reset ¶
func (rl *RateLimiter) Reset(ip string)
Reset clears attempts for an IP (e.g., on successful login).
type Tenant ¶
type Tenant struct {
ID string `json:"id"`
Name string `json:"name"`
Slug string `json:"slug"`
PlanID string `json:"plan_id"`
PaymentCustomerID string `json:"payment_customer_id,omitempty"`
PaymentSubID string `json:"payment_sub_id,omitempty"`
OwnerUserID string `json:"owner_user_id"`
Active bool `json:"active"`
CreatedAt time.Time `json:"created_at"`
EventsThisMonth int `json:"events_this_month"`
MonthResetAt time.Time `json:"month_reset_at"`
}
Tenant represents an isolated organization in the multi-tenant system.
func (*Tenant) CanAccessSOC ¶
CanAccessSOC returns true if the tenant's plan includes SOC Dashboard access.
func (*Tenant) CanIngestEvent ¶
CanIngestEvent checks if the tenant can still ingest events this month.
type TenantStore ¶
type TenantStore struct {
// contains filtered or unexported fields
}
TenantStore manages tenant records backed by SQLite.
func NewTenantStore ¶
func NewTenantStore(db *sql.DB) *TenantStore
NewTenantStore creates a tenant store.
func (*TenantStore) CreateTenant ¶
func (s *TenantStore) CreateTenant(name, slug, ownerUserID, planID string) (*Tenant, error)
CreateTenant creates a new tenant and assigns an owner.
func (*TenantStore) DeactivateTenant ¶
func (s *TenantStore) DeactivateTenant(tenantID string) error
DeactivateTenant marks a tenant as inactive (subscription cancelled).
func (*TenantStore) GetTenant ¶
func (s *TenantStore) GetTenant(id string) (*Tenant, error)
GetTenant returns a tenant by ID.
func (*TenantStore) GetTenantBySlug ¶
func (s *TenantStore) GetTenantBySlug(slug string) (*Tenant, error)
GetTenantBySlug returns a tenant by slug.
func (*TenantStore) IncrementEvents ¶
func (s *TenantStore) IncrementEvents(tenantID string, count int) error
IncrementEvents increments the monthly event counter. Returns error if quota exceeded.
func (*TenantStore) ListTenants ¶
func (s *TenantStore) ListTenants() []*Tenant
ListTenants returns all tenants.
func (*TenantStore) SetStripeIDs ¶
func (s *TenantStore) SetStripeIDs(tenantID, customerID, subID string) error
SetStripeIDs saves Stripe customer + subscription IDs.
func (*TenantStore) UpdatePlan ¶
func (s *TenantStore) UpdatePlan(tenantID, planID string) error
UpdatePlan changes a tenant's plan.
type TokenResponse ¶
type UsageInfo ¶
type UsageInfo struct {
Plan string `json:"plan"`
ScansUsed int `json:"scans_used"`
ScansLimit int `json:"scans_limit"`
Remaining int `json:"remaining"`
PeriodStart time.Time `json:"period_start"`
PeriodEnd time.Time `json:"period_end"`
Unlimited bool `json:"unlimited"`
}
UsageInfo represents current usage state for a caller.
type UsageTracker ¶
type UsageTracker struct {
// contains filtered or unexported fields
}
UsageTracker tracks scan usage per user/IP with monthly quotas.
func NewUsageTracker ¶
func NewUsageTracker(db *sql.DB) *UsageTracker
NewUsageTracker creates a usage tracker backed by PostgreSQL.
func (*UsageTracker) GetUsage ¶
func (t *UsageTracker) GetUsage(userID, ip string) *UsageInfo
GetUsage returns current usage for a user or IP.
func (*UsageTracker) RecordScan ¶
func (t *UsageTracker) RecordScan(userID, ip string) (int, error)
RecordScan atomically increments the scan counter and checks quota. Uses the free tier default limit (1000). For plan-aware quotas, use RecordScanWithLimit.
func (*UsageTracker) RecordScanWithLimit ¶
func (t *UsageTracker) RecordScanWithLimit(userID, ip string, planLimit int) (int, error)
RecordScanWithLimit atomically increments the scan counter and checks against planLimit. planLimit: -1=unlimited, 0=no scans allowed, >0=monthly cap. Returns remaining scans. Returns error if quota exceeded.
func (*UsageTracker) ResetExpired ¶
func (t *UsageTracker) ResetExpired()
ResetExpired cleans up old quota records from previous periods.
type User ¶
type User struct {
ID string `json:"id"`
Email string `json:"email"`
DisplayName string `json:"display_name"`
Role string `json:"role"` // admin, analyst, viewer
TenantID string `json:"tenant_id,omitempty"`
Active bool `json:"active"`
EmailVerified bool `json:"email_verified"`
PasswordHash string `json:"-"` // never serialized
VerifyToken string `json:"-"` // never serialized
VerifyExpiry *time.Time `json:"-"` // never serialized
CreatedAt time.Time `json:"created_at"`
LastLoginAt *time.Time `json:"last_login_at,omitempty"`
}
User represents an authenticated user in the system.
type UserStore ¶
type UserStore struct {
// contains filtered or unexported fields
}
UserStore manages user credentials backed by SQLite. Falls back to in-memory store if no DB is provided.
func NewUserStore ¶
NewUserStore creates a user store. If db is nil, uses in-memory only.
func (*UserStore) Authenticate ¶
Authenticate validates email/password and returns the user.
func (*UserStore) ChangePassword ¶
ChangePassword updates a user's password.
func (*UserStore) CreateAPIKey ¶
CreateAPIKey generates a new API key for a user. Returns the full key (only shown once).
func (*UserStore) CreateUser ¶
CreateUser creates a new user with a hashed password.
func (*UserStore) DeleteAPIKey ¶
DeleteAPIKey revokes an API key.
func (*UserStore) DeleteUser ¶
DeleteUser permanently removes a user.
func (*UserStore) GetByEmail ¶
GetByEmail returns a user by email.
func (*UserStore) ListAPIKeys ¶
ListAPIKeys returns all API keys for a user.
func (*UserStore) ListAllAPIKeys ¶
ListAllAPIKeys returns all API keys across all users (for superadmin).
func (*UserStore) SetVerifyToken ¶
SetVerifyToken generates a 6-digit verification code for a user.
func (*UserStore) UpdateUser ¶
UpdateUser updates a user's display name, role, and active status.
func (*UserStore) ValidateAPIKey ¶
ValidateAPIKey checks an API key and returns the associated role.
func (*UserStore) VerifyEmail ¶
VerifyEmail checks the verification code and marks email as verified.