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

use-identity

v0.2.4

Published

React hooks and utilities for identity management with OIDC-compliant backends

Readme

use-identity

A lightweight, TypeScript-first identity management library for frontend applications. Designed to work with OIDC-compliant identity providers behind a backend-for-frontend (BFF) pattern.

Features

  • 🎯 Framework Agnostic Core - Use with any JavaScript framework or vanilla JS/TS
  • ⚛️ React Integration - First-class React hooks and context provider
  • 🔄 Automatic Caching - Smart caching with configurable TTL
  • 🔁 Auto Refresh - Optional automatic identity refresh at intervals
  • 📡 Event System - Subscribe to identity state changes
  • 🔒 Type Safe - Full TypeScript support with strict typing
  • 🪶 Lightweight - Zero runtime dependencies (React is optional peer dep)
  • 🌐 SSR Ready - Server-side rendering support for Next.js/Remix

Installation

npm install use-identity
# or
yarn add use-identity
# or
pnpm add use-identity

Quick Start

React Usage

import { IdentityProvider, useIdentity } from "use-identity";

// Wrap your app with the provider
function App() {
  return (
    <IdentityProvider config={{ endpoint: "/api/identity" }}>
      <UserProfile />
    </IdentityProvider>
  );
}

// Use the hook in any component
function UserProfile() {
  const { identity, isLoading, isAuthenticated, refresh } = useIdentity();

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

  if (!isAuthenticated) {
    return <div>Please log in</div>;
  }

  return (
    <div>
      <img src={identity.picture ?? "/default-avatar.png"} alt="Avatar" />
      <h1>Hello, {identity.displayName}!</h1>
      <p>Email: {identity.email}</p>
      <button onClick={() => refresh()}>Refresh</button>
    </div>
  );
}

Non-React Usage

Use the /core entry point for non-React applications:

import { useIdentity, onIdentityChange, IdentityService } from "use-identity/core";

// Simple one-time load
const identity = await useIdentity({ endpoint: "/api/identity" });

if (identity) {
  console.log(`Hello, ${identity.displayName}!`);
} else {
  console.log("Not authenticated");
}

// Subscribe to changes
const unsubscribe = onIdentityChange((event) => {
  console.log("Identity state changed:", event.state);
});

// Full control with IdentityService class
const service = new IdentityService({
  endpoint: "/api/identity",
  cacheTTL: 60000, // 1 minute cache
  refreshInterval: 30000 // Auto-refresh every 30 seconds
});

service.subscribe((event) => {
  if (event.type === "identityLoaded") {
    console.log("User:", event.state.identity?.displayName);
  }
});

Configuration

| Option | Type | Default | Description | | ----------------- | --------- | ------------------ | ---------------------------------------------- | | endpoint | string | '/api/identity' | URL to fetch identity from | | cacheTTL | number | 300000 (5 min) | Cache time-to-live in milliseconds | | refreshInterval | number | 0 (disabled) | Auto-refresh interval in milliseconds | | fetchOnInit | boolean | true | Whether to fetch immediately on initialization | | timeout | number | 10000 (10s) | Request timeout in milliseconds | | retryAttempts | number | 1 | Number of retry attempts for failed requests | | retryDelay | number | 1000 | Delay between retry attempts | | fetcher | Fetcher | globalThis.fetch | Custom fetch function |

API Reference

React API

<IdentityProvider>

Context provider that manages identity state for your application.

<IdentityProvider
  config={{
    endpoint: "/api/identity",
    cacheTTL: 60000,
    refreshInterval: 30000
  }}
>
  {children}
</IdentityProvider>

useIdentity()

Main hook for accessing identity state and operations.

const {
  identity, // Identity | null - The current user identity
  status, // 'idle' | 'loading' | 'authenticated' | 'unauthenticated' | 'error'
  error, // IdentityError | null - Error information
  isLoading, // boolean - True during fetch
  isAuthenticated, // boolean - True when authenticated
  lastFetchedAt, // number | null - Timestamp of last fetch
  refresh, // () => Promise<Identity | null> - Refresh identity
  clearIdentity // () => void - Clear cached identity
} = useIdentity();

useIdentitySelector(selector, options?)

Select specific parts of identity state to prevent unnecessary re-renders.

// Only re-renders when displayName changes
const displayName = useIdentitySelector((state) => state.identity?.displayName ?? "Guest");

// With custom equality
const user = useIdentitySelector((state) => ({ name: state.identity?.displayName, email: state.identity?.email }), {
  isEqual: (a, b) => a.name === b.name && a.email === b.email
});

useAuthenticatedIdentity()

Convenience hook that provides proper typing for authenticated users.

const { identity, isAuthenticated, isLoading } = useAuthenticatedIdentity();

