Accepted
We need to implement a secure authentication system for our web application targeting ~100 users. While we have no specific compliance requirements, the system should follow security best practices and provide a good user experience. This aligns with typical early-stage startup needs.
Our authentication system combines secure password storage, robust session management, and JWT-based API authentication to create a comprehensive solution that provides both security and good UX.
- Using PBKDF2 with SHA-384 for password hashing
- 100,000 iterations for key stretching
- 128-bit (16 byte) random salt per NIST SP 800-132
- Additional integrity digest using SHA-384
- Version tracking for future algorithm upgrades
- Storage format:
$pbkdf2-shaXXX$v1$iterations$salt$hash$digest
- Session IDs generated using nanoid
- 21 characters (vs 36 for UUID)
- URL-safe by default
- Better distribution for database indexes
- ~121 bits of randomness (sufficient for session IDs)
- Efficient cookie storage
- Fast generation
- Server-side session storage in SQLite database
- Session limits per user (default: 3)
- IP address and user agent tracking
- Automatic cleanup of expired sessions
- Sliding expiration with 7-day default timeout
Access Token (15-minute lifetime):
{
"uid": 123,
"sid": "BBH5Yw1Xzv21nKQ6UvNRt",
"typ": "access",
"exp": 1704724382
}
Refresh Token (7-day lifetime):
{
"uid": 123,
"sid": "BBH5Yw1Xzv21nKQ6UvNRt",
"typ": "refresh",
"exp": 1705329182
}
- Short claim names reduce token size
- Standard
exp
claim maintained per JWT spec - Type-safe token validation in code
- Self-documenting type values
- HTTP-only cookies
- Secure flag enabled
- Strict same-site policy
- Host-only (no Domain attribute)
- Path restricted to "/"
sequenceDiagram
participant U as User Agent
participant B as Backend
participant DB as Database
U->>B: POST /api/login {email, password}
B->>DB: Verify credentials (PBKDF2)
B->>DB: Create session record
Note over B,DB: Store user_agent, IP, expiry
B->>B: Generate JWT tokens
Note over B: Link tokens to session_id
B->>U: Set HTTP-only cookies
Note over U: access_token (15min)<br/>refresh_token (7d)
U->>B: API Request with access_token
alt Valid Access Token
B->>DB: Verify session active
B->>U: API Response
else Expired Access Token
U->>B: Use refresh_token
B->>DB: Verify session still valid
B->>B: Generate new access_token
B->>U: Set new access_token cookie
end
stateDiagram-v2
[*] --> Login
Login --> ActiveSession: Create Session + Tokens
state ActiveSession {
[*] --> ValidAccess
ValidAccess --> ExpiredAccess: 15min
ExpiredAccess --> ValidAccess: Refresh
ExpiredAccess --> InvalidSession: Session Expired
}
ActiveSession --> [*]: Logout/Expire
InvalidSession --> [*]: Clear Cookies
- Constant-time comparisons for password verification
- NIST SP 800-63-3 compliant password requirements
- Protection against timing attacks
- Secure cookie attributes (httpOnly, secure, sameSite)
- Database-backed session validation
- JWT-Session linking for revocation capability
- Automatic session pruning
- Rate limiting ready
- Strong security following industry standards
- Future-proof with version tracking
- Clean separation of concerns
- Maintainable codebase
- Type-safe implementation
- Easy to upgrade security parameters
- Stateless API authentication with JWT
- Session tracking and revocation capability
- More complex than simple password hashing
- Additional database storage requirements
- Slightly higher computational overhead
- Need to manage both sessions and JWTs
- Additional database load for session validation
- Pros:
- Well-established and battle-tested
- Adaptive work factor
- Built-in salt generation
- Memory-hard function making hardware acceleration difficult
- Cons:
- Fixed output size (60 characters)
- Less flexible than PBKDF2
- Limited to 72 bytes of password data
- No version tracking built into format
- Pros:
- Winner of the Password Hashing Competition
- Memory-hard and highly resistant to GPU attacks
- Configurable memory, parallelism, and iterations
- Modern algorithm with strong security properties
- Cons:
- Relatively newer compared to PBKDF2 and Bcrypt
- Less widespread library support
- More complex implementation
- Higher system requirements for memory usage
We chose PBKDF2 because:
- Widespread support across languages and platforms
- FIPS-140 compliance if needed in future
- Simpler implementation while still meeting security requirements
- Flexibility in hash function selection
- Easy to adjust iterations as computational power increases
- Built-in support in many cryptographic libraries
- Pros:
- Truly stateless
- No database lookups needed
- Simple implementation
- Cons:
- No way to revoke tokens
- No session tracking
- Limited security controls
- Pros:
- Full control over sessions
- Easy to revoke
- Traditional and well-understood
- Cons:
- Database lookup on every request
- More complex scaling
- Cookie size limitations
We chose hybrid JWT+Sessions because:
- Combines benefits of both approaches
- Provides stateless API auth while maintaining control
- Enables session tracking and management
- Allows for token revocation
- Supports device management
- OWASP guidelines recommend 210,000 iterations
- Cloudflare limits us to 100,000 iterations
- Password format designed for upgradability
- Digest may be used to prevent tampering
- Session management considers scalability
- All security parameters are configurable
- Implementation follows NIST guidelines