Skip to main content

Custom JWT Token Validation

Learn how to implement JWT token validation without relying on third-party identity platforms.

Overview

For maximum control and flexibility, you can implement your own JWT token validation logic without using managed identity platforms like Auth0 or Okta.

When to Use Custom Implementation

Good Reasons

  • Existing identity system: You already have user authentication
  • Specific requirements: Need custom token claims or validation logic
  • Cost optimization: High volume makes managed platforms expensive
  • Regulatory compliance: Data residency or sovereignty requirements
  • Learning purposes: Educational projects or proof of concepts

Consider Managed Platforms If

  • You need user management UI
  • You want social login integration
  • You require enterprise SSO (SAML, etc.)
  • You prefer outsourcing security updates
  • You want quick time-to-market

Coming Soon

This guide is under development. Topics will include:

  • JWT structure and validation
  • JWKS endpoint implementation
  • Token generation and signing
  • Claims management
  • Refresh token handling
  • Token revocation strategies
  • Security best practices
  • Testing and validation

Architecture Overview

Your Application
├── Token Generation
│ ├── Sign with RSA private key
│ ├── Add standard claims (iss, sub, aud, exp)
│ └── Add custom claims
├── JWKS Endpoint
│ ├── Expose public keys
│ └── Handle key rotation
└── Token Validation
├── Verify signature with public key
├── Validate claims
└── Authorize based on claims

Basic Example

const jwt = require('jsonwebtoken');
const fs = require('fs');

// Generate tokens
const privateKey = fs.readFileSync('private-key.pem');

function generateToken(userId, scopes) {
return jwt.sign({
sub: userId,
iss: 'https://yourapp.com',
aud: 'https://api.yourapp.com',
scope: scopes.join(' '),
custom_claim: 'value'
}, privateKey, {
algorithm: 'RS256',
expiresIn: '1h',
keyid: 'key-1'
});
}

// Validate tokens
const publicKey = fs.readFileSync('public-key.pem');

function verifyToken(token) {
return jwt.verify(token, publicKey, {
issuer: 'https://yourapp.com',
audience: 'https://api.yourapp.com',
algorithms: ['RS256']
});
}

// Expose JWKS endpoint
app.get('/.well-known/jwks.json', (req, res) => {
res.json({
keys: [{
kty: 'RSA',
use: 'sig',
kid: 'key-1',
n: '...', // RSA modulus
e: 'AQAB'
}]
});
});

Key Management

Generating RSA Keys

# Generate private key
openssl genrsa -out private-key.pem 2048

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

# Extract modulus and exponent for JWKS
openssl rsa -in private-key.pem -noout -modulus

Key Rotation

Implement regular key rotation:

const activeKeys = new Map();
const retiredKeys = new Map();

function rotateKeys() {
// Move current key to retired
const currentKey = activeKeys.get('current');
if (currentKey) {
retiredKeys.set(currentKey.kid, currentKey);
}

// Generate and set new key
const newKey = generateNewKeyPair();
activeKeys.set('current', newKey);

// Remove old retired keys after grace period
cleanupOldKeys();
}

// Rotate keys monthly
setInterval(rotateKeys, 30 * 24 * 60 * 60 * 1000);

Resources

Contributing

Want to help complete this guide? Contribute on GitHub.