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

manage-token-sessions

v1.0.10

Published

A flexible token session manager for handling access/refresh token pairs with automatic refresh and cross-domain support

Readme

Token Session Manager

A flexible token session manager for handling access/refresh token pairs with automatic refresh and cross-domain support. Inspired by Auth0's battle-tested approach to token management.

Features

  • 🔄 Automatic Token Refresh: Proactively refreshes tokens before expiration
  • 🏪 Flexible Storage: Support for localStorage, sessionStorage, cookies, and custom storage
  • 🌐 Cross-Domain Support: Cookie storage with subdomain support for multi-app authentication
  • 🔒 JWT Decoding: Extracts expiration times from JWT tokens without verification
  • 🔗 Cross-Tab Synchronization: Prevents concurrent refresh attempts across browser tabs
  • 👁️ Tab Focus Detection: Automatically checks auth state when tab gains focus
  • 🎣 Lifecycle Hooks: Callbacks for session events (started, refreshed, expired, errors)
  • 🧪 Well Tested: Comprehensive test suite with 100% coverage
  • 📦 TypeScript: Full TypeScript support with detailed type definitions
  • 🪶 Lightweight: Minimal dependencies, tree-shakeable

Installation

npm install manage-token-sessions
# or
yarn add manage-token-sessions
# or
pnpm add manage-token-sessions

Quick Start

import { TokenSessionManager, LocalStorageAdapter } from 'manage-token-sessions';

// Define your refresh function
const refreshTokens = async (refreshToken: string) => {
  const response = await fetch('/api/auth/refresh', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ refreshToken })
  });
  
  const data = await response.json();
  return {
    accessToken: data.accessToken,
    refreshToken: data.refreshToken
  };
};

// Create the session manager
const sessionManager = new TokenSessionManager({
  refreshTokenFn: refreshTokens,
  storage: new LocalStorageAdapter(),
  onSessionRefreshed: (session) => {
    console.log('Session refreshed!', session);
  },
  onSessionExpired: () => {
    console.log('Session expired, redirecting to login...');
    window.location.href = '/login';
  }
});

// Start a session after login
await sessionManager.startSession({
  accessToken: 'your-jwt-access-token',
  refreshToken: 'your-refresh-token'
});

// Get current access token (automatically refreshes if needed)
const accessToken = await sessionManager.getCurrentAccessToken();

// Use the token in API calls
fetch('/api/protected', {
  headers: {
    'Authorization': `Bearer ${accessToken}`
  }
});

Storage Options

LocalStorage (Default)

import { LocalStorageAdapter } from 'manage-token-sessions';

const sessionManager = new TokenSessionManager({
  refreshTokenFn: refreshTokens,
  storage: new LocalStorageAdapter()
});

SessionStorage

import { SessionStorageAdapter } from 'manage-token-sessions';

const sessionManager = new TokenSessionManager({
  refreshTokenFn: refreshTokens,
  storage: new SessionStorageAdapter()
});

Cookies (Cross-Domain Support)

import { CookieStorageAdapter } from 'manage-token-sessions';

// Basic cookie storage
const sessionManager = new TokenSessionManager({
  refreshTokenFn: refreshTokens,
  storage: new CookieStorageAdapter()
});

// Cross-subdomain cookie storage
const crossDomainSessionManager = new TokenSessionManager({
  refreshTokenFn: refreshTokens,
  storage: new CookieStorageAdapter({
    domain: '.example.com', // Works across app1.example.com, app2.example.com, etc.
    secure: true,
    sameSite: 'lax'
  })
});

Memory Storage (Testing)

import { MemoryStorageAdapter } from 'manage-token-sessions';

const sessionManager = new TokenSessionManager({
  refreshTokenFn: refreshTokens,
  storage: new MemoryStorageAdapter()
});

Custom Storage

import { TokenStorage } from 'manage-token-sessions';

class CustomStorageAdapter implements TokenStorage {
  async get(key: string): Promise<string | null> {
    // Your custom get implementation
    return null;
  }
  
  async set(key: string, value: string): Promise<void> {
    // Your custom set implementation
  }
  
  async remove(key: string): Promise<void> {
    // Your custom remove implementation
  }
}

const sessionManager = new TokenSessionManager({
  refreshTokenFn: refreshTokens,
  storage: new CustomStorageAdapter()
});

Configuration Options

const sessionManager = new TokenSessionManager({
  // Required: Function to refresh tokens
  refreshTokenFn: async (refreshToken) => ({ accessToken, refreshToken }),

  // Optional: Storage adapter (default: LocalStorageAdapter)
  storage: new LocalStorageAdapter(),

  // Optional: Storage key (default: '@token-sessions@')
  storageKey: 'my-app-session',

  // Optional: Refresh buffer in seconds (default: 60)
  // Tokens are refreshed this many seconds before expiry
  expiryBufferSeconds: 120,

  // Optional: Refresh check interval in milliseconds (default: 30000)
  refreshIntervalMs: 15000,

  // Optional: Cross-tab lock configuration
  lockOptions: {
    timeout: 5000,    // Lock timeout in milliseconds
    retries: 10,      // Number of retry attempts
    retryDelay: 100   // Delay between retries
  },

  // Optional: Tab focus authentication check (default: true)
  checkOnFocus: true,           // Check auth state when tab gains focus
  focusDebounce: 100,           // Debounce focus events in milliseconds

  // Optional: Lifecycle hooks
  onSessionStarted: (session) => console.log('Session started', session),
  onSessionRefreshed: (session) => console.log('Session refreshed', session),
  onSessionExpired: () => console.log('Session expired'),
  onSessionError: (error) => console.error('Session error', error),
  onRefreshError: (error) => console.error('Refresh error', error),
  onFocusCheck: (session) => console.log('Focus check completed', session)
});

API Reference

TokenSessionManager

Methods

  • startSession(tokens, metadata?): Start a new session
  • getCurrentAccessToken(): Get current access token (auto-refreshes if needed)
  • getCurrentSession(): Get current session data
  • refreshSession(): Manually refresh the session
  • checkAuthState(): Manually trigger a focus check and return current session
  • endSession(): End the current session
  • hasActiveSession(): Check if there's an active session
  • destroy(): Clean up resources

Events

  • onSessionStarted(session): Called when a session is started
  • onSessionRefreshed(session): Called when tokens are refreshed
  • onSessionExpired(): Called when session expires
  • onSessionError(error): Called on session errors
  • onRefreshError(error): Called on refresh errors

Cross-Tab Synchronization

The package automatically prevents concurrent token refresh attempts across multiple browser tabs using a lock mechanism (similar to Auth0's approach). This ensures that:

  • Only one tab refreshes tokens at a time
  • Other tabs wait for the refresh to complete
  • No duplicate refresh requests are made
  • Token consistency is maintained across all tabs
// Multiple tabs with the same session manager configuration
// will automatically coordinate refresh attempts

const sessionManager = new TokenSessionManager({
  refreshTokenFn: refreshTokens,
  storage: new LocalStorageAdapter(),
  // Lock configuration (optional)
  lockOptions: {
    timeout: 5000,  // Wait up to 5 seconds for lock
    retries: 10,    // Retry 10 times if lock fails
    retryDelay: 100 // Wait 100ms between retries
  }
});

// Tab 1: Triggers refresh
await sessionManager.refreshSession();

// Tab 2: Waits for Tab 1 to complete, then uses the new token
const token = await sessionManager.getCurrentAccessToken();

Tab Focus Authentication Check

The package automatically checks authentication state when a browser tab gains focus. This provides instant feedback when users switch between tabs and ensures the UI reflects the current session state immediately.

How it works:

  • Automatic Detection: When you switch to a tab, it immediately validates the current session
  • Instant Sync: No waiting for the periodic refresh interval (default 30 seconds)
  • Cross-Tab Login: If logged in from another tab, switching back shows the authenticated state instantly
  • Cross-Tab Logout: If logged out in another tab, switching back shows the logged-out state instantly
  • Session Validation: Checks if stored tokens are still valid and not expired
  • Clock Synchronization: Aligns refresh timers with tokens refreshed by other tabs

Configuration:

const sessionManager = new TokenSessionManager({
  refreshTokenFn: refreshTokens,

  // Tab focus options (all optional)
  checkOnFocus: true,           // Enable focus checking (default: true)
  focusDebounce: 100,           // Debounce rapid focus events (default: 100ms)

  // Focus check callback
  onFocusCheck: (session) => {
    console.log('Tab focused, session state:', session ? 'authenticated' : 'logged out');
  }
});

// Manual focus check (useful for testing)
const currentSession = await sessionManager.checkAuthState();

Example Scenarios:

// Scenario 1: Login in another tab
// Tab A: User is logged out
// Tab B: User logs in successfully
// Tab A: User switches back → onSessionStarted() fires → UI updates to authenticated

// Scenario 2: Logout in another tab
// Tab A: User is logged in
// Tab B: User logs out
// Tab A: User switches back → onSessionExpired() fires → UI updates to logged out

// Scenario 3: Token refresh in another tab
// Tab A: Token expires in 30 seconds
// Tab B: Token gets refreshed → now expires in 1 hour
// Tab A: User switches back → Timer synchronizes to 1 hour expiry

Benefits:

Instant UI Updates: No delay when switching between tabs ✅ Better UX: Immediate feedback on authentication state changes ✅ Bidirectional Sync: Detects both login and logout from other tabs ✅ Security: Quick detection of session changes from other tabs ✅ Clock Synchronization: Aligns with token refreshes from other tabs ✅ Efficiency: Only checks when user actually focuses the tab

Cross-Domain Authentication

For applications spanning multiple subdomains, use cookie storage with a shared domain:

// On auth.example.com (login page)
const authSessionManager = new TokenSessionManager({
  refreshTokenFn: refreshTokens,
  storage: new CookieStorageAdapter({
    domain: '.example.com',
    secure: true,
    sameSite: 'lax'
  })
});

// After successful login
await authSessionManager.startSession(tokens);

// On app1.example.com and app2.example.com
const appSessionManager = new TokenSessionManager({
  refreshTokenFn: refreshTokens,
  storage: new CookieStorageAdapter({
    domain: '.example.com',
    secure: true,
    sameSite: 'lax'
  })
});

// Session is automatically available across subdomains
const accessToken = await appSessionManager.getCurrentAccessToken();

Error Handling

const sessionManager = new TokenSessionManager({
  refreshTokenFn: refreshTokens,
  onRefreshError: async (error) => {
    if (error.message.includes('invalid_grant')) {
      // Refresh token is invalid, redirect to login
      window.location.href = '/login';
    } else {
      // Network error, retry later
      console.error('Refresh failed, will retry:', error);
    }
  },
  onSessionError: (error) => {
    console.error('Session error:', error);
  }
});

License

MIT