Skip to content

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.

LayerCatchesDoes not catch
AuthGuard + JwtStrategyMissing, malformed, expired, unsigned-incorrectly tokensStolen-but-unexpired tokens, replay before expiry, key compromise
OAuth2ClientCSRF (state cookie), code-interception (PKCE), SSRF on token exchange (no redirects)Phishing the provider, malicious provider, callback URL takeover
hash_password + burn_verifyTiming-based account enumeration via loginSlow GPU farms cracking dumped hashes, weak passwords, password reuse
AbilityGuardMissing or malformed Ability (500), unauthenticated GraphQL/WS (403)Logic bugs in your AbilityFactory itself
Repo::scopedScope leakage on every read — handler cannot forget the filterCustom 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 shaperLeak of unexposed columns, field-restricted grantsA handler returning a hand-built serde_json::Value shape that does not match Model (500)
ThrottlerGuardBrute-force login, naïve enumeration scrapesDDoS at network or HTTP layer (use a CDN / edge throttler)

These are out of scope on purpose. They matter, they belong in your deployment, and they are not part of the framework’s surface.

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.

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.

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.

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.

For a production app:

  1. Bearer tokens with short TTL + refresh.
  2. EdDSA split between issuer and resource servers.
  3. Authz guards bound on every controller (negative-path tests catch the missing one).
  4. 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([...]).
  5. CDN / WAF in front for DDoS and bot defense.
  6. Real secret store for JWT_PRIVATE_KEY, OAuth secrets, DB credentials.
  7. TLS terminated at the edge or at the app (HttpConfig).
  8. Logging at the right level. nest_rs::authn and nest_rs::orm emit warn+ on denials and security events — pipe them to your SIEM, do not silence them.