> ## Documentation Index
> Fetch the complete documentation index at: https://guide.codepure.com/llms.txt
> Use this file to discover all available pages before exploring further.

# JWT Secrets and Token Security

> Securing JWT signing secrets and token management

## Common Misconfiguration

Weak or exposed JWT secrets allow attackers to forge tokens, bypass authentication, and gain unauthorized access to systems.

### Vulnerable Example

```javascript theme={null}
// VULNERABLE - Hardcoded JWT secrets
const jwt = require('jsonwebtoken');

// Never hardcode JWT secrets!
const JWT_SECRET = 'my-super-secret-key'; // Weak secret
const REFRESH_SECRET = 'refresh-secret-123'; // Predictable

// Using HS256 with weak secret
function generateToken(userId) {
    return jwt.sign(
        { userId, role: 'admin' },
        JWT_SECRET,
        { expiresIn: '7d' } // Long expiration
    );
}

// No token validation
function verifyToken(token) {
    try {
        return jwt.verify(token, JWT_SECRET);
    } catch (err) {
        return null; // Silently fail
    }
}

// Storing sensitive data in JWT
function createInsecureToken(user) {
    return jwt.sign({
        id: user.id,
        email: user.email,
        password: user.password, // Never include passwords!
        creditCard: user.creditCard, // Never include sensitive data!
        ssn: user.ssn
    }, JWT_SECRET);
}
```

```python theme={null}
# VULNERABLE - Flask JWT implementation
from flask import Flask
from flask_jwt_extended import JWTManager, create_access_token
import datetime

app = Flask(__name__)

# Weak and hardcoded secrets
app.config['JWT_SECRET_KEY'] = 'super-secret' # Never hardcode!
app.config['JWT_ACCESS_TOKEN_EXPIRES'] = datetime.timedelta(days=365) # Too long!
app.config['JWT_ALGORITHM'] = 'HS256' # Consider RS256 for better security

jwt = JWTManager(app)

# No refresh token rotation
@app.route('/login', methods=['POST'])
def login():
    # ... authentication logic ...
    access_token = create_access_token(
        identity=user_id,
        additional_claims={'role': 'admin', 'permissions': ['all']} # Too much info
    )
    return {'token': access_token}

# Using none algorithm (dangerous!)
def decode_token_unsafe(token):
    import jwt
    return jwt.decode(token, options={"verify_signature": False}) # Never do this!
```

## Secure Example

