npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@nekzus/tokenly

v1.5.4

Published

Secure JWT token management with advanced device fingerprinting

Readme

Tokenly 🔐

Github Workflow npm-version npm-month npm-total Donate

Advanced JWT Token Management with Device Fingerprinting

Enterprise-grade security by default for modern applications

Contents

✨ 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/tokenly

Required Dependencies

npm install cookie-parser

⚠️ Important: cookie-parser is 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:

  1. X-Real-IP: Direct proxy IP
  2. X-Forwarded-For: First IP in proxy chain
  3. Default IP (if provided)
  4. 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_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.

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-IP for single proxy setups
  • Handle X-Forwarded-For for proxy chains

🤝 Contributing

We welcome contributions! Please see our Contributing Guide for details.

📄 License

MIT © Nekzus