Threat model
The security layers nestrs ships cover a specific slice. They catch the bug classes that ship most often in business apps — forgotten filters, leaked fields, missing auth — and they catch them structurally, not by review. They do not cover everything a real production deployment must think about. This page lists what each layer covers, what it does not, and where the responsibility falls back on you.
What each layer catches
Section titled “What each layer catches”| Layer | Catches | Does not catch |
|---|---|---|
AuthGuard + JwtStrategy | Missing, malformed, expired, unsigned-incorrectly tokens | Stolen-but-unexpired tokens, replay before expiry, key compromise |
OAuth2Client | CSRF (state cookie), code-interception (PKCE), SSRF on token exchange (no redirects) | Phishing the provider, malicious provider, callback URL takeover |
hash_password + burn_verify | Timing-based account enumeration via login | Slow GPU farms cracking dumped hashes, weak passwords, password reuse |
AbilityGuard | Missing or malformed Ability (500), unauthenticated GraphQL/WS (403) | Logic bugs in your AbilityFactory itself |
Repo::scoped | Scope leakage on every read — handler cannot forget the filter | Custom queries that ignore Scope<E, A> |
Bind<S, A> | Cross-tenant by-id reads (403) and missing rows (404) | Handlers that fetch by id outside Bind and skip access |
Authorize shaper | Leak of unexposed columns, field-restricted grants | A handler returning a hand-built serde_json::Value shape that does not match Model (500) |
ThrottlerGuard | Brute-force login, naïve enumeration scrapes | DDoS at network or HTTP layer (use a CDN / edge throttler) |
What the framework does not do
Section titled “What the framework does not do”These are out of scope on purpose. They matter, they belong in your deployment, and they are not part of the framework’s surface.
Token theft and replay before expiry
Section titled “Token theft and replay before expiry”A stolen JWT remains valid until exp. The framework’s mitigation is
short access tokens (default 1 hour, configurable via
JwtOptions::expires_in) paired with a refresh-token endpoint your
app exposes. Long-lived sessions need rotation; the auth crate has
no built-in revocation list — if you need server-side revocation,
write it as a revoked_tokens table the Strategy consults on
verify.
The framework’s posture is bearer tokens, which are immune to
CSRF by construction (the browser does not auto-attach an
Authorization header). Cookie-based sessions are not supported as
a first-party credential. If you bridge to one (an SSR layer that
forwards cookies), add CSRF protection at the bridge — it is not
the framework’s job.
Key management
Section titled “Key management”EdDSA splits the issuer’s private key from every verifier’s public
key — see Issuer and resource server.
Rotation, HSM integration, and secret-store wiring are not the
framework’s job. Treat NESTRS_AUTHN__PRIVATE_KEY as you would
treat any production secret: load it from a real secret store, never
commit it, rotate on a schedule.
The HTTP transport ships TLS configuration via
HttpConfig. Configure it. The framework
will not refuse to serve over plaintext, so an unconfigured app is
plaintext by default.
Network-level DDoS
Section titled “Network-level DDoS”The throttler is a per-handler guard, not a network defense. A real deployment fronts the app with a CDN, WAF, or rate-limiter that sheds load before the request reaches the process.
Cryptographic primitives
Section titled “Cryptographic primitives”The framework wraps jsonwebtoken,
argon2,
and oauth2. The
cryptographic correctness comes from those crates. The framework’s
job is to wire them correctly — call hash_password instead of
storing plaintext, verify_password instead of comparing strings,
JwtService::verify instead of decoding manually.
Defense in depth — what to layer
Section titled “Defense in depth — what to layer”For a production app:
- Bearer tokens with short TTL + refresh.
- EdDSA split between issuer and resource servers.
- Authz guards bound on every controller (negative-path tests catch the missing one).
- Exposure is opt-in — no
#[expose]on server-only columns. A column crosses the wire only when its field carries#[expose], so a forgotten or newly-migrated column is hidden, not leaked. The masker is the second line for columns you do expose but restrict, covering them only when the field rule is.fields([...]). - CDN / WAF in front for DDoS and bot defense.
- Real secret store for
JWT_PRIVATE_KEY, OAuth secrets, DB credentials. - TLS terminated at the edge or at the app (
HttpConfig). - Logging at the right level.
nest_rs::authnandnest_rs::ormemitwarn+ on denials and security events — pipe them to your SIEM, do not silence them.
Going further
Section titled “Going further”- Security overview — the concept map.
- Negative-path tests — auth refused, cross-tenant denied, rollback verified.
- Throttler — per-handler rate limiting.