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

@mapnests/gateway-web-sdk

v1.0.2

Published

Session token management SDK with automatic refresh for React/Next.js applications

Readme

Gateway Session SDK

A lightweight, production-ready session token management SDK for React and Next.js applications. Handles automatic token refresh with configurable intervals to prevent race conditions and token expiration issues.

Features

  • 🔄 Automatic Token Refresh: Configurable background refresh (default: 25 minutes)
  • 🔒 HttpOnly Cookie Support: Secure token storage via server-set cookies
  • ⚛️ React Integration: Simple React hook for seamless integration
  • 🎯 Singleton Pattern: Single session instance across your entire app
  • 📦 Zero Dependencies: Lightweight with only React as a peer dependency
  • 🚀 Next.js Compatible: Works with both React and Next.js applications (SSR-safe)
  • ⏱️ Configurable Intervals: Customize refresh and expiry times
  • 🔔 State Subscriptions: React to session state changes
  • 📘 TypeScript Support: Full TypeScript definitions included

Security Notice

⚠️ Important: This SDK prioritizes server-set HttpOnly cookies for maximum security. The SDK includes a fallback mechanism to set cookies client-side, but this is less secure as these cookies cannot be HttpOnly and are accessible to JavaScript.

Recommended Setup:

  • Always set cookies from your server using the Set-Cookie header with HttpOnly flag
  • The SDK will automatically detect and skip client-side cookie setting when server cookies are present
  • Client-side cookies should only be used for development or specific use cases where server-side setting is not possible

Installation

npm install gateway-web-sdk

Quick Start

Simple Implementation (Copy-Paste)

This setup covers bootstrap on app start and three API patterns:

  • Users → Fetch interceptor
  • Products → Axios interceptor
  • Orders → Manual implementation
  1. Configure and initialize once on app start
import React from 'react';
import ReactDOM from 'react-dom/client';
import { SessionManager } from 'gateway-web-sdk';
import { API_BASE_URL, BOOTSTRAP_PATH, TOKEN_COOKIE_NAME, REFRESH_INTERVAL, TOKEN_EXPIRY, CREDENTIALS } from './config.js';
import App from './App';

const sessionManager = SessionManager.getInstance();
try {
  sessionManager.configure({
    bootstrapUrl: `${API_BASE_URL}${BOOTSTRAP_PATH}`,
    tokenCookieName: TOKEN_COOKIE_NAME,
    refreshInterval: (REFRESH_INTERVAL ? parseInt(REFRESH_INTERVAL) : 25) * 60 * 1000,
    tokenExpiry: (TOKEN_EXPIRY ? parseInt(TOKEN_EXPIRY) : 30) * 60 * 1000,
    credentials: CREDENTIALS === 'true',
  });
} catch (error) {
  console.error('Failed to configure session manager:', error);
}

sessionManager.initialize().catch(err => console.error('Failed to initialize session:', err));

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
  1. API layer with three patterns
// src/api/index.js
import axios from 'axios';
import { fetchInterceptor, setupAxiosInterceptor, SessionManager } from 'gateway-web-sdk';

const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'https://your-gateway.example.com';

// Users via Fetch interceptor
export const getUser = () => fetchInterceptor(`${API_BASE_URL}/api/user`);

// Products via Axios interceptor
export const axiosInstance = setupAxiosInterceptor(
  axios.create({ baseURL: API_BASE_URL, withCredentials: true })
);
export const getProducts = () => axiosInstance.get('/api/products');

// Orders via Manual implementation
const sm = SessionManager.getInstance();
async function manual(url, init = {}) {
  const opts = { ...init, headers: { ...(init.headers || {}) }, credentials: 'include' };
  opts.headers['cf-session-id'] = sm.getSessionId();
  if (sm.shouldUseTokenHeader()) {
    const t = sm.getToken(sm.config.tokenCookieName);
    if (t) opts.headers[sm.config.tokenCookieName] = t;
  }
  let res = await fetch(url, opts);
  if (res.status === 401) {
    const cloned = res.clone();
    try {
      const data = await cloned.json();
      if (data.error_msg === 'INVALID_SESSION') {
        if (sm.isRefreshing()) {
          await sm.waitForRefresh();
        } else {
          const existingToken = sm.getToken(sm.config.tokenCookieName);
          if (!existingToken) {
            await sm.refreshToken();
          }
        }
        opts.headers['cf-session-id'] = sm.getSessionId();
        if (sm.shouldUseTokenHeader()) {
          const nt = sm.getToken(sm.config.tokenCookieName);
          if (nt) opts.headers[sm.config.tokenCookieName] = nt;
        }
        res = await fetch(url, opts);
      }
    } catch {
      // Not JSON or parsing failed
    }
  }
  return res;
}
export const getOrders = () => manual(`${API_BASE_URL}/api/orders`);
  1. Usage in a component
