CtrlK
BlogDocsLog inGet started
Tessl Logo

giuseppe-trisciuoglio/developer-kit

Comprehensive developer toolkit providing reusable skills for Java/Spring Boot, TypeScript/NestJS/React/Next.js, Python, PHP, AWS CloudFormation, AI/RAG, DevOps, and more.

89

Quality

89%

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Risky

Do not use without reviewing

Overview
Quality
Evals
Security
Files

troubleshooting.mdplugins/developer-kit-java/skills/spring-boot-security-jwt/references/

Troubleshooting JWT Security Issues

Common JWT Token Problems

1. Token Not Accepted - 401 Unauthorized

@Component
@Slf4j
public class JwtTroubleshootingService {

    public TokenDiagnostic diagnoseToken(String token) {
        TokenDiagnostic diagnostic = new TokenDiagnostic();

        try {
            // Check token format
            if (!isValidTokenFormat(token)) {
                diagnostic.addError("Invalid token format - should be header.payload.signature");
                return diagnostic;
            }

            // Decode without verification for structure check
            Jwt untrustedJwt = decodeUntrusted(token);
            diagnostic.setClaims(untrustedJwt.getClaims());

            // Check expiration
            if (untrustedJwt.getExpiresAt() != null &&
                Instant.now().isAfter(untrustedJwt.getExpiresAt())) {
                diagnostic.addError(String.format(
                    "Token expired at %s (current time: %s)",
                    untrustedJwt.getExpiresAt(),
                    Instant.now()));
            }

            // Check not before
            if (untrustedJwt.getNotBefore() != null &&
                Instant.now().isBefore(untrustedJwt.getNotBefore())) {
                diagnostic.addError(String.format(
                    "Token not valid until %s (current time: %s)",
                    untrustedJwt.getNotBefore(),
                    Instant.now()));
            }

            // Try to verify with current keys
            try {
                Jwt trustedJwt = jwtDecoder.decode(token);
                diagnostic.setValid(true);
            } catch (JwtException e) {
                diagnostic.addError("Token verification failed: " + e.getMessage());

                // Check if it's a key rotation issue
                if (isKeyRotationIssue(e)) {
                    diagnostic.addSuggestion("Check if token was signed with a previous key");
                    diagnostic.addSuggestion("Verify key rotation configuration");
                }
            }

        } catch (Exception e) {
            diagnostic.addError("Unexpected error: " + e.getMessage());
        }

        return diagnostic;
    }

    private boolean isValidTokenFormat(String token) {
        String[] parts = token.split("\\.");
        return parts.length == 3;
    }

    private Jwt decodeUntrusted(String token) {
        // Decode without signature verification for diagnostics
        String[] parts = token.split("\\.");
        String payload = new String(Base64.getUrlDecoder().decode(parts[1]));

        // Parse claims
        Map<String, Object> claims = parseJson(payload);

        return Jwt.withTokenValue(token)
            .headers(headers -> headers.put("alg", "none"))
            .claims(claimsMap -> claimsMap.putAll(claims))
            .build();
    }
}

2. JWT Signature Verification Failed

@Service
@Slf4j
public class SignatureTroubleshootingService {

    public SignatureDiagnostic diagnoseSignatureIssue(String token, Exception e) {
        SignatureDiagnostic diagnostic = new SignatureDiagnostic();

        // Analyze the error message
        String errorMessage = e.getMessage().toLowerCase();

        if (errorMessage.contains("algorithm")) {
            diagnostic.setPossibleCause("Algorithm mismatch");
            diagnostic.addCheck("Check JWT header algorithm matches decoder configuration");
        }

        if (errorMessage.contains("key")) {
            diagnostic.setPossibleCause("Key mismatch");
            diagnostic.addCheck("Verify correct public key is configured");
            diagnostic.addCheck("Check if key rotation occurred");
        }

        if (errorMessage.contains("signature")) {
            diagnostic.setPossibleCause("Invalid signature");
            diagnostic.addCheck("Token may have been tampered with");
            diagnostic.addCheck("Check encoding issues");
        }

        // Extract algorithm from token
        try {
            String header = new String(Base64.getUrlDecoder().decode(token.split("\\.")[0]));
            Map<String, Object> headerMap = parseJson(header);
            String algorithm = (String) headerMap.get("alg");
            diagnostic.setTokenAlgorithm(algorithm);
        } catch (Exception ex) {
            diagnostic.addCheck("Unable to parse token header");
        }

        return diagnostic;
    }

