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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@identity-base/react-client

v0.7.9

Published

Headless React authentication client for Identity.Base

Readme

@identity-base/react-client

Headless React authentication client for Identity.Base. Provides all the heavy lifting for authentication flows while giving you complete control over your UI.

Built on (and depends on) @identity-base/client-core.

Features

  • 🔐 Complete OAuth2/PKCE flow - Authorization code with PKCE
  • 🔄 Automatic token management - Refresh tokens, storage, expiration
  • 🎯 Headless design - Zero UI opinions, bring your own components
  • 🛡️ Route protection - <ProtectedRoute> and useRequireAuth
  • 📱 MFA support - Email and SMS multi-factor authentication
  • 👤 Profile management - User profiles with custom metadata
  • 🔗 External providers - Google, Microsoft, Apple sign-in
  • Cross-tab sync - Authentication state synced across tabs
  • 📦 TypeScript first - Complete type definitions
  • 🪝 React hooks - Clean, composable API

Installation

npm install @identity-base/react-client

Quick Start

1. Wrap your app with IdentityProvider

import { IdentityProvider, enableDebugLogging } from '@identity-base/react-client'

// Enable debug logging in development
if (import.meta.env.DEV) {
  enableDebugLogging(true)
}

function App() {
  return (
    <IdentityProvider
      config={{
        apiBase: 'https://your-identity-api.com',
        clientId: 'your-spa-client-id',
        redirectUri: 'https://your-app.com/auth/callback',
      }}
    >
      <Router>
        <Routes>
          <Route path="/login" element={<LoginPage />} />
          <Route path="/dashboard" element={<DashboardPage />} />
          <Route path="/auth/callback" element={<CallbackPage />} />
        </Routes>
      </Router>
    </IdentityProvider>
  )
}

2. Create a login form

import { useLogin } from '@identity-base/react-client'

function LoginPage() {
  const { login, isLoading, error } = useLogin({
    onSuccess: () => navigate('/dashboard')
  })

  const handleSubmit = async (e: FormEvent) => {
    e.preventDefault()
    await login({ email, password })
  }

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onChange={e => setEmail(e.target.value)}
        disabled={isLoading}
      />
      <input
        type="password"
        value={password}
        onChange={e => setPassword(e.target.value)}
        disabled={isLoading}
      />
      <button type="submit" disabled={isLoading}>
        {isLoading ? 'Signing in...' : 'Sign In'}
      </button>
      {error && <div className="error">{error.message}</div>}
    </form>
  )
}

3. Protect routes

import { ProtectedRoute, useAuth } from '@identity-base/react-client'

function DashboardPage() {
  return (
    <ProtectedRoute fallback={<div>Please sign in...</div>}>
      <Dashboard />
    </ProtectedRoute>
  )
}

function Dashboard() {
  const { user, logout } = useAuth()

  return (
    <div>
      <h1>Welcome {user?.displayName}!</h1>
      <button onClick={logout}>Sign Out</button>
    </div>
  )
}

4. Handle OAuth callback

import { useAuthorization } from '@identity-base/react-client'
import { useSearchParams } from 'react-router-dom'

function CallbackPage() {
  const [params] = useSearchParams()
  const { handleCallback, isLoading, error } = useAuthorization({
    onSuccess: () => navigate('/dashboard')
  })

  useEffect(() => {
    const code = params.get('code')
    const state = params.get('state')

    if (code && state) {
      handleCallback(code, state)
    }
  }, [params, handleCallback])

  if (isLoading) return <div>Completing sign in...</div>
  if (error) return <div>Sign in failed: {error.message}</div>

  return <div>Processing...</div>
}

Configuration

interface IdentityConfig {
  // Required
  apiBase: string              // 'https://your-identity-api.com'
  clientId: string             // Your OAuth2 client ID
  redirectUri: string          // Where to redirect after auth

  // Optional
  scope?: string               // OAuth2 scopes (default: 'openid profile email offline_access')
  tokenStorage?: 'localStorage' | 'sessionStorage' | 'memory'
  autoRefresh?: boolean        // Auto refresh access tokens (default: true)
  timeout?: number            // API timeout in ms (default: 10000)
}

Token Auto-Refresh

  • When autoRefresh is true (default), the SDK inspects the JWT exp claim and requests a new access token 30 seconds before expiry. If refresh fails, cached tokens are cleared and the error is rethrown so the UI can prompt for a fresh sign-in.
  • When autoRefresh is false, expired access tokens are removed from storage and ensureValidToken() resolves to null, leaving it to the host app to restart authentication.
  • Auto-refresh applies regardless of storage backend (localStorage, sessionStorage, or in-memory) and requires a refresh token to be available.

Hooks API

useAuth()

const {
  user,           // Current user profile or null
  isAuthenticated, // Boolean auth state
  isLoading,      // Initial loading state
  error,          // Any auth errors
  refreshUser,    // Manually refresh user data
} = useAuth()

useLogin()