import { useEffect, useState } from 'react';
import { useSession } from 'gateway-web-sdk';
import { getUser, getProducts, getOrders } from './api/index.js';

export default function Dashboard() {
  const { isInitialized, isLoading, error } = useSession();
  const [data, setData] = useState(null);

  useEffect(() => {
    if (!isInitialized) return;
    (async () => {
      try {
        const [u, p, o] = await Promise.all([getUser(), getProducts(), getOrders()]);
        if (!u.ok) throw new Error('User failed');
        if (!o.ok) throw new Error('Orders failed');
        setData({ user: await u.json(), products: p.data, orders: await o.json() });
      } catch (e) {
        console.error(e);
        setData(null);
      }
    })();
  }, [isInitialized]);

  if (isLoading) return <p>Loading session…</p>;
  if (error) return <p>Session error, limited mode.</p>;
  if (!data) return <p>Loading data…</p>;

  return (
    <div>
      <h3>User</h3>
      <pre>{JSON.stringify(data.user, null, 2)}</pre>
      <h3>Products</h3>
      <pre>{JSON.stringify(data.products, null, 2)}</pre>
      <h3>Orders</h3>
      <pre>{JSON.stringify(data.orders, null, 2)}</pre>
    </div>
  );
}

Notes

  • Install axios if you use the Axios pattern: npm i axios
  • Ensure your gateway exposes:
    • GET /api/session/bootstrap
    • GET /api/user
    • GET /api/products
    • GET /api/orders
  • The SDK automatically sends cookies; token header fallback is added only when necessary.

React Application

import React from 'react';
import { useSession } from 'gateway-web-sdk';

function App() {
  const { isInitialized, isLoading, error, timeUntilRefresh, refresh } = useSession();

  if (isLoading) {
    return <div>Loading session...</div>;
  }

  if (error) {
    return <div>Error: {error}</div>;
  }

  if (!isInitialized) {
    return <div>Session not initialized</div>;
  }

  return (
    <div>
      <h1>Session Active</h1>
      <p>Next refresh in: {Math.floor(timeUntilRefresh / 1000)} seconds</p>
      <button onClick={refresh}>Refresh Now</button>
    </div>
  );
}

export default App;

Configuration

Configure the session manager before your app renders:

import React from 'react';
import ReactDOM from 'react-dom/client';
import { SessionManager } from 'gateway-web-sdk';
import App from './App';

// Configure session manager
const sessionManager = SessionManager.getInstance();
sessionManager.configure({
  bootstrapUrl: 'https://your-api.com/session/bootstrap',
  refreshInterval: 25 * 60 * 1000, // 25 minutes
  tokenExpiry: 30 * 60 * 1000,     // 30 minutes
  headers: {
    'X-Custom-Header': 'value',
  },
  credentials: true, // Include cookies in requests
});

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

Next.js Application

Using App Router (app/layout.js)

'use client';

import { useEffect } from 'react';
import { SessionManager } from 'gateway-web-sdk';

export default function RootLayout({ children }) {
  useEffect(() => {
    const sessionManager = SessionManager.getInstance();
    sessionManager.configure({
      bootstrapUrl: '/api/session/bootstrap',
      refreshInterval: 25 * 60 * 1000,
    });
    
    sessionManager.initialize();
  }, []);

  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  );
}

Using Pages Router (pages/_app.js)

import { useEffect } from 'react';
import { SessionManager } from 'gateway-web-sdk';

function MyApp({ Component, pageProps }) {
  useEffect(() => {
    const sessionManager = SessionManager.getInstance();
    sessionManager.configure({
      bootstrapUrl: '/api/session/bootstrap',
      refreshInterval: 25 * 60 * 1000,
    });
    
    sessionManager.initialize();
  }, []);

  return <Component {...pageProps} />;
}

export default MyApp;

API Reference

useSession(options)

React hook for session management.

Parameters

| Parameter | Type | Default | Description | |-----------|------|---------|-------------| | options.autoInitialize | boolean | true | Automatically initialize session on mount |

Return Value

