v0.7.0
Security
This release contains several security fixes across the core, the NestJS adapter and the Angular client. Upgrading to v0.7.0 is strongly recommended.
Upgrade checklist
Four things to check before you deploy. The first two fail quietly — nothing errors, the behaviour just degrades.
1. Configure trust proxy if you run behind a proxy or load balancer
The client IP now comes from the framework-resolved req.ip rather than a raw X-Forwarded-For header. Unconfigured, req.ip is the socket peer — so behind a proxy every request appears to come from the proxy's IP, and anything keyed on client IP pools all your users into one bucket: account lockout, adaptive-MFA risk scoring and IP-scoped sign-in blocks, geolocation, and audit records.
// Match your topology. Never use `true` — that trusts any forwarded header.
// 1 behind a single reverse proxy (Caddy, Nginx)
// 'uniquelocal' behind AWS ELB/ALB or another private-network LB
// '10.0.0.0/8' an explicit CIDR list
app.getHttpAdapter().getInstance().set('trust proxy', 1);
Leave it unset only when nothing proxies you — req.ip is then the socket peer, which is the safe default. See the NestJS quick start.
2. Set password.passwordReset.baseUrl if you use the shipped forgot-password route
That route now builds the reset link from server configuration only and ignores a baseUrl in the request body. If your frontend was supplying one, reset emails still carry the code but lose the clickable link until you configure this.
password: {
passwordReset: {
baseUrl: 'https://app.example.com/reset-password',
},
},
Need a dynamically computed link? Write your own controller and pass baseUrl to AuthService.forgotPassword() directly.
3. Express or Fastify with apiKeys.globalAllowlist: true
Routes marked apiKey: 'deny' — API-key management and the admin bundle — are now genuinely closed to API-key callers. A key that previously reached them returns 403. Use a session token for those routes. See the API keys guide.
4. Adaptive MFA with blockedSignIn.scope set to device or ip
Block records are stored under a new key, and existing blocks are not carried over. With a blockDuration they simply expire on their own; a permanent block needs re-issuing.
Breaking changes
reCAPTCHA now applies to the shipped signup and login routes. With recaptcha.enabled: true, those two routes require a valid recaptchaToken. If you mount the shipped route bundles, ship frontend token generation before upgrading, or signup and login answer RECAPTCHA_REQUIRED. Hand-written controllers using @RequireRecaptcha() are unaffected. See the reCAPTCHA guide.
retainVerification is admin-only. It moved to the admin DTO and is ignored on self-service profile updates. Changing an email or phone always resets its verified status.
Hook decorators no longer take priority. HookDecoratorOptions is no longer exported and the decorators take no argument. The option never affected execution order — order hooks by their position in the NAuthHooksModule.forFeature([...]) array.
// Before
@PreSignupHook({ priority: 1 })
export class MyHook implements IPreSignupHookProvider {}
// After
@PreSignupHook()
export class MyHook implements IPreSignupHookProvider {}
Log metadata is redacted by key name. Any metadata key ending token, secret, password, apikey, privatekey, authorization or cookie is now replaced with [REDACTED]. Adjust anything that parses those fields out of your logs.
MFA grace period
Auth responses now carry mfaGracePeriod whenever MFA setup is pending but not yet enforced, on challenge responses as well as success ones — so a signup response tells the frontend that setup is due and can drive an optional setup flow straight away. New mfa.grace.skipForSignup defers the setup challenge to the next login to keep onboarding frictionless.
This also fixes a bug that made the grace period ineffective: the login user projection omitted createdAt, which the grace period is calculated from, so it read as expired on every login and enforcement demanded setup immediately no matter what mfa.gracePeriod was set to. See the MFA guide.