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/tokenlynpm install cookie-parser
β οΈ Important:cookie-parseris required for secure handling of refresh tokens with HttpOnly cookies.
import { getClientIP, Tokenly } 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_secretWhen 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-IPfor single proxy setups - Handle
X-Forwarded-Forfor proxy chains
We welcome contributions! Please see our Contributing Guide for details.
MIT Β© Nekzus