---
title: Authentication Codes Must Expire Quickly
impact: MEDIUM
impactDescription: reduces the time window for attackers to brute-force or intercept one-time codes
tags: authentication, codes, expiry, otp, security, kotlin
---

## Authentication Codes Must Expire Quickly

One-time codes (OTP) used for password resets, 2FA, or login links should have a very short operational lifespan. The longer a code is valid, the more time an attacker has to social engineer the code from the victim or brute-force it.

**Incorrect (long-lived or non-expiring codes):**

```kotlin
// VULNERABLE: Code remains valid for 24 hours
val resetToken = UUID.randomUUID().toString()
db.save(ResetToken(userId, resetToken, createdAt = Instant.now()))

// In the verification logic:
fun verify(token: String) {
    val record = db.findByToken(token) ?: throw Exception("Invalid")
    // No check for time elapsed!
}
```

**Correct (short expiry with state management):**

```kotlin
import java.time.Duration
import java.time.Instant

private val OTP_EXPIRY = Duration.ofMinutes(5)

fun generateAndStoreOtp(userId: String) {
    val code = (100000..999999).random().toString()
    
    // Store in Redis with TTL (Time To Live)
    redis.set("otp:$userId", code, OTP_EXPIRY.toSeconds())
}

fun verifyOtp(userId: String, inputCode: String): Boolean {
    val storedCode = redis.get("otp:$userId")
    
    if (storedCode == null) {
        throw ExpiredCodeException("OTP has expired")
    }
    
    if (storedCode == inputCode) {
        // IMPORTANT: Invalidate the code immediately after successful use
        redis.del("otp:$userId")
        return true
    }
    
    // Increment failure counter for rate limiting...
    return false
}
```

**Recommended Expiry Guidelines:**
- **2FA / Login Codes:** 2 - 10 minutes.
- **Magic Login Links:** 15 - 30 minutes.
- **Email Verification:** 24 hours (security requirement is lower than active login).
- **Password Reset:** 15 - 60 minutes.

**Security Checklist:**
1.  **Single Use:** Delete the code immediately upon successful verification.
2.  **Rate Limiting:** Lock the account or the OTP flow after 3-5 failed attempts.
3.  **Secure Generation:** Use `SecureRandom` instead of `Random` for code generation.

**Tools:** Redis (with EXPIRE), Spring Security, Manual Audit