```javascript theme={null}
// SECURE - Proper JWT implementation
const jwt = require('jsonwebtoken');
const crypto = require('crypto');
const fs = require('fs').promises;

class SecureJWTManager {
    constructor() {
        this.accessTokenSecret = null;  // For HS256 in dev
        this.refreshTokenSecret = null; // For HS256 in dev
        this.publicKey = null;
        this.privateKey = null;
        this.isAsymmetric = false;
        this.initialized = false;
    }

    async initialize() {
        // Use RS256 with key pairs for production
        if (process.env.NODE_ENV === 'production') {
            await this.loadRSAKeys();
            this.isAsymmetric = true;
        } else {
            // Use strong random secrets for development
            this.loadHMACSecrets();
            this.isAsymmetric = false;
        }
        
        this.initialized = true;
    }

    async loadRSAKeys() {
        // Load RSA key pair from secure location
        try {
            this.privateKey = await fs.readFile(process.env.JWT_PRIVATE_KEY_PATH, 'utf8');
            this.publicKey = await fs.readFile(process.env.JWT_PUBLIC_KEY_PATH, 'utf8');
        } catch (error) {
            console.error('Failed to load RSA keys:', error);
            throw new Error('JWT key configuration failed');
        }
    }

    loadHMACSecrets() {
        // Load from environment with validation
        const accessSecret = process.env.JWT_ACCESS_SECRET;
        const refreshSecret = process.env.JWT_REFRESH_SECRET;
        
        if (!accessSecret || accessSecret.length < 32) {
            throw new Error('JWT_ACCESS_SECRET must be at least 32 characters');
        }
        
        if (!refreshSecret || refreshSecret.length < 32) {
            throw new Error('JWT_REFRESH_SECRET must be at least 32 characters');
        }
        
        this.accessTokenSecret = accessSecret;
        this.refreshTokenSecret = refreshSecret;
    }

    generateTokenPair(userId, userInfo = {}) {
        // Minimal payload - don't include sensitive data
        const iat = Math.floor(Date.now() / 1000);
        const accessPayload = {
            sub: userId,
            type: 'access',
            iat: iat
        };
        
        const algorithm = this.isAsymmetric ? 'RS256' : 'HS256';
        
        // Access token - short lived
        const accessToken = this.signToken(accessPayload, {
            algorithm: algorithm,
            expiresIn: '15m', // Short expiration
            issuer: 'api.example.com',
            audience: 'app.example.com'
        });
        
        // Refresh token - longer lived but still limited
        const refreshPayload = {
            sub: userId,
            type: 'refresh',
            iat: iat,
            family: crypto.randomBytes(16).toString('hex') // Token family for rotation
        };
        
        const refreshToken = this.signToken(refreshPayload, {
            algorithm: algorithm, // Use the same strong algorithm
            expiresIn: '7d',
            issuer: 'api.example.com'
        });
        
        return {
            accessToken,
            refreshToken,
            expiresIn: 900 // 15 minutes in seconds
        };
    }

    signToken(payload, options) {
        let secret;
        if (this.isAsymmetric) {
            secret = this.privateKey;
        } else {
            secret = (payload.type === 'refresh' ? this.refreshTokenSecret : this.accessTokenSecret);
        }
        
        return jwt.sign(payload, secret, options);
    }

    verifyToken(token, isRefreshToken = false) {
        try {
            let secret, algorithms;
            
            if (this.isAsymmetric) {
                secret = this.publicKey;
                algorithms = ['RS256'];
            } else {
                secret = isRefreshToken ? this.refreshTokenSecret : this.accessTokenSecret;
                algorithms = ['HS256'];
            }
            
            const decoded = jwt.verify(token, secret, {
                algorithms: algorithms,
                issuer: 'api.example.com',
                audience: isRefreshToken ? undefined : 'app.example.com', // Audience may not apply to refresh
                clockTolerance: 30 // 30 seconds clock skew tolerance
            });
            
            // Additional validation
            const expectedType = isRefreshToken ? 'refresh' : 'access';
            if (decoded.type !== expectedType) {
                throw new Error('Invalid token type');
            }
            
            return decoded;
        } catch (error) {
            // Log for security monitoring
            console.error('Token verification failed:', error.message);
            throw error;
        }
    }
    
    async verifyRefreshTokenAndRotate(token) {
        try {
            const decoded = this.verifyToken(token, true);
            
            // Check if token family is valid (for rotation detection)
            // const isValidFamily = await this.checkTokenFamily(decoded.family);
            // if (!isValidFamily) {
            //     // Possible token reuse - revoke all tokens for this user
            //     await this.revokeUserTokens(decoded.sub);
            //     throw new Error('Token reuse detected');
            // }
            
            // Invalidate old token
            // await this.invalidateToken(token);

            // Generate new token pair
            return this.generateTokenPair(decoded.sub);
            
        } catch (error) {
            console.error('Refresh token verification failed:', error.message);
            throw error;
        }
    }

    // Generate cryptographically secure secret
    static generateSecret(length = 64) {
        return crypto.randomBytes(length).toString('base64');
    }
}

// Express middleware for JWT validation
const jwtMiddleware = (jwtManager) => {
    return async (req, res, next) => {
        const authHeader = req.headers.authorization;
        
        if (!authHeader || !authHeader.startsWith('Bearer ')) {
            return res.status(401).json({ error: 'No token provided' });
        }
        
        const token = authHeader.substring(7);
        
        try {
            const decoded = jwtManager.verifyToken(token, false);
            req.user = decoded;
            
            // Check if token is close to expiration
            const now = Math.floor(Date.now() / 1000);
            if (decoded.exp - now < 300) { // Less than 5 minutes
                res.setHeader('X-Token-Expiring-Soon', 'true');
            }
            
            next();
        } catch (error) {
            return res.status(401).json({ error: 'Invalid token' });
        }
    };
};
```

