arrow_backBack to Knowledge Base
Security·Sep 12, 2025·8 MIN READ

JWT Authentication Is Not Enough

Security practices every backend should implement: refresh token rotation, robust RBAC, session revocation, and audit logging.

jwtrbacsecuritynestjs

01.What JWTs Actually Give You

A JWT proves that a token was signed by your server. That's it. It does not mean the user is still authorized. It does not mean the session hasn't been revoked. It does not mean the user hasn't been compromised. Treating JWTs as sufficient authentication is one of the most common backend security mistakes.

02.The Revocation Problem

JWTs are stateless by design — once issued, they're valid until expiry. If a user logs out, changes their password, or gets compromised, you cannot invalidate their existing token without a blocklist. Store a Redis-backed token blocklist keyed by jti (JWT ID) with TTL matching the token expiry.

typescript
async revokeToken(jti: string, expiresAt: number) {
  const ttl = expiresAt - Math.floor(Date.now() / 1000);
  await this.redis.setex(`blocklist:${jti}`, ttl, '1');
}

async isRevoked(jti: string): Promise<boolean> {
  return !!(await this.redis.get(`blocklist:${jti}`));
}

03.Refresh Token Rotation

Short-lived access tokens (15 minutes) + long-lived refresh tokens (7 days) is the right model. On every refresh, issue a new refresh token and invalidate the old one. Detect token reuse — if an already-used refresh token is presented, it indicates a compromised session. Revoke the entire family immediately.

04.RBAC Beyond Simple Role Checks

Role-based access control should be permission-based, not role-based. A user has a role, a role has permissions. Check permissions, not roles. This gives you granular control without rewriting guards every time you add a feature.

  • Never check: user.role === 'admin'
  • Always check: user.permissions.includes('documents:read')
  • Store permission sets in Redis, refresh on login
  • Audit every permission check for sensitive resources

05.Audit Logging

Every sensitive action — login, logout, permission change, resource access — must be logged with timestamp, user ID, IP address, and action type. Store these in an append-only table or stream to CloudWatch. This is non-negotiable for any production system handling user data.