    public List<String> getKeyDiagnostics() {
        List<String> diagnostics = new ArrayList<>();

        // Check current key
        try {
            RSAPublicKey currentKey = (RSAPublicKey) keyProvider.getCurrentPublicKey();
            diagnostics.add(String.format(
                "Current key: %d bits, Modulus: %s...",
                currentKey.getModulus().bitLength(),
                currentKey.getModulus().toString().substring(0, 20)
            ));
        } catch (Exception e) {
            diagnostics.add("Error accessing current key: " + e.getMessage());
        }

        // Check key rotation status
        if (keyRotationService.isRotationInProgress()) {
            diagnostics.add("Key rotation in progress - multiple keys may be valid");
        }

        // Check key expiration
        Instant keyExpiration = keyRotationService.getCurrentKeyExpiration();
        if (keyExpiration != null) {
            long daysUntilExpiration = Duration.between(Instant.now(), keyExpiration).toDays();
            if (daysUntilExpiration < 7) {
                diagnostics.add(String.format(
                    "WARNING: Key expires in %d days", daysUntilExpiration));
            }
        }

        return diagnostics;
    }
}

3. Performance Issues with JWT Validation

@Component
@Slf4j
public class JwtPerformanceTroubleshooter {

    public PerformanceDiagnostic analyzeJwtPerformance() {
        PerformanceDiagnostic diagnostic = new PerformanceDiagnostic();

        // Check cache hit rates
        CacheStats tokenCacheStats = tokenCache.stats();
        diagnostic.setTokenCacheHitRate(tokenCacheStats.hitRate());
        diagnostic.setTokenCacheSize(tokenCache.estimatedSize());

        if (tokenCacheStats.hitRate() < 0.8) {
            diagnostic.addIssue("Low token cache hit rate");
            diagnostic.addRecommendation("Consider increasing cache size");
            diagnostic.addRecommendation("Check cache TTL configuration");
        }

        // Check database connection pool
        HikariPoolMXBean poolProxy = dataSource.getHikariPoolMXBean();
        diagnostic.setActiveConnections(poolProxy.getActiveConnections());
        diagnostic.setIdleConnections(poolProxy.getIdleConnections());
        diagnostic.setTotalConnections(poolProxy.getTotalConnections());

        if (poolProxy.getActiveConnections() > poolProxy.getMaximumPoolSize() * 0.8) {
            diagnostic.addIssue("High database connection usage");
            diagnostic.addRecommendation("Consider increasing connection pool size");
            diagnostic.addRecommendation("Check for connection leaks");
        }

        // Check JWT processing time
        double avgProcessingTime = metricsService.getAverageJwtProcessingTime();
        diagnostic.setAverageProcessingTime(avgProcessingTime);

        if (avgProcessingTime > 50) { // ms
            diagnostic.addIssue("High JWT processing time");
            diagnostic.addRecommendation("Check key size (larger keys are slower)");
            diagnostic.addRecommendation("Consider caching decoded tokens");
        }

        return diagnostic;
    }

    @Scheduled(fixedRate = 60000) // Every minute
    public void monitorPerformance() {
        PerformanceDiagnostic diagnostic = analyzeJwtPerformance();

        if (diagnostic.hasIssues()) {
            log.warn("JWT Performance Issues Detected: {}", diagnostic.getIssues());

            // Send alert if critical
            if (diagnostic.isCritical()) {
                alertService.sendPerformanceAlert(diagnostic);
            }
        }
    }
}

Authentication Flow Debugging

