NeXuS

Authentication Security

The Auth Service implements several security best practices for user authentication.

Password Security

bcrypt Hashing

Passwords are hashed using bcrypt with a cost factor of 12:

const hash = await bcrypt.hash(password, 12)

Cost factor 12 provides strong brute-force resistance while keeping login times reasonable (~250ms per hash).

Password Requirements

JWT Token Security

Access Tokens

Refresh Tokens

Token Rotation

Each refresh operation:

  1. Verifies the refresh token JWT signature
  2. Looks up the SHA-256 hash in the refresh_tokens table
  3. Deletes the old token hash
  4. Issues a new refresh token
  5. Stores the new hash

This means each refresh token can only be used once. If a token is replayed (stolen and used by an attacker), the legitimate user’s next refresh will fail, alerting them to the compromise.

const COOKIE_OPTS = {
  httpOnly: true,       // Not accessible to JavaScript (XSS protection)
  secure: true,         // Only sent over HTTPS
  sameSite: 'strict',   // Not sent in cross-site requests (CSRF protection)
  path: '/',
  maxAge: 7 * 24 * 60 * 60 * 1000,  // 7 days
}

Rate Limiting

Auth endpoints are rate-limited to prevent brute-force attacks:

const authLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 20,                   // 20 attempts per window
  message: { error: 'Too many attempts, try again later' }
})

This applies to all /auth/* routes including login, register, and refresh.

CORS Configuration

app.use(cors({
  origin: [
    'https://nexus.sebhosting.com',
    'http://localhost:3000'
  ],
  credentials: true,  // Allow cookies
}))

Only the NeXuS frontend and local development are allowed as origins.

Role-Based Access

Auto-Refresh

The frontend refreshes access tokens every 13 minutes (2 minutes before the 15-minute expiry). If the refresh fails, the user is automatically logged out.

Logout

On logout:

  1. All refresh tokens for the user are deleted from PostgreSQL
  2. The refresh token cookie is cleared
  3. Client-side state is reset