if (!isAuthenticated) {
  return <Redirect to="/login" />;
}

// TypeScript knows identity is not null here
return <div>Welcome, {identity.displayName}!</div>;

useIdentitySSR()

Hook for server-side rendering that handles hydration.

const { identity, isHydrated, isLoading } = useIdentitySSR();

if (!isHydrated || isLoading) {
  return <Skeleton />;
}

return identity ? <UserMenu user={identity} /> : <LoginButton />;

Core API (Non-React)

IdentityService

The core service class for identity management.

const service = new IdentityService({
  endpoint: "/api/identity",
  cacheTTL: 60000
});

// Fetch identity
const identity = await service.fetchIdentity();

// Get cached identity (returns null if expired)
const cached = service.getCachedIdentity();

// Subscribe to changes
const unsubscribe = service.subscribe((event) => {
  console.log(event.type, event.state);
});

// Clear identity
service.clearIdentity();

// Start/stop auto-refresh
service.startAutoRefresh();
service.stopAutoRefresh();

// Clean up
service.dispose();

Convenience Functions

import {
  useIdentity,
  getCachedIdentity,
  clearIdentity,
  onIdentityChange,
  getIdentityService,
  resetIdentityService
} from "use-identity/core";

// Load identity (uses cached if available)
const identity = await useIdentity();

// Get cached without fetching
const cached = getCachedIdentity();

// Clear cached identity
clearIdentity();

// Subscribe to changes
const unsubscribe = onIdentityChange((event) => {
  console.log("State changed:", event.state);
});

// Get/create default service instance
const service = getIdentityService();

// Reset default service (useful for testing)
resetIdentityService();

Types

Identity

The normalized identity object:

interface Identity {
  id: string; // Unique identifier (from 'sub' claim)
  displayName: string; // Computed display name
  email: string | null;
  emailVerified: boolean;
  picture: string | null;
  claims: OIDCUserInfo; // Full OIDC claims
}

IdentityState

The full state object:

interface IdentityState {
  identity: Identity | null;
  status: "idle" | "loading" | "authenticated" | "unauthenticated" | "error";
  error: IdentityError | null;
  isLoading: boolean;
  isAuthenticated: boolean;
  lastFetchedAt: number | null;
}

IdentityError

Error information:

interface IdentityError {
  code: "NETWORK_ERROR" | "UNAUTHORIZED" | "FORBIDDEN" | "NOT_FOUND" | "SERVER_ERROR" | "INVALID_RESPONSE" | "TIMEOUT" | "UNKNOWN";
  message: string;
  statusCode?: number;
  cause?: Error;
}

Backend Setup

This library expects your backend to expose an identity endpoint that returns OIDC UserInfo claims. A typical setup with APISIX:

# APISIX route configuration
routes:
  - uri: /api/identity
    upstream:
      nodes:
        - host: 127.0.0.1
          port: 8080
    plugins:
      openid-connect:
        # ... OIDC configuration
      proxy-rewrite:
        # Rewrite to userinfo endpoint

The endpoint should return a JSON object with at least a sub claim:

{
  "sub": "user-123",
  "name": "John Doe",
  "email": "[email protected]",
  "email_verified": true,
  "picture": "https://example.com/avatar.jpg"
}

Advanced Usage

Custom Fetcher

const customFetcher = async (url: string, init?: RequestInit) => {
  const response = await fetch(url, {
    ...init,
    headers: {
      ...init?.headers,
      "X-Custom-Header": "value"
    }
  });
  return response;
};

const service = new IdentityService({
  endpoint: "/api/identity",
  fetcher: customFetcher
});

Sharing State Between React and Non-React Code

// Create a shared service instance
const sharedService = new IdentityService({
  endpoint: '/api/identity',
  fetchOnInit: false,
});

// Use in React
function App() {
  return (
    <IdentityProvider service={sharedService}>
      <MyApp />
    </IdentityProvider>
  );
}

// Use in non-React code (e.g., API client)
sharedService.subscribe((event) => {
  if (event.state.isAuthenticated) {
    apiClient.setUser(event.state.identity);
  }
});

Testing

import { vi } from 'vitest';
import { render } from '@testing-library/react';
import { IdentityProvider } from 'use-identity';

const mockUserInfo = {
  sub: 'test-user',
  name: 'Test User',
  email: '[email protected]',
};

const mockFetcher = vi.fn().mockResolvedValue(
  new Response(JSON.stringify(mockUserInfo), {
    status: 200,
    headers: { 'Content-Type': 'application/json' },
  })
);

render(
  <IdentityProvider config={{ fetcher: mockFetcher }}>
    <YourComponent />
  </IdentityProvider>
);

License

MIT