Authentication Debug Filter

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class AuthenticationDebugFilter implements Filter {

    private static final Logger debugLog = LoggerFactory.getLogger("auth.debug");

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                        FilterChain chain) throws IOException, ServletException {

        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        if (isDebugEnabled(httpRequest)) {
            debugLog.info("=== Authentication Debug ===");
            debugLog.info("Request: {} {}", httpRequest.getMethod(), httpRequest.getRequestURI());
            debugLog.info("Remote IP: {}", getClientIpAddress(httpRequest));
            debugLog.info("User-Agent: {}", httpRequest.getHeader("User-Agent"));
            debugLog.info("Authorization Header: {}",
                maskAuthorizationHeader(httpRequest.getHeader("Authorization")));

            // Debug JWT if present
            String authHeader = httpRequest.getHeader("Authorization");
            if (authHeader != null && authHeader.startsWith("Bearer ")) {
                debugJwtToken(authHeader.substring(7));
            }

            // Capture timing
            long startTime = System.currentTimeMillis();

            try {
                chain.doFilter(request, response);
            } finally {
                long duration = System.currentTimeMillis() - startTime;
                debugLog.info("Response: {} ({}ms)", httpResponse.getStatus(), duration);
                debugLog.info("=== End Authentication Debug ===");
            }
        } else {
            chain.doFilter(request, response);
        }
    }

    private void debugJwtToken(String token) {
        try {
            // Decode without verification
            String[] parts = token.split("\\.");
            if (parts.length == 3) {
                String header = new String(Base64.getUrlDecoder().decode(parts[0]));
                String payload = new String(Base64.getUrlDecoder().decode(parts[1]));

                debugLog.info("JWT Header: {}", header);
                debugLog.info("JWT Payload: {}", payload);

                // Check expiration
                Map<String, Object> claims = parseJson(payload);
                if (claims.containsKey("exp")) {
                    Long exp = ((Number) claims.get("exp")).longValue();
                    Instant expiration = Instant.ofEpochSecond(exp);
                    debugLog.info("Token expires: {} (in {} minutes)",
                        expiration,
                        Duration.between(Instant.now(), expiration).toMinutes());
                }
            }
        } catch (Exception e) {
            debugLog.error("Failed to debug JWT token: {}", e.getMessage());
        }
    }

    private boolean isDebugEnabled(HttpServletRequest request) {
        // Enable debug based on header, parameter, or property
        return "true".equals(request.getHeader("X-Auth-Debug")) ||
               "true".equals(request.getParameter("debug")) ||
               authDebugEnabled;
    }

    private String maskAuthorizationHeader(String header) {
        if (header == null) return null;
        if (header.length() > 20) {
            return header.substring(0, 20) + "...";
        }
        return header;
    }
}

Authentication Flow State Tracker

@Component
public class AuthenticationFlowTracker {

    private final Map<String, FlowState> activeFlows = new ConcurrentHashMap<>();

    public void startFlow(String flowId, String type, Map<String, Object> context) {
        FlowState state = FlowState.builder()
            .flowId(flowId)
            .type(type)
            .startTime(Instant.now())
            .context(context)
            .steps(new ArrayList<>())
            .build();

        activeFlows.put(flowId, state);
        log.info("Started auth flow {}: {}", flowId, type);
    }

    public void addStep(String flowId, String step, Map<String, Object> data) {
        FlowState state = activeFlows.get(flowId);
        if (state != null) {
            FlowStep flowStep = FlowStep.builder()
                .step(step)
                .timestamp(Instant.now())
                .data(data)
                .build();

            state.getSteps().add(flowStep);
            log.debug("Added step to flow {}: {}", flowId, step);
        }
    }

    public void completeFlow(String flowId, boolean success, String error) {
        FlowState state = activeFlows.get(flowId);
        if (state != null) {
            state.setEndTime(Instant.now());
            state.setSuccess(success);
            state.setError(error);

            Duration duration = Duration.between(state.getStartTime(), state.getEndTime());
            log.info("Completed auth flow {} in {}ms - Success: {}",
                flowId, duration.toMillis(), success);

            // Log detailed flow
            if (log.isDebugEnabled()) {
                state.getSteps().forEach(step ->
                    log.debug("  Step: {} at {}", step.getStep(), step.getTimestamp()));
            }

            // Archive flow for analysis
            archiveFlow(state);
            activeFlows.remove(flowId);
        }
    }

    @Scheduled(fixedRate = 300000) // Every 5 minutes
    public void cleanupStaleFlows() {
        Instant cutoff = Instant.now().minus(5, ChronoUnit.MINUTES);

        activeFlows.entrySet().removeIf(entry -> {
            if (entry.getValue().getStartTime().isBefore(cutoff)) {
                log.warn("Cleaning up stale auth flow: {}", entry.getKey());
                return true;
            }
            return false;
        });
    }
}

Common Configuration Issues

Configuration Validator

@Component
public class JwtConfigurationValidator {

    public ConfigurationValidationResult validateConfiguration() {
        ConfigurationValidationResult result = new ConfigurationValidationResult();

        // Validate JWT settings
        validateJwtSettings(result);

        // Validate security settings
        validateSecuritySettings(result);

        // Validate integration settings
        validateIntegrationSettings(result);

        return result;
    }

