Security¶
FastSvelte ships sensible security defaults so you start on solid ground.
Passwords¶
Passwords are hashed with Argon2id (backend/app/util/hash_util.py), a modern memory-hard algorithm. Plaintext passwords are never stored.
Sessions¶
- Tokens are 256-bit random values (
secrets.token_urlsafe(32)). - Only a SHA-256 hash of the token is stored server-side — a database leak doesn't expose usable session tokens.
- The cookie is HttpOnly (JavaScript can't read it), Secure outside
dev, and SameSitestrictoutsidedev(laxin dev). - Logout invalidates the session server-side; expired sessions are pruned by the cron job.
See Authentication for the full model.
Access control¶
Precedence-based roles (readonly < member < org_admin < sys_admin) gate every route via min_role_required(...). All business data is organization-scoped, so tenants are isolated — see Multi-Tenancy.
OAuth CSRF protection¶
The Google OAuth flow signs a state parameter (JWT, FS_JWT_SECRET_KEY) and validates it on callback. See Google OAuth.
AI spend protection¶
AI usage is hard-capped by default: when an organization exhausts its allotment + credits, calls are blocked rather than silently billed. Overage requires turning on both an org setting and a system-level kill switch, neither enabled by default — see AI Usage & Credit Billing. This protects you from runaway model spend.
CORS & email verification¶
Allowed origins are configured per environment in backend/app/config/settings.py and tighten in production. Accounts must verify their email before they can log in.
Rate limiting¶
The abuse-prone public endpoints are rate limited per client IP, so one source can't hammer them:
| Endpoint | Limit |
|---|---|
POST /auth/login |
5 / 15 min |
POST /auth/signup, POST /auth/signup-org |
3 / hour |
POST /password/forgot |
3 / hour |
POST /auth/resend-verification |
10 / min |
The priority is the endpoints that send email to a user-supplied address (signup, forgot-password, resend-verification): left open they let an attacker burn your email spend and sender reputation regardless of how much traffic you have. Login is capped against credential stuffing. A breach returns the standard ErrorResponse with a Retry-After header.
To rate limit another route, add the dependency to it:
from fastapi import Depends
from app.util.rate_limit import rate_limit
@router.post("/expensive", dependencies=[Depends(rate_limit("10/minute"))])
Storage (read before scaling)¶
Counters live in process memory by default (FS_RATE_LIMIT_STORAGE_URI=async+memory://). The shipped container runs a single process, so this is correct for one instance. But each instance keeps its own counters, so if you run more than one instance (horizontal scaling / replicas behind a load balancer) the effective limit multiplies by the number of instances. For a real limit across instances, point it at a shared store:
Async Redis needs the coredis package (uv add coredis).
Behind a proxy¶
The limiter reads the client IP from X-Forwarded-For, which hosting platforms set for you. If you'd rather rate limit at the edge, a reverse proxy like nginx can do it too. See its limit_req docs.
Deliberately left out¶
Kept out to stay lean; add if your threat model calls for it:
- Per-account login limiting (keying login on the target email, not just the IP) defends a distributed attack against one account. Worth adding once you have accounts worth attacking.
- The AI endpoint is authenticated, so it's not a public abuse surface, and it's already hard-capped by the AI spend protection above. It gets no separate HTTP limit.
- Reset and verify token endpoints rely on high-entropy tokens rather than a request limit.
Not included (add as needed)¶
To stay lean, FastSvelte does not bundle security-header middleware (CSP/HSTS). Add it when your threat model calls for it, e.g. a middleware that sets security headers.
Production checklist¶
- Strong, unique
FS_JWT_SECRET_KEYandFS_CRON_SECRET. - Serve over HTTPS so
Securecookies take effect. - Strict
FS_CORS_ORIGINSfor production. - Stripe live keys + verified webhook secret (see Billing & Subscriptions).