const {
  login,     // (credentials) => Promise<LoginResponse>
  logout,    // () => Promise<void>
  isLoading, // Boolean loading state
  error,     // Login/logout errors
} = useLogin({
  onSuccess: (response) => {}, // Optional success callback
  onError: (error) => {},     // Optional error callback
})

useRegister()

const {
  register,  // (userData) => Promise<{correlationId}>
  isLoading, // Boolean loading state
  error,     // Registration errors
} = useRegister({
  onSuccess: (response) => {}, // Optional success callback
})

useAuthorization()

const {
  startAuthorization, // () => Promise<void> (redirects to auth)
  handleCallback,     // (code, state) => Promise<User>
  isLoading,
  error,
} = useAuthorization({
  onSuccess: () => {}, // Called after successful auth
})

Route Protection

ProtectedRoute Component

<ProtectedRoute
  fallback={<LoginPrompt />}
  redirectTo="/login"
  onUnauthenticated={() => {}}
>
  <SecretContent />
</ProtectedRoute>

useRequireAuth Hook

function SecurePage() {
  const user = useRequireAuth({
    redirectTo: '/login',
    onUnauthenticated: () => notify('Please sign in')
  })

  // user is null during loading/redirecting
  // user is UserProfile once authenticated
  if (!user) return null

  return <div>Hello {user.displayName}</div>
}

Advanced Usage

Debug Logging

The client includes built-in debug logging to help troubleshoot authentication flows. Debug logging is disabled by default.

Enable via Code (Recommended)

import { enableDebugLogging } from '@identity-base/react-client'

// Enable only in development
if (import.meta.env.DEV) {
  enableDebugLogging(true)
}

// Or conditionally based on environment variable
if (import.meta.env.VITE_ENABLE_IDENTITY_DEBUG === 'true') {
  enableDebugLogging(true)
}

Enable via Browser Console

// Enable debug logging at runtime
__enableIdentityDebug(true)   // Returns true
__enableIdentityDebug(false)  // Returns false

// Check current debug state
window.__identityDebugEnabled  // true/false

Debug Output

When enabled, you'll see detailed logs for:

  • Authentication flows (login, logout, token refresh)
  • API requests and responses
  • MFA challenges and verification
  • User profile updates
  • Authorization code exchanges

Example debug output:

IdentityProvider.tsx: Module loading
IdentityProvider: Creating new instance with config: {...}
IdentityProvider.refreshUser: Starting user refresh
Using Bearer token authentication for /users/me
useMfa.verifyChallenge: Starting MFA verification

Custom Token Storage

import { createTokenStorage } from '@identity-base/react-client'

const storage = createTokenStorage('sessionStorage')
// or implement your own TokenStorage interface

Direct Auth Manager Access

import { useIdentityContext } from '@identity-base/react-client'

function AdvancedComponent() {
  const { authManager } = useIdentityContext()

  const handleExternalAuth = () => {
    const url = authManager.buildExternalStartUrl('google', 'login', '/dashboard')
    window.location.href = url
  }
}

TypeScript

The package is built with TypeScript and includes complete type definitions:

import type {
  UserProfile,
  LoginRequest,
  IdentityConfig,
  ApiError
} from '@identity-base/react-client'

// Debug utilities
import { enableDebugLogging, debugLog } from '@identity-base/react-client'

Troubleshooting

Enable Debug Logging

If you're experiencing issues with authentication flows, enable debug logging to see detailed information:

import { enableDebugLogging } from '@identity-base/react-client'

// Enable in development
if (process.env.NODE_ENV === 'development') {
  enableDebugLogging(true)
}

// Or enable via browser console
// __enableIdentityDebug(true)

Common Issues

Authentication Not Working

  1. Check debug logs - Enable debug logging to see API requests/responses
  2. Verify configuration - Ensure apiBase, clientId, and redirectUri are correct
  3. CORS issues - Make sure your API allows requests from your frontend origin
  4. Token storage - Try different storage options (localStorage, sessionStorage, memory)

MFA Issues

// Check MFA debug output
__enableIdentityDebug(true)

// Look for these debug messages:
// - "useMfa.verifyChallenge: Starting MFA verification"
// - "useMfa.verifyChallenge: MFA verification successful"
// - "useMfa.verifyChallenge: MFA verification failed"

OAuth/PKCE Issues

// Debug OAuth flows
__enableIdentityDebug(true)

// Check for PKCE-related logs:
// - "IdentityProvider: Creating new instance with config"
// - Authorization URL generation
// - Token exchange logs

Browser Console Debug Commands

// Enable/disable debug logging
__enableIdentityDebug(true)    // Enable
__enableIdentityDebug(false)   // Disable

// Check current state
window.__identityDebugEnabled  // true or false

// Manual user refresh (when authenticated)
window.__identityRefreshUser?.()

Performance Tips

  • Use memory storage for better performance in SPAs: tokenStorage: 'memory'
  • Disable debug logging in production to reduce console output
  • Use route-level protection with <ProtectedRoute> for better UX

License

MIT