A lightweight Node.js library to store, validate, and invalidate JWTs in Redis using hashed tokens. It supports marking JWTs as valid or blacklisted, setting expiration, and uses the Redis SCAN
command for efficient key deletion.
- Hashing: Securely stores hashed JWT strings, preventing direct exposure of tokens if Redis is compromised.
-
Valid/Blacklisted Separation: Maintains two distinct sets of JWTs—
valid-jwt
andblacklisted-jwt
. - Expiration: Each stored JWT record can be assigned a TTL (Time-to-Liveredis-jwt-hash-store-docs.md).
- Efficient Key Deletion: Uses the Redis SCAN command to safely remove multiple keys without blocking Redis.
- Easy to Use: Simple, minimal API for storing, validating, and removing JWTs by hash.
npm install redis-jwt-hash-store
const RedisJWTHashStore = require('redis-jwt-hash-store');
const store = new RedisJWTHashStore({
redisOptions: {
socket: { host: '127.0.0.1', port: 6379 },
},
prefixValid: 'valid-jwt',
prefixBlacklist: 'blacklisted-jwt',
});
- redisOptions: (Object) Connection options for the node-redis client
- prefixValid: (String) Key prefix for valid JWTs. Defaults to "valid-jwt"
- prefixBlacklist: (String) Key prefix for blacklisted JWTs. Defaults to "blacklisted-jwt"
/**
* @param {string} keyName - Unique identifier for the user (e.g. userId).
* @param {string} rawJWT - The raw JWT string.
* @param {string} locale - Locale info, like IP or country.
* @param {number} expiration - TTL in seconds.
*/
await store.storeValidJWT('user123', 'eyJhbGci...', '127.0.0.1', 3600);
Stores a hashed version of the JWT in Redis under the valid-jwt prefix. The expiration parameter sets how long the record remains in Redis (e.g., 3600 seconds = 1 hour).
/**
* @param {string} keyName - Unique identifier for the user (e.g. userId).
* @param {string} rawJWT - The raw JWT string.
* @param {string} locale - Locale info, like IP or country.
* @param {number} expiration - TTL in seconds.
*/
await store.storeBlacklistedJWT('user123', 'eyJhbGci...', '127.0.0.1', 3600);
Stores a hashed version of the JWT in Redis under the blacklisted-jwt prefix. Useful for immediately invalidating a token after logout, password change, etc.
/**
* @param {string} keyName - The keyName used when the JWT was stored.
* @param {string} rawJWT - The raw JWT to validate.
*/
await store.validateJWT('user123', 'eyJhbGci...');
Checks if the hash of rawJWT:
- Exists in valid-jwt
- Does not exist in blacklisted-jwt
Throws an error if not valid or if blacklisted:
- "JWT is blacklisted"
- "JWT does not exist in valid list"
/**
* @param {string} prefix - Either `store.prefixValid` or `store.prefixBlacklist`.
* @param {string} keyName - The keyName used when storing the JWT.
* @param {string} rawJWT - The raw JWT to remove from Redis.
*/
await store.deleteRecord(store.prefixValid, 'user123', 'eyJhbGci...');
Removes one specific hashed JWT record in either valid-jwt or blacklisted-jwt.
/**
* @param {string} prefix - Either `store.prefixValid` or `store.prefixBlacklist`.
* @param {string} keyName - The keyName you want to remove.
*/
await store.deleteAllByKeyName(store.prefixValid, 'user123');
Removes all hashed JWT records for the specified keyName under valid-jwt or blacklisted-jwt. Uses Redis SCAN under the hood to avoid performance pitfalls of KEYS on large datasets.
The library uses SHA-256 (via Node's crypto module) to hash JWT strings. Storing only the hash instead of the raw JWT:
- Protects against attackers who might compromise Redis
- Ensures that no raw JWT tokens are stored in plain text
const RedisJWTHashStore = require('redis-jwt-hash-store');
(async () => {
try {
// 1. Create a new store instance
const store = new RedisJWTHashStore({
redisOptions: {
socket: { host: '127.0.0.1', port: 6379 },
},
prefixValid: 'valid-jwt',
prefixBlacklist: 'blacklisted-jwt',
});
// Sample data
const rawJWT = 'eyJhbGciOiJIUzI1NiIsInR5...'; // Some JWT
const userKey = 'user123';
const locale = '127.0.0.1';
const expirationInSeconds = 60; // 1 minute
// 2. Store in valid JWT
await store.storeValidJWT(userKey, rawJWT, locale, expirationInSeconds);
console.log('Stored JWT in valid-jwt.');
// 3. Validate -> should succeed
await store.validateJWT(userKey, rawJWT);
console.log('Validation successful.');
// 4. Blacklist the same JWT
await store.storeBlacklistedJWT(
userKey,
rawJWT,
locale,
expirationInSeconds
);
console.log('Blacklisted the JWT.');
// 5. Validate again -> should fail
try {
await store.validateJWT(userKey, rawJWT);
} catch (err) {
console.error('Expected error after blacklisting:', err.message);
}
// 6. Delete all valid-jwt keys for user123
await store.deleteAllByKeyName(store.prefixValid, userKey);
console.log(`Deleted all valid-jwt records for ${userKey}.`);
// 7. Disconnect
store.client.disconnect();
} catch (err) {
console.error('Error:', err);
}
})();