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

formreader-session-timeout

v0.2.6

Published

Session timeout microfrontend: configurable JWT expiry decoding and refresh with idle tracking

Readme

@formreader/session-timeout

A lightweight, fully configurable React microfrontend for JWT token refresh management, idle session tracking, and auto-logout with comprehensive event callbacks.

Features

  • JWT Expiry Decoding — Reads token expiry from JWT exp claim, no server roundtrip needed
  • Configurable Refresh Timing — Schedule token refresh at any point before expiry
  • Idle Detection — Automatic idle timeout with configurable warning dialogs
  • Custom Payload Formatting — Tailor refresh/logout request payloads to your API
  • Flexible HTTP Integration — Use your own axios/fetch client or rely on built-in fetch
  • Event-Driven — Full control via callbacks: onRefreshSuccess, onIdle, onSessionExpired, etc.
  • Zero External Dependencies — Works standalone; React is a peer dependency only
  • Fully Typed — TypeScript support out of the box
  • Singleton Pattern — Global session manager or per-component hooks

Installation

npm install @formreader/session-timeout

Or using yarn:

yarn add @formreader/session-timeout

Quick Start

1. Hook-Based Usage (Recommended)

Import and use the useSessionTimeout hook in any React component:

import React from 'react';
import { useSessionTimeout } from '@formreader/session-timeout';

export function MyComponent() {
  const {
    sessionState,
    timeUntilIdle,
    idleWarningVisible,
    extendSession,
    refreshToken,
    logout,
  } = useSessionTimeout({
    enabled: true,
    config: {
      refreshThresholdMs: 2 * 60 * 1000,        // Refresh 2 min before expiry
      idleTimeoutMs: 15 * 60 * 1000,            // Logout after 15 min idle
      idleCheckIntervalMs: 10 * 1000,           // Check idle every 10 sec
      idleWarningThresholdMs: 2 * 60 * 1000,    // Show warning 2 min before idle
      autoRefresh: true,
      showIdleWarning: true,
      debug: true,
      // Your API endpoints
      refreshEndpoint: '/api/auth/refresh/',
      logoutEndpoint: '/api/auth/logout/',
      // Custom payload format (if needed)
      refreshPayloadFormatter: (token) => ({ refresh: token }),
    },
    onRefreshSuccess: () => console.log('Token refreshed'),
    onIdle: () => console.log('User idle'),
    onSessionExpired: () => console.log('Session expired'),
  });

  return (
    <div>
      <p>Session: {sessionState.isActive ? '🟢 Active' : '🔴 Inactive'}</p>
      {idleWarningVisible && (
        <div className="warning">
          <p>Your session is about to expire in {timeUntilIdle}ms</p>
          <button onClick={extendSession}>Stay Logged In</button>
        </div>
      )}
    </div>
  );
}

2. Service-Based Usage

Use SessionManager directly for more control:

import {
  getSessionManager,
  resetSessionManager,
  getStoredToken,
  getTokenInfo,
} from '@formreader/session-timeout';

// Initialize on app startup
const manager = getSessionManager({
  refreshThresholdMs: 2 * 60 * 1000,
  idleTimeoutMs: 15 * 60 * 1000,
  autoRefresh: true,
  debug: true,
  refreshEndpoint: '/api/auth/refresh/',
  logoutEndpoint: '/api/auth/logout/',
  refreshPayloadFormatter: (token) => ({ refresh: token }),
});

manager.init();

// Listen to events
manager.on('tokenRefreshed', () => console.log('Token refreshed'));
manager.on('idle', () => console.log('User idle'));

// Manual refresh if needed
await manager.refreshToken();

// Cleanup on logout or unmount
manager.destroy();

Configuration

All settings are optional and inherit sensible defaults. Pass them via the config object:

Session Timing

| Option | Type | Default | Description | |--------|------|---------|-------------| | refreshThresholdMs | number | 2 * 60 * 1000 | Milliseconds before token expiry to trigger refresh | | idleTimeoutMs | number | 15 * 60 * 1000 | Milliseconds of inactivity before session ends | | idleCheckIntervalMs | number | 10 * 1000 | How often to check for idle activity | | idleWarningThresholdMs | number | 2 * 60 * 1000 | Milliseconds before idle timeout to show warning | | maxSessionDurationMs | number | 8 * 60 * 60 * 1000 | Absolute max session duration (8 hours) |

API Configuration

| Option | Type | Default | Description | |--------|------|---------|-------------| | refreshEndpoint | string | /auth/refresh/ | URL for token refresh | | logoutEndpoint | string | /auth/logout/ | URL for logout | | autoRefresh | boolean | true | Auto-refresh tokens before expiry |

UI Configuration

| Option | Type | Default | Description | |--------|------|---------|-------------| | showIdleWarning | boolean | true | Show idle warning dialog | | debug | boolean | false | Log debug messages to console |

Custom Integration

| Option | Type | Description | |--------|------|-------------| | httpClient? | { post(...): Promise } | Custom HTTP client (defaults to fetch) | | refreshPayloadFormatter? | (token) => object | Format refresh request payload | | logoutPayloadFormatter? | (token) => object | Format logout request payload |


Custom HTTP Client

If you have an existing axios instance or custom HTTP client, pass it to avoid dependencies:

import axios from 'axios';

const { useSessionTimeout } = require('@formreader/session-timeout');

useSessionTimeout({
  config: {
    httpClient: {
      post: (url, body, options) => axios.post(url, body, options),
    },
    refreshThresholdMs: 2 * 60 * 1000,
    // ... other config
  },
});

Custom Payload Formatting

Your API may expect a different payload structure. Use the formatters to customize:

Example 1: API requires refresh field

useSessionTimeout({
  config: {
    refreshPayloadFormatter: (token) => ({
      refresh: token, // API expects "refresh", not "token"
    }),
    logoutPayloadFormatter: (token) => ({
      refresh: token,
    }),
  },
});

Request sent:

POST /api/auth/refresh/
{ "refresh": "eyJhbGc..." }

Example 2: Complex nested structure

useSessionTimeout({
  config: {
    refreshPayloadFormatter: (token) => ({
      action: 'refresh',
      credentials: {
        refresh_token: token,
        client_id: 'my-client',
      },
    }),
  },
});

Request sent:

POST /api/auth/refresh/
{
  "action": "refresh",
  "credentials": {
    "refresh_token": "eyJhbGc...",
    "client_id": "my-client"
  }
}

Event Callbacks

Full control over session lifecycle:

useSessionTimeout({
  config: { /* ... */ },
  
  // Called when token is about to expire
  onSessionExpiring: () => {
    console.warn('Token expiring soon');
  },

  // Called when user becomes idle
  onIdle: () => {
    console.log('User idle – logging out');
  },

  // Called when session has completely expired
  onSessionExpired: () => {
    console.log('Session expired – redirecting to login');
    window.location.href = '/login';
  },

  // Called after successful token refresh
  onRefreshSuccess: () => {
    console.log('Token refreshed silently');
  },

  // Called on refresh failure
  onRefreshFailure: (error) => {
    console.error('Refresh failed:', error.message);
  },
});

Token Storage

Tokens are stored in sessionStorage by default (cleared on browser close). To persist across tabs, use localStorage:

import { storeToken, getStoredToken } from '@formreader/session-timeout';

// Store persistently
storeToken(token, true); // true = use localStorage

// Retrieve (checks both storage types)
const token = getStoredToken();

Token Inspection

Decode and inspect JWT payloads without verification (client-side only):

import { getTokenInfo, isTokenExpired, getTimeUntilExpiry } from '@formreader/session-timeout';

const token = getStoredToken();

// Get full token info
const info = getTokenInfo(token);
console.log(info.expiresAt);  // Unix timestamp (ms)
console.log(info.expiresIn);  // Milliseconds until expiry
console.log(info.payload);    // JWT payload object

// Quick checks
const expired = isTokenExpired(token);
const timeLeft = getTimeUntilExpiry(token);

Global Session Manager

For app-wide state, initialize the singleton once at startup:

import { getSessionManager, resetSessionManager } from '@formreader/session-timeout';

// App startup
const manager = getSessionManager({
  refreshThresholdMs: 2 * 60 * 1000,
  idleTimeoutMs: 15 * 60 * 1000,
  autoRefresh: true,
  refreshEndpoint: '/api/auth/refresh/',
  logoutEndpoint: '/api/auth/logout/',
});

manager.init();

// Listen to events
manager.on('tokenRefreshed', () => console.log('Token refreshed'));

// Later, reset if needed (e.g., on logout)
resetSessionManager();

TypeScript Support

Full TypeScript definitions included:

import {
  SessionConfig,
  SessionState,
  TokenInfo,
  JWTPayload,
  RefreshTokenResponse,
  UseSessionTimeoutOptions,
} from '@formreader/session-timeout';

const config: SessionConfig = {
  refreshThresholdMs: 2 * 60 * 1000,
  idleTimeoutMs: 15 * 60 * 1000,
  // ... rest of config
};

API Reference

useSessionTimeout(options?)

React hook for session management in components.

Parameters:

  • options.enabled?: boolean — Enable/disable the hook (default: true)
  • options.config?: Partial<SessionConfig> — Configuration (merged with defaults)
  • options.onSessionExpiring?: () => void — Callback before expiry
  • options.onSessionExpired?: () => void — Callback on expiry
  • options.onIdle?: () => void — Callback on idle
  • options.onRefreshSuccess?: () => void — Callback on refresh success
  • options.onRefreshFailure?: (error: Error) => void — Callback on refresh failure

Returns:

{
  sessionState: SessionState;           // Current session state
  timeUntilIdle: number | null;         // Milliseconds until idle
  idleWarningVisible: boolean;          // Show idle warning
  extendSession: () => void;            // Reset idle timer
  refreshToken: () => Promise<boolean>; // Manually refresh
  logout: () => Promise<void>;          // Logout
  updateConfig: (newConfig) => void;    // Update config
  manager: SessionManager;              // Underlying manager
}

SessionManager

Direct access to session management service.

Methods:

  • init() — Initialize session management
  • refreshToken(): Promise<boolean> — Refresh token
  • logout(): Promise<void> — Logout and cleanup
  • extendSession() — Reset idle timer
  • getState(): SessionState — Current state
  • getConfig(): SessionConfig — Current config
  • updateConfig(newConfig) — Update config
  • on(event, callback): () => void — Subscribe to events
  • destroy() — Cleanup and destroy manager

Events:

  • 'initialized' — Manager initialized
  • 'tokenRefreshed' — Token successfully refreshed
  • 'idle' — User became idle
  • 'activity' — User activity detected
  • 'idleWarning' — Idle warning (includes timeRemaining)
  • 'sessionExtended' — Session extended
  • 'refreshFailed' — Token refresh failed
  • 'loggedOut' — Logged out

Token Service Functions

  • getTokenInfo(token): TokenInfo | null — Get token expiry and payload
  • isTokenExpired(token, bufferMs?): boolean — Check if token expired
  • getTimeUntilExpiry(token): number — Get milliseconds until expiry
  • getStoredToken(): string | null — Get token from storage
  • storeToken(token, persistent?) — Store token
  • clearToken() — Clear stored token
  • validateToken(token) — Validate token structure

Production Checklist

  • [ ] Configure refreshThresholdMs for your token lifetime
  • [ ] Set refreshEndpoint to your backend refresh URL
  • [ ] Customize refreshPayloadFormatter if your API requires custom payload
  • [ ] Set appropriate idleTimeoutMs based on security requirements
  • [ ] Pass your app's HTTP client via httpClient option
  • [ ] Test token refresh before token expires
  • [ ] Test idle timeout and warning
  • [ ] Handle onSessionExpired to redirect to login
  • [ ] Disable debug: true in production
  • [ ] Monitor onRefreshFailure for refresh failures

Browser Support

  • Chrome/Edge: ✅ All versions
  • Firefox: ✅ All versions
  • Safari: ✅ 12+
  • Mobile: ✅ iOS 12+, Android 5+

Requires fetch API or polyfill.


License

MIT