[TERMINAL · SKILLS]
> mounting /skills...
> indexing skill manifests...
> linking agents: claude · codex · gemini · cursor
> ready.
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░] 0%
Use Cases/Build a Session Management System

Build a Session Management System

Build a secure session management system with Redis-backed sessions, concurrent session limits, device tracking, forced logout, session activity monitoring, and suspicious login detection.

Development#typescript#tsconfig#type-checking#type-safety#javascript-migration
Works with:claude-codeopenai-codexgemini-clicursor

Skills stack · 5 skills

Avg quality 96/100·All SAFE
>

typescript

v1.0.0

Sets up and repairs TypeScript projects: picks tsconfig.json settings for a Node.js service, an npm library or a bundled web app, runs type-checking in CI, and replaces `any` and unsafe casts with types the compiler can verify. Use when someone asks to "set up TypeScript", "fix this tsconfig", "fix a TypeScript error", "upgrade to TypeScript 6 or 7", "migrate JavaScript to TypeScript", "publish types for a package", "run .ts files in Node" or "make this type-safe". Covers the changed defaults in TypeScript 6.0 and 7.0, module and moduleResolution, strict mode, project references, declaration output, narrowing, satisfies and common compiler errors.

93/100 quality
7.30× impact
SAFE· stale score
View skill
>

redis

v1.1.0

Redis is an in-memory data store used as a cache, session store, message broker and counter service. Use when adding a cache layer, storing sessions, rate limiting an API, running pub/sub or Streams with consumer groups, taking a distributed lock, building a leaderboard, or configuring Redis for production (persistence, maxmemory, eviction).

93/100 quality
1.79× impact
SAFE· stale score
View skill
>

postgresql

v1.1.0

Assists with designing schemas, writing performant queries, managing indexes, and operating PostgreSQL databases. Use when working with JSONB, full-text search, window functions, CTEs, row-level security, replication, or performance tuning. Trigger words: postgresql, postgres, sql, database, jsonb, rls, window functions, cte.

100/100 quality
5.15× impact
SAFE· stale score
View skill
>

hono

v1.1.0

Hono is a small web framework built on Web Standards (Request/Response) that runs unchanged on Cloudflare Workers, Deno, Bun, Node.js, AWS Lambda and Vercel. Use it when the user asks to build a Hono API, add Zod validation, JWT auth, CORS or middleware, use the type-safe RPC client (hc), render JSX, stream responses, or deploy one codebase to several runtimes.

100/100 quality
6.20× impact
SAFE· stale score
View skill
>

zod

v1.1.0

Zod is a TypeScript-first schema library that validates data at runtime and infers static types from the same schema. Use when validating API request bodies, form input, environment variables or config files, parsing untrusted JSON, or migrating from Zod 3 to Zod 4. Covers z.object, safeParse, z.infer, coercion, discriminated unions, refinements, JSON Schema output and the v4 API changes.

93/100 quality
3.87× impact
SAFE· stale score
View skill
$

The Problem

Petra leads security at a 30-person fintech. They use stateless JWTs for authentication — but JWTs can't be revoked. When an employee leaves, their token works for 24 hours until it expires. A compromised device means waiting for token expiry. There's no way to see how many active sessions a user has, force logout, or detect login from a new country. Compliance requires the ability to terminate all sessions instantly. They need server-side sessions with Redis, device tracking, concurrent limits, and instant revocation.

Step 1: Build the Session Manager

typescript
// src/auth/sessions.ts — Redis-backed session management with device tracking
import { randomBytes, createHash } from "node:crypto";
import { pool } from "../db";
import { Redis } from "ioredis";

const redis = new Redis(process.env.REDIS_URL!);

const SESSION_TTL = 30 * 86400;      // 30 days
const MAX_SESSIONS_PER_USER = 5;
const IDLE_TIMEOUT = 4 * 3600;       // 4 hours idle = session expired

interface Session {
  id: string;
  userId: string;
  deviceId: string;
  deviceName: string;
  ip: string;
  userAgent: string;
  country: string;
  createdAt: number;
  lastActiveAt: number;
  expiresAt: number;
}

// Create a new session after login
export async function createSession(
  userId: string,
  metadata: { ip: string; userAgent: string; country?: string; deviceFingerprint?: string }
): Promise<{ sessionId: string; sessionToken: string }> {
  // Check concurrent session limit
  const activeSessions = await getActiveSessions(userId);

  if (activeSessions.length >= MAX_SESSIONS_PER_USER) {
    // Revoke oldest session
    const oldest = activeSessions.sort((a, b) => a.lastActiveAt - b.lastActiveAt)[0];
    await revokeSession(oldest.id, userId, "max_sessions_exceeded");
  }

  // Detect suspicious login
  await detectSuspiciousLogin(userId, metadata);

  // Generate session ID and token
  const sessionId = randomBytes(16).toString("hex");
  const sessionToken = randomBytes(32).toString("hex");
  const tokenHash = createHash("sha256").update(sessionToken).digest("hex");

  const now = Date.now();
  const session: Session = {
    id: sessionId,
    userId,
    deviceId: metadata.deviceFingerprint || createHash("md5").update(metadata.userAgent + metadata.ip).digest("hex").slice(0, 12),
    deviceName: parseDeviceName(metadata.userAgent),
    ip: metadata.ip,
    userAgent: metadata.userAgent,
    country: metadata.country || "unknown",
    createdAt: now,
    lastActiveAt: now,
    expiresAt: now + SESSION_TTL * 1000,
  };

  // Store session in Redis
  await redis.setex(`session:${tokenHash}`, SESSION_TTL, JSON.stringify(session));

  // Track user's active sessions
  await redis.sadd(`user:sessions:${userId}`, tokenHash);
  await redis.expire(`user:sessions:${userId}`, SESSION_TTL);

  // Log login event
  await pool.query(
    `INSERT INTO login_events (user_id, session_id, ip, user_agent, country, device_name, created_at)
     VALUES ($1, $2, $3, $4, $5, $6, NOW())`,
    [userId, sessionId, metadata.ip, metadata.userAgent, metadata.country, session.deviceName]
  );

  return { sessionId, sessionToken };
}

// Validate and refresh session on every request
export async function validateSession(sessionToken: string): Promise<Session | null> {
  const tokenHash = createHash("sha256").update(sessionToken).digest("hex");
  const data = await redis.get(`session:${tokenHash}`);
  if (!data) return null;

  const session: Session = JSON.parse(data);

  // Check expiry
  if (Date.now() > session.expiresAt) {
    await redis.del(`session:${tokenHash}`);
    await redis.srem(`user:sessions:${session.userId}`, tokenHash);
    return null;
  }

  // Check idle timeout
  if (Date.now() - session.lastActiveAt > IDLE_TIMEOUT * 1000) {
    await redis.del(`session:${tokenHash}`);
    await redis.srem(`user:sessions:${session.userId}`, tokenHash);
    return null;
  }

  // Refresh last active time
  session.lastActiveAt = Date.now();
  await redis.setex(`session:${tokenHash}`, SESSION_TTL, JSON.stringify(session));

  return session;
}

// Get all active sessions for a user
export async function getActiveSessions(userId: string): Promise<Session[]> {
  const tokenHashes = await redis.smembers(`user:sessions:${userId}`);
  const sessions: Session[] = [];

  for (const hash of tokenHashes) {
    const data = await redis.get(`session:${hash}`);
    if (data) {
      sessions.push(JSON.parse(data));
    } else {
      // Clean up stale reference
      await redis.srem(`user:sessions:${userId}`, hash);
    }
  }

  return sessions.sort((a, b) => b.lastActiveAt - a.lastActiveAt);
}

// Revoke a specific session
export async function revokeSession(sessionId: string, userId: string, reason: string): Promise<boolean> {
  const tokenHashes = await redis.smembers(`user:sessions:${userId}`);

  for (const hash of tokenHashes) {
    const data = await redis.get(`session:${hash}`);
    if (data) {
      const session: Session = JSON.parse(data);
      if (session.id === sessionId) {
        await redis.del(`session:${hash}`);
        await redis.srem(`user:sessions:${userId}`, hash);

        await pool.query(
          "INSERT INTO session_events (user_id, session_id, event, reason, created_at) VALUES ($1, $2, 'revoked', $3, NOW())",
          [userId, sessionId, reason]
        );

        return true;
      }
    }
  }

  return false;
}

// Revoke ALL sessions (emergency / password change / account compromise)
export async function revokeAllSessions(userId: string, reason: string, exceptToken?: string): Promise<number> {
  const tokenHashes = await redis.smembers(`user:sessions:${userId}`);
  let revoked = 0;

  const exceptHash = exceptToken ? createHash("sha256").update(exceptToken).digest("hex") : null;

  for (const hash of tokenHashes) {
    if (hash === exceptHash) continue;
    await redis.del(`session:${hash}`);
    await redis.srem(`user:sessions:${userId}`, hash);
    revoked++;
  }

  await pool.query(
    "INSERT INTO session_events (user_id, session_id, event, reason, created_at) VALUES ($1, 'all', 'revoked_all', $2, NOW())",
    [userId, reason]
  );

  return revoked;
}

// Detect suspicious login patterns
async function detectSuspiciousLogin(userId: string, metadata: { ip: string; country?: string }): Promise<void> {
  if (!metadata.country) return;

  // Get last known country
  const { rows: [lastLogin] } = await pool.query(
    "SELECT country FROM login_events WHERE user_id = $1 ORDER BY created_at DESC LIMIT 1",
    [userId]
  );

  if (lastLogin && lastLogin.country !== metadata.country && metadata.country !== "unknown") {
    // New country — flag for review
    await redis.rpush("security:alerts", JSON.stringify({
      type: "new_country_login",
      userId,
      previousCountry: lastLogin.country,
      newCountry: metadata.country,
      ip: metadata.ip,
      timestamp: new Date().toISOString(),
    }));

    // Notify user
    await redis.rpush("notification:queue", JSON.stringify({
      userId,
      type: "suspicious_login",
      data: {
        country: metadata.country,
        ip: metadata.ip,
        message: `New login from ${metadata.country}. If this wasn't you, change your password immediately.`,
      },
    }));
  }
}

function parseDeviceName(userAgent: string): string {
  if (userAgent.includes("iPhone")) return "iPhone";
  if (userAgent.includes("iPad")) return "iPad";
  if (userAgent.includes("Android")) return "Android";
  if (userAgent.includes("Mac")) return "Mac";
  if (userAgent.includes("Windows")) return "Windows PC";
  if (userAgent.includes("Linux")) return "Linux";
  return "Unknown Device";
}

Results

  • Instant session revocation — terminated employee's access killed in milliseconds; no waiting 24 hours for JWT expiry; compliance requirement met
  • Concurrent sessions limited to 5 — oldest session auto-revoked when a 6th device logs in; prevents credential sharing and unauthorized access
  • Suspicious login detection — login from a new country triggers user notification and security alert; account takeover attempts caught immediately
  • Device management UI — users see "iPhone (São Paulo, active 5 min ago), Windows PC (Berlin, active 2 days ago)"; they can revoke any device with one click
  • 4-hour idle timeout — unattended workstations automatically lose access; meets SOC 2 and PCI DSS session management requirements

Related use cases