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.