    private void validateJwtSettings(ConfigurationValidationResult result) {
        // Check token expiration
        Duration accessTokenExpiration = jwtProperties.getAccessTokenExpiration();
        if (accessTokenExpiration.toMinutes() > 60) {
            result.addWarning("Access token expiration is very long (" +
                accessTokenExpiration.toMinutes() + " minutes)");
            result.addRecommendation("Consider reducing to 15-30 minutes");
        }

        // Check key configuration
        if (jwtProperties.getSecret() != null) {
            if (jwtProperties.getSecret().length() < 32) {
                result.addError("JWT secret is too short (minimum 32 characters)");
            }
            if ("changeit".equals(jwtProperties.getSecret()) ||
                "secret".equals(jwtProperties.getSecret())) {
                result.addError("Using default JWT secret - change immediately");
            }
        }

        // Check key store configuration
        if (jwtProperties.getKeyStore() != null) {
            Resource keyStore = jwtProperties.getKeyStore();
            if (!keyStore.exists()) {
                result.addError("JWT keystore file not found: " + keyStore);
            }
        }
    }

    private void validateSecuritySettings(ConfigurationValidationResult result) {
        // Check if HTTPS is enforced
        if (!securityProperties.isRequireSsl()) {
            result.addError("HTTPS is not required - tokens will be sent in clear text");
            result.addRecommendation("Set server.ssl.enabled=true");
        }

        // Check CORS configuration
        CorsConfiguration corsConfig = corsConfigurationSource.getCorsConfiguration(null);
        if (corsConfig != null) {
            if (corsConfig.getAllowedOrigins() != null &&
                corsConfig.getAllowedOrigins().contains("*")) {
                result.addWarning("CORS allows all origins - security risk");
            }
        }

        // Check session management
        if (securityProperties.getSessionTimeout() == null ||
            securityProperties.getSessionTimeout().toMinutes() > 60) {
            result.addWarning("Session timeout is not configured or too long");
        }
    }

    private void validateIntegrationSettings(ConfigurationValidationResult result) {
        // Check OAuth2 configuration
        if (oauth2Properties.getClientRegistration() != null) {
            oauth2Properties.getClientRegistration().forEach((clientId, registration) -> {
                if (registration.getClientSecret() == null ||
                    registration.getClientSecret().length() < 16) {
                    result.addWarning("OAuth2 client secret for " + clientId + " is weak");
                }
            });
        }

        // Check database configuration
        if (dataSource instanceof HikariDataSource) {
            HikariConfig config = ((HikariDataSource) dataSource).getHikariConfig();
            if (config.getMaximumPoolSize() < 10) {
                result.addWarning("Database connection pool is small (" +
                    config.getMaximumPoolSize() + ")");
            }
        }
    }
}

Quick Fix Scripts

#!/bin/bash
# JWT Quick Troubleshooting Script

echo "=== JWT Security Troubleshooting ==="
echo

# Check if jq is available
if ! command -v jq &> /dev/null; then
    echo "Installing jq for JSON processing..."
    if [[ "$OSTYPE" == "darwin"* ]]; then
        brew install jq
    else
        sudo apt-get install jq
    fi
fi

# Function to decode JWT
decode_jwt() {
    token=$1
    echo "Decoding JWT: ${token:0:20}..."

    # Decode header
    header=$(echo $token | cut -d. -f1)
    header_decoded=$(echo $header | base64 -d 2>/dev/null || echo $header | base64 -d 2>/dev/null || echo "Failed to decode")
    echo "Header: $header_decoded" | jq . 2>/dev/null || echo "Header: $header_decoded"

    # Decode payload
    payload=$(echo $token | cut -d. -f2)
    payload_decoded=$(echo $payload | base64 -d 2>/dev/null || echo "Failed to decode")
    echo "Payload: $payload_decoded" | jq . 2>/dev/null || echo "Payload: $payload_decoded"

    # Check expiration
    exp=$(echo $payload_decoded | jq -r '.exp' 2>/dev/null)
    if [[ "$exp" != "null" && "$exp" != "" ]]; then
        exp_date=$(date -d @$exp 2>/dev/null || date -r $exp 2>/dev/null)
        echo "Expires: $exp_date"

        if [[ $(date +%s) -gt $exp ]]; then
            echo "❌ TOKEN EXPIRED"
        else
            echo "✅ Token valid"
        fi
    fi
}