{
  isInitialized: boolean;      // Whether session is initialized
  isLoading: boolean;          // Whether bootstrap is in progress
  error: string | null;        // Error message if any
  lastRefreshTime: number;     // Timestamp of last refresh
  nextRefreshTime: number;     // Timestamp of next scheduled refresh
  timeUntilRefresh: number;    // Milliseconds until next refresh
  refresh: () => Promise<void>; // Manual refresh function
  initialize: () => Promise<void>; // Manual initialize function
}

SessionManager

Core session management class (Singleton).

Methods

configure(config)

Configure the session manager.

sessionManager.configure({
  bootstrapUrl: '/session/bootstrap',  // Required: Bootstrap API endpoint
  tokenCookieName: 'stoken',     // Optional: Custom token cookie name
  refreshInterval: 25 * 60 * 1000,      // Optional: Refresh interval (ms)
  tokenExpiry: 30 * 60 * 1000,          // Optional: Token expiry (ms)
  maxRetries: 3,                         // Optional: Max retry attempts
  headers: {},                           // Optional: Additional headers
  credentials: true,                     // Optional: Include credentials
});
initialize()

Initialize session by calling bootstrap API.

await sessionManager.initialize();
refreshToken()

Manually refresh the session token.

await sessionManager.refreshToken();
getSessionStatus()

Get current session status.

const status = sessionManager.getSessionStatus();
subscribe(listener)

Subscribe to session state changes.

const unsubscribe = sessionManager.subscribe((state) => {
  console.log('Session state:', state);
});

// Later: unsubscribe()
destroy()

Clean up and reset the session manager.

sessionManager.destroy();

Interceptors (Optional)

fetchInterceptor(url, options)

Fetch wrapper with automatic token refresh on 401/403.

import { fetchInterceptor } from 'gateway-web-sdk';

const response = await fetchInterceptor('/api/data', {
  method: 'GET',
  headers: { 'Content-Type': 'application/json' }
});

setupAxiosInterceptor(axiosInstance)

Configure Axios instance with automatic token refresh.

import axios from 'axios';
import { setupAxiosInterceptor } from 'gateway-web-sdk';

const api = setupAxiosInterceptor(axios.create({
  baseURL: 'https://api.example.com'
}));

Client-Side Best Practices

  1. Single Instance: Call configure() once at app startup and reuse the singleton.
  2. Token Timing: Set refreshInterval about 5 minutes before tokenExpiry to avoid race conditions.
  3. Error Handling: Handle errors and provide user feedback for limited mode.
  4. HTTPS: Use secure, production-grade origins (https) in production environments.
  5. CORS/Credentials: If cross-origin, ensure credentials are enabled in client config and server CORS.
  6. Initialization: Initialize after configure() and before issuing business API calls.

Troubleshooting

Cookies not being set

  • Verify your API returns Set-Cookie header with HttpOnly flag
  • Check CORS configuration allows credentials
  • Ensure credentials: true in configuration

Automatic refresh not working

  • Check browser console for errors
  • Verify refreshInterval is set correctly
  • Ensure timer isn't being cleared prematurely

Multiple initializations

  • The SDK uses singleton pattern, but ensure you're not calling initialize() multiple times
  • Use autoInitialize: false in useSession() if you want manual control

Next.js SSR errors

  • The SDK automatically detects SSR environments and prevents initialization
  • Always wrap initialization in useEffect or client components ('use client')
  • Do not call initialize() during server-side rendering

TypeScript Support

Full TypeScript definitions are included:

import { SessionManager, useSession } from 'gateway-web-sdk';
import type { SessionConfig, SessionState, UseSessionOptions } from 'gateway-web-sdk';

const config: SessionConfig = {
  bootstrapUrl: '/api/session',
  refreshInterval: 25 * 60 * 1000
};

const manager = SessionManager.getInstance();
manager.configure(config);

Changelog

[1.0.0] - 2024-01-12

Added

  • Initial production release
  • TypeScript definitions for full type safety
  • Build process with Rollup (CJS + ESM outputs)
  • SSR detection for Next.js compatibility
  • Configuration validation
  • Secure session ID generation using crypto.randomUUID()
  • URL encoding for cookie values

Security

  • Added warnings for client-side cookie limitations
  • Improved session ID generation with Web Crypto API
  • Added SSR environment checks to prevent runtime errors
  • URL-encoded cookie values to prevent injection

License

MIT

Contributing

Contributions are welcome! Please open an issue or submit a pull request.

Support

For issues and questions: