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 🙏

© 2025 – Pkg Stats / Ryan Hefner

anti-session-hijack

v1.0.7

Published

Client SDK for session hijacking prevention

Readme

Anti Session Hijack

A robust server-side SDK for preventing session hijacking by binding user sessions to a unique browser fingerprint. This package ensures that stolen session tokens cannot be reused from different browsers or devices.

Features

  • Session Hijacking Prevention - Binds sessions to browser fingerprints
  • Automatic Detection - Detects and responds to hijacking attempts
  • JWT-based Authentication - Secure token-based sessions
  • UUID Support - Uses UUIDs instead of sequential IDs for better security
  • Database Agnostic - Works with any PostgreSQL-compatible database

Installation

npm i anti-session-hijack

Peer Dependencies

npm install @neondatabase/serverless bcryptjs jose

Database Setup

  1. Create Required Tables Run these SQL commands in your PostgreSQL database:
-- Users table
CREATE TABLE IF NOT EXISTS users (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    name VARCHAR(255) NOT NULL,
    email VARCHAR(255) UNIQUE NOT NULL,
    password_hash TEXT NOT NULL,
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

-- Sessions table
CREATE TABLE IF NOT EXISTS sessions (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    auth_token_hash VARCHAR(255) NOT NULL,
    fingerprint VARCHAR(255) NOT NULL,
    created_at TIMESTAMP DEFAULT NOW(),
    UNIQUE(user_id, fingerprint)
);
  1. Environment Variables Create a .env file:
DATABASE_URL=your_postgres_connection_string
JWT_SECRET=your_secure_jwt_secret_key

Usage

Importing Functions

import { 
  signup, 
  login, 
  verifySession, 
} from 'anti-session-hijack';

API Reference

  1. signup(input, db) Creates a new user account.
Parameters:
  • input: SignupInput - User registration data
{
  name: string;      // User's full name
  email: string;     // User's email (must be unique)
  password: string;  // User's password (min 6 characters)
}
  • db: any - Database connection instance (e.g., NeonDB connection)
Returns: Promise
{
  id: string;          // UUID of the created user
  name: string;        // User's name
  email: string;       // User's email
  created_at: string;  // Creation timestamp
}
Example:
import { neon } from '@neondatabase/serverless';

const db = neon(process.env.DATABASE_URL!);

const newUser = await signup(
  {
    name: 'John Doe',
    email: '[email protected]',
    password: 'securepassword123'
  },
  db
);
  1. login(input, db, fingerprint, options) Authenticates a user and creates a session.
Parameters:
  • input: LoginInput - User credentials
{
  email: string;     // User's email
  password: string;  // User's password
}
  • db: any - Database connection instance
  • fingerprint: string - Browser fingerprint (see Fingerprint Generation section)
  • options: DBOptions - Configuration options
{
  jwtSecret: string;           // Secret key for JWT signing
  jwtExpiry?: string;          // Optional (default: '7d')
}
Returns: Promise
{
  token: string;       // JWT token (store in HttpOnly cookie)
  user: {
    id: string;        // User UUID
    name: string;      // User's name
    email: string;     // User's email
  }
}
Example:
// Client-side: Generate fingerprint
import FingerprintJS from '@fingerprintjs/fingerprintjs';

const fp = await FingerprintJS.load();
const result = await fp.get();
const fingerprint = result.visitorId;

// Server-side: Login
const loginResult = await login(
  {
    email: '[email protected]',
    password: 'securepassword123'
  },
  db,
  fingerprint,
  {
    jwtSecret: process.env.JWT_SECRET!,
    jwtExpiry: '7d'
  }
);

// Set HttpOnly cookie
response.cookies.set('authToken', loginResult.token, {
  httpOnly: true,
  secure: process.env.NODE_ENV === 'production',
  sameSite: 'strict',
  maxAge: 7 * 24 * 60 * 60
});
  1. verifySession(input, db, fingerprint, options) Verifies a session's validity and detects hijacking attempts.
Parameters:
  • input: VerifyInput - Session verification data
{
  fingerprint: string;
  
}
  • db: any - Database connection instance
  • fingerprint: string - Current browser fingerprint
  • options: DBOptions - Configuration options
Returns: Promise
{
  valid: boolean;       // Whether session is valid
  hijacked?: boolean;   // Whether hijacking was detected
  user?: {              // User data (if valid)
    id: string;
    name: string;
    email: string;
  }
}
Example:
const authToken = request.cookies.get('authToken')?.value;

const verification = await verifySession(
  { authToken },
  db,
  fingerprint,
  {
    jwtSecret: process.env.JWT_SECRET!,
    jwtExpiry: '7d'
  }
);

if (!verification.valid) {
  if (verification.hijacked) {
    // Alert user and force logout
    console.error('Session hijacking detected!');
  }
  // Redirect to login
}

Fingerprint Generation

Option 1: Using FingerprintJS (Recommended)

npm install @fingerprintjs/fingerprintjs
// Client-side implementation
import FingerprintJS from '@fingerprintjs/fingerprintjs';

export async function getBrowserFingerprint(): Promise<string> {
  const fp = await FingerprintJS.load();
  const result = await fp.get();
  return result.visitorId;
}

// Send fingerprint with login/verification requests
const fingerprint = await getBrowserFingerprint();

Option 2: Custom Fingerprint Algorithm

Create your own fingerprint using browser properties:

Security Implementation

Next.js API Route Example app/api/auth/login/route.ts

import { NextRequest, NextResponse } from "next/server";
import { db } from "@/lib/db";
import { login } from "anti-session-hijack";

export async function POST(request: NextRequest) {
  try {
    const { email, password, fingerprint } = await request.json();

    if (!email || !password || !fingerprint) {
      return NextResponse.json(
        { error: "Email, password, and fingerprint are required" },
        { status: 400 }
      );
    }

    const result = await login(
      { email, password },
      db,
      fingerprint,
      {
        jwtSecret: process.env.JWT_SECRET!,
        jwtExpiry: "7d"
      }
    );

    const response = NextResponse.json({
      message: "Login successful",
      user: result.user
    });

    response.cookies.set("authToken", result.token, {
      httpOnly: true,
      secure: process.env.NODE_ENV === 'production',
      sameSite: 'strict',
      maxAge: 7 * 24 * 60 * 60,
      path: '/'
    });

    return response;

  } catch (error: any) {
    console.error("Login error:", error);
    return NextResponse.json(
      { error: error.message || "Internal server error" },
      { status: 500 }
    );
  }
}

app/api/verify-session/route.ts

import { NextRequest, NextResponse } from "next/server";
import { db } from "@/lib/db";
import { verifySession } from "anti-session-hijack";

export async function POST(request: NextRequest) {
  try {
    const authToken = request.cookies.get("authToken")?.value;
    const { fingerprint } = await request.json();

    if (!authToken || !fingerprint) {
      return NextResponse.json({ valid: false }, { status: 401 });
    }

    const result = await verifySession(
      { authToken },
      db,
      fingerprint,
      {
        jwtSecret: process.env.JWT_SECRET!,
        jwtExpiry: "7d"
      }
    );

    return NextResponse.json(result);

  } catch (error) {
    console.error("Verification error:", error);
    return NextResponse.json({ valid: false }, { status: 500 });
  }
}

Author

Ashin Sabu Mathew