# Test token generation
test_token_generation() {
    echo "Testing token generation..."

    response=$(curl -s -X POST http://localhost:8080/api/auth/login \
        -H "Content-Type: application/json" \
        -d '{"email":"test@example.com","password":"password"}')

    if echo $response | jq -e '.accessToken' > /dev/null 2>&1; then
        token=$(echo $response | jq -r '.accessToken')
        echo "✅ Token generated successfully"
        decode_jwt $token
    else
        echo "❌ Failed to generate token"
        echo "Response: $response"
    fi
}

# Test token validation
test_token_validation() {
    echo
    echo "Testing token validation..."

    # Get a valid token first
    response=$(curl -s -X POST http://localhost:8080/api/auth/login \
        -H "Content-Type: application/json" \
        -d '{"email":"test@example.com","password":"password"}')

    token=$(echo $response | jq -r '.accessToken')

    if [[ -n "$token" && "$token" != "null" ]]; then
        echo "Testing with valid token..."
        status=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/api/users/me \
            -H "Authorization: Bearer $token")

        if [[ $status == "200" ]]; then
            echo "✅ Valid token accepted"
        else
            echo "❌ Valid token rejected (HTTP $status)"
        fi

        echo "Testing with invalid token..."
        status=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/api/users/me \
            -H "Authorization: Bearer invalid.token.here")

        if [[ $status == "401" ]]; then
            echo "✅ Invalid token rejected"
        else
            echo "❌ Invalid token accepted (HTTP $status)"
        fi
    else
        echo "❌ Could not get valid token for testing"
    fi
}

# Check security headers
check_security_headers() {
    echo
    echo "Checking security headers..."

    response=$(curl -s -I http://localhost:8080/api/public/health)

    if echo "$response" | grep -qi "strict-transport-security"; then
        echo "✅ HSTS header present"
    else
        echo "❌ HSTS header missing"
    fi

    if echo "$response" | grep -qi "x-content-type-options"; then
        echo "✅ X-Content-Type-Options header present"
    else
        echo "❌ X-Content-Type-Options header missing"
    fi

    if echo "$response" | grep -qi "x-frame-options"; then
        echo "✅ X-Frame-Options header present"
    else
        echo "❌ X-Frame-Options header missing"
    fi
}

# Main execution
test_token_generation
test_token_validation
check_security_headers

echo
echo "=== Troubleshooting Complete ==="
echo "If issues persist, check application logs for detailed error messages."

Debugging Checklist

Pre-Deployment Checklist

security-checklist:
  jwt-configuration:
    - [ ] JWT secret is at least 32 characters long
    - [ ] JWT secret is not using default value
    - [ ] Access token expiration is <= 30 minutes
    - [ ] Refresh token expiration is reasonable (7-30 days)
    - [ ] Key rotation is configured
    - [ ] HTTPS is enforced in production

  authentication:
    - [ ] Password encoding uses Argon2 or BCrypt
    - [ ] Brute force protection is enabled
    - [ ] Account lockout is configured
    - [ ] Password policy is enforced
    - [ ] Password history checking is enabled
    - [ ] MFA is available for sensitive operations

  authorization:
    - [ ] Role-based access control is implemented
    - [ ] Permission-based authorization is configured
    - [ ] Method-level security is properly used
    - [ ] Endpoint security rules are comprehensive
    - [ ] Admin endpoints are properly secured

  token-management:
    - [ ] Refresh tokens are securely stored
    - [ ] Token revocation is implemented
    - [ ] Token blacklisting is configured
    - [ ] Session management is configured
    - [ ] Concurrent session limits are set

  monitoring:
    - [ ] Security events are logged
    - [ ] Failed authentication attempts are tracked
    - [ ] Token validation metrics are collected
    - [ ] Performance monitoring is configured
    - [ ] Security health checks are implemented

  testing:
    - [ ] Unit tests cover security logic
    - [ ] Integration tests validate authentication flow
    - [ ] Security tests check common vulnerabilities
    - [ ] Performance tests validate token handling
    - [ ] Penetration testing is scheduled

  production-readiness:
    - [ ] SSL/TLS certificates are valid
    - [ ] Security headers are configured
    - [ ] CORS is properly configured
    - [ ] Rate limiting is implemented
    - [ ] Error messages don't leak information
    - [ ] Backup and recovery procedures exist

plugins

developer-kit-java

skills

README.md

CHANGELOG.md

context7.json

CONTRIBUTING.md

README_CN.md

README_ES.md

README_IT.md

README.md

tessl.json

tile.json