1.5.4 • Published 6 months ago
@nekzus/tokenly v1.5.4
Tokenly 🔐
Advanced JWT Token Management with Device Fingerprinting
Enterprise-grade security by default for modern applications
Contents
- Features
- Installation
- Quick Start
- Configuration
- Security Features
- API Reference
- Environment Variables
- Best Practices
- Contributing
- License
✨ Features
- 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
📦 Installation
npm install @nekzus/tokenlyRequired Dependencies
npm install cookie-parser⚠️ Important:
cookie-parseris required for secure handling of refresh tokens with HttpOnly cookies.
🚀 Quick Start
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 });
});🔧 Configuration
Basic Configuration
const auth = new Tokenly({
accessTokenExpiry: "15m", // 15 minutes
refreshTokenExpiry: "7d", // 7 days
securityConfig: {
enableFingerprint: true, // Enable device tracking
maxDevices: 5, // Max devices per user
},
});Advanced Security Configuration
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
},
});🛡️ Security Features
Device Fingerprinting
- User Agent: Browser/client identification
- IP Address: Client's IP address
- Cryptographic Salt: Unique per instance
- Consistent Hashing: Same device = Same fingerprint
Token Management
- 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
Security Events
// 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}`);
});📘 API Reference
Token Generation
const token = auth.generateAccessToken(
payload: { userId: string; role: string },
options?: { fingerprint?: string; deviceId?: string },
context?: { userAgent: string; ip: string }
);IP Detection Helper
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)
Type Definitions
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;
};
}🔑 Environment Variables & Secrets
Required Variables
# .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.
Secret Generation
# Generate secure random secrets
node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"Security Guidelines
- 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
🔐 Best Practices
Token Security
- Use short-lived access tokens (5-15 minutes)
- Implement refresh token rotation
- Enable blacklisting for critical applications
Refresh Token Security
- Use HttpOnly cookies for refresh tokens
- Configure cookie-parser middleware
- Enable secure and sameSite options in production
- Implement proper CORS configuration when needed
Device Management
- Enable fingerprinting for sensitive applications
- Set reasonable device limits per user
- Monitor security events
IP Detection
- Configure proxy headers correctly
- Use
X-Real-IPfor single proxy setups - Handle
X-Forwarded-Forfor proxy chains
🤝 Contributing
We welcome contributions! Please see our Contributing Guide for details.
📄 License
MIT © Nekzus