```yaml theme={null}
# SECURE - Kubernetes secret for JWT keys
apiVersion: v1
kind: Secret
metadata:
  name: jwt-keys
  namespace: production
type: Opaque
data:
  jwt-private-key: LS0tLS1CRUdJTi... # Base64 encoded RSA private key
  jwt-public-key: LS0tLS1CRUdJTi...  # Base64 encoded RSA public key
  # Fallback HMAC secrets (if needed, otherwise remove)
  jwt-access-secret: <base64-encoded-random-secret>
  jwt-refresh-secret: <base64-encoded-random-secret>
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: jwt-config
data:
  JWT_ALGORITHM: "RS256"
  JWT_ISSUER: "api.example.com"
  JWT_AUDIENCE: "app.example.com"
  JWT_ACCESS_TOKEN_EXPIRES: "15m"
  JWT_REFRESH_TOKEN_EXPIRES: "7d"
```

```bash theme={null}
# Generate RSA keys for JWT
# Generate private key
openssl genpkey -algorithm RSA -out jwt-private.pem -pkeyopt rsa_keygen_bits:4096

# Generate public key
openssl rsa -pubout -in jwt-private.pem -out jwt-public.pem

# Generate random secrets for dev (HS256)
openssl rand -base64 64 > jwt-access-secret.txt
openssl rand -base64 64 > jwt-refresh-secret.txt
```

## Detection Patterns

* JWT Token Format: `` `eyJ[A-Za-z0-9-_]+\.eyJ[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+` ``
* Algorithm "none" Vulnerability: `` `("alg"|"algorithm")\s*:\s*("none"|"None")` ``
* Common Weak Secret: `` `(secret|password)['"]?\s*[:=]\s*['"](secret|12345|admin|jwt|super-secret)['"]` ``
* Hardcoded Secret Variable: `` `(JWT_SECRET|SECRET_KEY)\s*[:=]\s*['"][^'"]{1,20}['"]` `` (Finds *short*, likely weak secrets)

## Prevention Best Practices

1. **Use Strong, Random Secrets:** A weak, guessable secret (like 'secret123') makes your token forgeable. Use a cryptographically secure random string (at least 256 bits / 32 characters) and load it from a secrets manager or environment variable.
2. **Prefer Asymmetric Algorithms (RS256):** `HS256` (symmetric) uses one secret to sign and verify. `RS256` (asymmetric) uses a *private key* to sign and a *public key* to verify. This is safer because you can share the public key with other services for verification without exposing the private signing key.
3. **Short Access Token Expiration:** Access tokens (which grant access) should be very short-lived (e.g., 5-15 minutes). This dramatically limits the window of opportunity if a token is stolen.
4. **Implement Token Refresh:** Use a separate, long-lived "refresh token" (e.g., 7 days) to get a new access token. This refresh token should be stored securely (as an `httpOnly` cookie) and ideally implement rotation (where using a refresh token invalidates it and issues a new one).
5. **Minimal, Non-Sensitive Payload:** A JWT is *signed* (tamper-proof) but *not* encrypted (it's Base64 encoded, which is reversible). Anyone can read its contents. Never put sensitive data like passwords, permissions, or PII in the payload. Use the `sub` (subject) claim to store the user ID and nothing more.
6. **Validate All Claims:** On the server, *always* verify the signature. Also, verify the `exp` (expiration), `iss` (issuer), and `aud` (audience) claims to ensure the token is not expired and was intended for your specific service.
7. **Implement Token Revocation:** JWTs are stateless, which means they are valid until they expire. For critical events (like a user logging out or changing their password), you need a way to "revoke" their token. This usually involves maintaining a "denylist" (e.g., in Redis) of token IDs that are no longer valid.
8. **Secure Client-Side Storage:** Do not store JWTs in `localStorage` on the client, as it's vulnerable to XSS attacks. Store them in secure, `httpOnly` cookies, which cannot be accessed by JavaScript.
9. **Monitor for Token Anomalies:** Log and alert on token verification failures, attempts to use expired tokens, or impossible-to-achieve token refreshes. This can indicate an attacker is trying to forge or replay tokens.
10. **Rotate Your Keys:** The secrets and private keys used to sign tokens should be rotated regularly (e.g., every 90 days). This limits the lifespan of any key that might have been leaked.
