Decoding JWTs: Understanding JSON Web Tokens

Decode JWT Explained Simply: Understanding JSON Web Tokens

JSON Web Tokens (JWTs) have become the standard for secure authentication in modern web applications. But what exactly are they, and how do you decode them? Let’s break this down in simple terms.

What are JSON Web Tokens?

JSON Web Token (JWT) could refer to two different things - the standard (set of rules) that define a way to securely transmit information between parties or the Base64 encoded token string itself which contains the information. In this article, we typically refer to the latter.

JWTs are commonly used for:

  • Authentication: Proving who you are to a service
  • Authorization: Determining what you can access
  • Information Exchange: Securely sharing data between systems

JWT Structure and Components

Every JWT consists of three parts separated by dots (.):

header.payload.signature

1. Header

The header contains metadata about the token:

{
  "alg": "HS256",
  "typ": "JWT"
}
  • alg: The signing algorithm used (HS256, RS256, etc.)
  • typ: The token type (always “JWT”)

2. Payload

The payload contains the actual data (called “claims”):

{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022,
  "exp": 1516242622
}
  • sub: Subject (usually user ID)
  • name: User’s name
  • iat: Issued at (timestamp)
  • exp: Expiration time

3. Signature

The signature ensures the token hasn’t been tampered with. It’s created by combining the header, payload, and a secret key.

Benefits and Use Cases of JWTs

Stateless Authentication: Unlike traditional sessions, JWTs don’t require server-side storage. The server can verify a token without looking up user information in a database.

Single Sign-On (SSO): JWTs enable users to log in once and access multiple services without re-authenticating.

API Authentication: Perfect for securing REST APIs and microservices.

Mobile Applications: Efficient for mobile app authentication due to their compact size.

JWT Decoding Explained

Manual Decoding Process

  1. Split the token into its three parts
  2. Decode the header and payload from Base64URL
  3. Verify the signature to ensure authenticity

Here’s a simple example in JavaScript. Note that JWTs use Base64URL encoding, which is different from the standard Base64 that atob() expects. We need to convert it first by replacing URL-safe characters.

function decodeJWT(token) {
  const parts = token.split('.');
  
  // Decode header and payload from Base64URL
  // First, we need to replace URL-safe characters ('-' and '_') with Base64 characters ('+' and '/')
  const base64UrlHeader = parts[0];
  const base64UrlPayload = parts[1];
  
  const base64Header = base64UrlHeader.replace(/-/g, '+').replace(/_/g, '/');
  const base64Payload = base64UrlPayload.replace(/-/g, '+').replace(/_/g, '/');
  
  const header = JSON.parse(atob(base64Header));
  const payload = JSON.parse(atob(base64Payload));
  
  return { header, payload };
}

// Example usage
const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";

const decoded = decodeJWT(token);
console.log(decoded.payload.name); // "John Doe"

Using JWT Libraries

Most programming languages have JWT libraries that handle decoding automatically:

JavaScript/Node.js:

const jwt = require('jsonwebtoken');

try {
  const decoded = jwt.verify(token, secretKey);
  console.log(decoded);
} catch (error) {
  console.log('Invalid token');
}

JWT Security Best Practices

1. Protect Sensitive Information

  • Never store passwords in JWT payloads
  • Avoid sensitive PII (Personally Identifiable Information) - JWTs are meant to be secure, not secret. The main goal is to ensure that a message going from point A to point B has not been tampered with, not to keep the information private.
  • Keep payloads small for better performance

2. Secure Key Management

  • Use strong secret keys (at least 256 bits)
  • Store keys securely (environment variables, secret management services)
  • Rotate keys regularly for enhanced security

3. Token Lifecycle Management

  • Set appropriate expiration times (typically 15 minutes to 1 hour for access tokens)
  • Implement token refresh for longer sessions
  • Revoke tokens when users log out or change passwords

4. Communication Security

  • Always use HTTPS for JWT transmission
  • Set secure and httpOnly flags for cookies containing JWTs
  • Implement CSRF protection for cookie-based storage

Common JWT Algorithms

HMAC (Symmetric)

  • HS256, HS384, HS512: Uses a shared secret key
  • Pros: Fast, simple to implement
  • Cons: Secret must be shared securely

RSA (Asymmetric)

  • RS256, RS384, RS512: Uses public/private key pairs
  • Pros: More secure, no shared secrets
  • Cons: Slower, more complex key management

ECDSA (Elliptic Curve)

  • ES256, ES384, ES512: Modern alternative to RSA
  • Pros: Smaller keys, faster than RSA
  • Cons: Less widely supported

Verifying JWT Signatures

Signature verification is crucial for security:

function verifyJWT(token, secretKey) {
  try {
    // This will throw an error if the signature is invalid
    const decoded = jwt.verify(token, secretKey);
    return { valid: true, payload: decoded };
  } catch (error) {
    return { valid: false, error: error.message };
  }
}

Online JWT Decoding Tools

There are online tools can help decode JWTs for debugging:

  • jwt.io: Popular JWT debugger with interactive interface

⚠️ Security Warning: Only use these tools with test tokens. Never decode production tokens containing sensitive information on third-party websites.

Advanced JWT Topics

JWT Claims and Standards

Registered Claims (standardized):

  • iss: Issuer
  • sub: Subject
  • aud: Audience
  • exp: Expiration time
  • nbf: Not before time
  • iat: Issued at time
  • jti: JWT ID

Public Claims: Custom claims that should be registered to avoid collisions.

Private Claims: Application-specific claims for internal use.

JWT Extensions

  • JWE (JSON Web Encryption): Encrypts JWT payloads
  • JWS (JSON Web Signature): Signs JWT payloads
  • JWT Profile for OAuth 2.0: Standardized JWT usage in OAuth flows

Troubleshooting Common JWT Issues

Expired Tokens

if (decoded.exp * 1000 < Date.now()) {
  // Token has expired
  return res.status(401).json({ error: 'Token expired' });
}

Invalid Signatures

try {
  const decoded = jwt.verify(token, secretKey);
} catch (error) {
  if (error.name === 'JsonWebTokenError') {
    // Invalid signature
    return res.status(401).json({ error: 'Invalid token' });
  }
}

Clock Skew Issues

const options = {
  clockTolerance: 30 // Allow 30 seconds of clock skew
};
const decoded = jwt.verify(token, secretKey, options);

Conclusion

JWTs provide a powerful, flexible way to handle authentication and authorization in modern web applications. Understanding how to decode and verify them is essential for developers working with secure systems.

Key takeaways:

  • JWTs are self-contained and don’t require server-side storage
  • Always verify signatures before trusting JWT contents
  • Follow security best practices for production use
  • Use appropriate algorithms for your security requirements
  • Implement proper token lifecycle management

If this article was helpful, tweet it!