Advanced JWT Token Management with Device Fingerprinting
Enterprise-grade security by default for modern applications
- Features
- Installation
- Quick Start
- Configuration
- Security Features
- API Reference
- Environment Variables
- Best Practices
- Contributing
- License
- Zero Configuration Required: Works out of the box with secure defaults
- Device Fingerprinting: Unique identification of devices to prevent token theft
- Framework Agnostic: Use with Express, Fastify, Koa, or any Node.js framework
- TypeScript First: Full type safety and excellent IDE support
- Production Ready: Built for enterprise applications
npm install @nekzus/tokenly
npm install cookie-parser
β οΈ Important:cookie-parser
is required for secure handling of refresh tokens with HttpOnly cookies.
import { Tokenly, getClientIP } from '@nekzus/tokenly';
import cookieParser from 'cookie-parser';
import dotenv from 'dotenv';
// Load environment variables
dotenv.config();
// Initialize Express
const app = express();
// Required middleware for refresh tokens
app.use(cookieParser());
// Initialize Tokenly
const auth = new Tokenly({
accessTokenExpiry: '15m',
refreshTokenExpiry: '7d',
securityConfig: {
enableFingerprint: true,
maxDevices: 5
}
});
// Generate token with fingerprinting
app.post('/login', (req, res) => {
const token = auth.generateAccessToken(
{ userId: '123', role: 'user' },
undefined,
{
userAgent: req.headers['user-agent'] || '',
ip: getClientIP(req.headers)
}
);
res.json({ token });
});
const auth = new Tokenly({
accessTokenExpiry: '15m', // 15 minutes
refreshTokenExpiry: '7d', // 7 days
securityConfig: {
enableFingerprint: true, // Enable device tracking
maxDevices: 5 // Max devices per user
}
});
const auth = new Tokenly({
accessTokenExpiry: '5m', // Shorter token life
refreshTokenExpiry: '1d', // Daily refresh required
securityConfig: {
enableFingerprint: true, // Required for device tracking
enableBlacklist: true, // Enable token revocation
maxDevices: 3 // Strict device limit
}
});
- User Agent: Browser/client identification
- IP Address: Client's IP address
- Cryptographic Salt: Unique per instance
- Consistent Hashing: Same device = Same fingerprint
- Access Tokens: Short-lived JWTs for API access
- Refresh Tokens: Long-lived tokens for session maintenance
- Blacklisting: Optional token revocation support
- Expiration Control: Configurable token lifetimes
// Invalid Fingerprint Detection
auth.on('invalid_fingerprint', (event) => {
console.log(`Security Alert: Invalid fingerprint detected`);
console.log(`User: ${event.userId}`);
console.log(`IP: ${event.context.ip}`);
});
// Device Limit Reached
auth.on('max_devices_reached', (event) => {
console.log(`Device limit reached for user: ${event.userId}`);
console.log(`Current devices: ${event.context.currentDevices}`);
});
const token = auth.generateAccessToken(
payload: { userId: string; role: string },
options?: { fingerprint?: string; deviceId?: string },
context?: { userAgent: string; ip: string }
);
import { getClientIP } from '@nekzus/tokenly';
const clientIP = getClientIP(headers, defaultIP);
Priority order:
X-Real-IP
: Direct proxy IPX-Forwarded-For
: First IP in proxy chain- Default IP (if provided)
- Empty string (fallback)
interface AccessToken {
raw: string;
payload: {
userId: string;
role: string;
[key: string]: any;
};
}
interface InvalidFingerprintEvent {
type: 'invalid_fingerprint';
userId: string;
token: string;
context: {
expectedFingerprint: string;
receivedFingerprint: string;
ip: string;
userAgent: string;
timestamp: string;
};
}
interface MaxDevicesEvent {
type: 'max_devices_reached';
userId: string;
context: {
currentDevices: number;
maxAllowed: number;
ip: string;
userAgent: string;
timestamp: string;
};
}
# .env
JWT_SECRET_ACCESS=your_secure_access_token_secret
JWT_SECRET_REFRESH=your_secure_refresh_token_secret
When environment variables are not provided, Tokenly automatically:
- Generates cryptographically secure random secrets
- Uses SHA-256 for secret generation
- Implements secure entropy sources
- Creates unique secrets per instance
β οΈ Important: While auto-generated secrets are cryptographically secure, they regenerate on each application restart. This means all previously issued tokens will become invalid. For production environments, always provide permanent secrets through environment variables.
# Generate secure random secrets
node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"
- Never commit secrets to version control
- Use different secrets for development and production
- Minimum length of 32 characters recommended
- Rotate secrets periodically in production
- Use secret management services when available
- Use short-lived access tokens (5-15 minutes)
- Implement refresh token rotation
- Enable blacklisting for critical applications
- Use HttpOnly cookies for refresh tokens
- Configure cookie-parser middleware
- Enable secure and sameSite options in production
- Implement proper CORS configuration when needed
- Enable fingerprinting for sensitive applications
- Set reasonable device limits per user
- Monitor security events
- Configure proxy headers correctly
- Use
X-Real-IP
for single proxy setups - Handle
X-Forwarded-For
for proxy chains
We welcome contributions! Please see our Contributing Guide for details.
MIT Β© Nekzus