Logout - Aligheri/jwt-owasp-based-starter GitHub Wiki
Logout Implementation
Overview
This Spring Boot OWASP-based starter provides a secure logout mechanism that handles JWT token revocation, cookie deletion, and proper cleanup. The implementation follows OWASP security best practices for session management and token handling.
Key Features
- JWT Token Revocation: Securely revokes JWT tokens by storing their digest in a blacklist
- Cookie Management: Automatically deletes authentication cookies
- Token Encryption: Uses encrypted token storage with SHA-256 hashing
- Automatic Cleanup: Scheduled task to clean up revoked tokens every 30 days
- Error Handling: Comprehensive exception handling with proper logging
Architecture Components
1. Authentication Service Interface
V logout(String jwtToken, HttpServletResponse response, String cookieName);
2. Token Revoker
Handles the core token revocation logic:
- Calculates SHA-256 digest of decrypted tokens
- Stores revoked token digests in database
- Provides token revocation status checking
- Automatic cleanup scheduling
3. Revoked Token Repository
JPA repository for managing revoked tokens in the database.
4. Database Entity
RevokedToken
entity stores:
- Unique ID
- JWT token digest (SHA-256)
- Revocation timestamp
Implementation Guide
Step 1: Database Setup
Ensure your database has the revoked tokens table:
CREATE TABLE revoked_token (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
jwt_token_digest VARCHAR(255) NOT NULL,
revocation_date TIMESTAMP
);
Step 2: Controller Implementation
@PostMapping("/logout")
public ResponseEntity<DefaultRegisterResponse> logout(
@RequestHeader("Authorization") String authHeader,
HttpServletResponse response) {
String jwt = extractJwtFromHeader(authHeader);
if (jwt == null) {
return ResponseEntity.badRequest()
.body(new DefaultRegisterResponse("Authorization header is missing or invalid"));
}
try {
DefaultRegisterResponse result = authenticationService.logout(jwt, response, "fingerprint");
return ResponseEntity.ok(result);
} catch (IllegalArgumentException e) {
logger.error("Invalid token format during logout: {}", e.getMessage());
return ResponseEntity.badRequest()
.body(new DefaultRegisterResponse("Invalid token format"));
} catch (Exception e) {
logger.error("Logout error: {}", e.getMessage(), e);
return ResponseEntity.badRequest()
.body(new DefaultRegisterResponse("Logout failed: " + e.getMessage()));
}
}
private String extractJwtFromHeader(String authHeader) {
if (authHeader == null || authHeader.isEmpty()) {
return null;
}
return authHeader.startsWith("Bearer ") ? authHeader.substring(7) : authHeader;
}
Step 3: Service Configuration
Inject the required dependencies:
@Service
public class AuthenticationServiceImpl implements AuthenticationService {
private final TokenRevoker tokenRevoker;
private final CookieProvider cookieProvider;
private final Supplier<V> registerResponseSupplier;
// Implementation as shown in the provided code
}
Security Considerations
Token Digest Storage
- Only SHA-256 digests are stored, never the actual tokens
- Prevents token reconstruction even if database is compromised
- Uses Base64 encoding for consistent storage format
Cookie Security
- Automatic cookie deletion on logout
- Configurable cookie names for flexibility
- HttpOnly and Secure flags should be set on cookies
Error Handling
- Generic error messages to prevent information disclosure
- Detailed logging for debugging (server-side only)
- Graceful degradation on system failures
Configuration Options
Cleanup Schedule
The system automatically schedules cleanup every 30 days at 3 AM. To customize:
// Modify scheduleRevokedTokensDeletion() method
long initialDelay = calculateInitialDelay(); // Your custom logic
long periodMinutes = 30 * 24 * 60; // 30 days in minutes
Cookie Configuration
Configure cookie names and properties in your application properties:
app.security.cookie.name=fingerprint
app.security.cookie.secure=true
app.security.cookie.http-only=true
API Usage
Request Format
POST /logout
Authorization: Bearer <your-jwt-token>
Response Format
{
"message": "Logged out successfully!"
}
Error Responses
{
"message": "Invalid token format"
}
Testing
Unit Tests
Test the core components:
- TokenRevoker token revocation logic
- Cookie deletion functionality
- Error handling scenarios
Integration Tests
- Full logout flow testing
- Database interaction verification
- Token revocation persistence
Monitoring and Logging
Key Log Events
- Successful logouts:
INFO
level - Token processing errors:
WARN
level - System failures:
ERROR
level
Metrics to Monitor
- Logout success/failure rates
- Token revocation performance
- Database cleanup execution
Troubleshooting
Common Issues
-
"Invalid token format" errors
- Check Authorization header format
- Ensure Bearer prefix is included
- Verify token is properly Base64 encoded
-
Database connection issues
- Verify RevokedTokenRepository configuration
- Check database connectivity
- Ensure table schema matches entity
-
Cookie deletion failures
- Verify cookie name configuration
- Check domain/path settings
- Ensure response object is properly injected
Debug Mode
Enable debug logging for detailed troubleshooting:
logging.level.your.package.name=DEBUG
Migration Notes
When upgrading or migrating:
- Backup existing revoked tokens table
- Update database schema if needed
- Test token revocation functionality
- Verify cookie handling works correctly
- Monitor error logs during transition period