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

@pawells/react-auth

v3.0.0

Published

Keycloak Authorization Code Flow with PKCE authentication for React SPAs

Readme

React Authentication Library

CI npm version Node License: MIT

Description

@pawells/react-auth is a Keycloak Authorization Code + PKCE authentication library for React SPAs, built on oidc-client-ts. It provides:

  • PKCE (S256) enforcement on every sign-in flow — no additional configuration required
  • Automatic silent token renewal via refresh token, wired to the oidc-client-ts event system
  • Configurable session storage: sessionStorage (default, cleared on tab close, per-tab isolated) or localStorage
  • Popup authentication flow for sign-in without a full-page redirect
  • An Axios hook (useAuthAxios) and an Apollo Link factory (createAuthApolloLink) for authenticated API and GraphQL requests

Requirements

  • Node.js >=22
  • react >=19.0.0 (required peer)
  • react-dom >=19.0.0 (required peer)
  • @apollo/client >=4.0.0 (optional peer — required only for createAuthApolloLink)
  • axios >=1.0.0 (optional peer — required only for useAuthAxios)

Installation

npm install @pawells/react-auth

To use the Axios integration:

npm install axios

To use the Apollo Link integration:

npm install @apollo/client

Quick Start

Place AuthProvider inside your router so that onSigninCallback can call useNavigate:

import { BrowserRouter, useNavigate } from 'react-router-dom';
import { AuthProvider } from '@pawells/react-auth';
import App from './App';

function AppWithAuth() {
  const navigate = useNavigate();
  return (
    <AuthProvider
      authority="https://keycloak.example.com/realms/my-realm"
      client_id="my-spa-client"
      redirect_uri={`${window.location.origin}/auth/callback`}
      post_logout_redirect_uri={window.location.origin}
      onSigninCallback={() => navigate('/')}
    >
      <App />
    </AuthProvider>
  );
}

export default function Root() {
  return (
    <BrowserRouter>
      <AppWithAuth />
    </BrowserRouter>
  );
}

AuthProvider handles the redirect callback automatically on mount. When the browser lands on redirect_uri with a code query parameter, the provider exchanges the code, stores the session, and calls onSigninCallback — no extra route or component is required.

API Reference

Components

AuthProvider

Provides Keycloak Authorization Code + PKCE authentication to the component tree. Silent token renewal via refresh token is enabled automatically. The storageType prop is read only on initial render; changes after mount are ignored.

Props (AuthProviderProps — extends KeycloakAuthConfig and accepts children):

| Prop | Type | Required | Default | Description | |---|---|---|---|---| | authority | string | Yes | — | Keycloak realm base URL. Format: https://{host}/realms/{realm} (Keycloak 17+) | | client_id | string | Yes | — | Client ID of a public (non-confidential) Keycloak client | | redirect_uri | string | Yes | — | URI to redirect to after a successful login; must be registered in Keycloak | | post_logout_redirect_uri | string | No | — | URI to redirect to after logout; must be registered in Keycloak's "Valid post logout redirect URIs" | | popup_redirect_uri | string | No | — | URI for the popup authentication callback window; required for loginWithPopup() and must be registered in Keycloak | | scope | string | No | 'openid profile email' | OAuth2/OIDC scopes to request. Add 'offline_access' for long-lived refresh tokens | | storageType | StorageType | No | 'sessionStorage' | Where to persist the OIDC user session. See StorageType | | onSigninCallback | (user: User) => void | No | — | Invoked after a successful signin redirect callback. Use to navigate the user to their intended destination | | children | React.ReactNode | Yes | — | Component subtree that receives the auth context |

Hooks

useAuth(): AuthContextValue

Returns the current auth context value from the nearest AuthProvider. Provides access to authentication state, login/logout methods, and token retrieval.

Throws a BaseError when called outside an AuthProvider.

function Profile() {
  const { user, logout, isAuthenticated } = useAuth();
  if (!isAuthenticated) return <div>Not logged in</div>;
  return <button onClick={logout}>{user?.profile.name}</button>;
}

The returned AuthContextValue exposes all AuthState fields plus five methods:

| Member | Type | Description | |---|---|---| | isAuthenticated | boolean | Whether the user holds a valid, non-expired access token | | isLoading | boolean | Whether an auth operation is in progress (hydration, callback, or silent renew) | | user | User \| null | The authenticated OIDC user object, or null when not authenticated | | error | Error \| null | The most recent auth error, or null if none | | login() | () => Promise<void> | Initiates the Keycloak Authorization Code + PKCE redirect flow | | loginWithPopup() | () => Promise<void> | Opens a Keycloak login popup without navigating the main page. Requires popup_redirect_uri. Throws if the popup is blocked | | logout() | () => Promise<void> | Redirects to the Keycloak end_session_endpoint and clears the local session | | getAccessToken() | () => Promise<string \| null> | Returns the current access token, triggering silent renewal if expired. Returns null if not authenticated or if renewal fails | | clearSession() | () => Promise<void> | Removes the user from storage and resets auth state without redirecting to Keycloak |

useAuthAxios(instance?: AxiosInstance): AxiosInstance

Returns an Axios instance with a request interceptor that automatically attaches a valid Keycloak Bearer token to same-origin requests. The token is attached only when the resolved request URL has the same origin as window.location; cross-origin requests do not receive an Authorization header (CWE-319 mitigation). If the access token is expired, silent renewal via the refresh token is attempted before the request is dispatched. If the user is not authenticated, the request proceeds without an Authorization header.

Pass an existing Axios instance to attach the interceptor to it; omit the argument for a new axios.create() instance. The returned instance reference is stable across re-renders unless the passed instance reference changes.

// New instance (default)
function MediaService() {
  const api = useAuthAxios();
  const fetchItems = () => api.get('/api/items');
}

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

function MediaService() {
  const api = useAuthAxios(sharedAxios);
}

Note: If multiple components call useAuthAxios with the same shared AxiosInstance, each call registers a separate request interceptor. Pass a dedicated instance per hook invocation to avoid duplicate interceptors.

Integrations

createAuthApolloLink(getAccessToken: AuthContextValue['getAccessToken']): ApolloLink

Creates an Apollo Link that prepends a valid Keycloak Bearer token to the Authorization header of same-origin GraphQL operations. The token is attached only when the GraphQL endpoint URI has the same origin as window.location; cross-origin requests do not receive an Authorization header (CWE-319 mitigation). Silent token renewal is triggered transparently when the access token is expired. If getAccessToken throws, the error is propagated to the GraphQL operation's error handler. If the subscription is cancelled before the token resolves, no error is emitted.

import { useMemo } from 'react';
import { ApolloClient, ApolloProvider, HttpLink, InMemoryCache } from '@apollo/client';
import { createAuthApolloLink, useAuth } from '@pawells/react-auth';

function ApolloSetup({ children }: { children: React.ReactNode }) {
  const { getAccessToken } = useAuth();

  const client = useMemo(() => {
    const authLink = createAuthApolloLink(getAccessToken);
    const httpLink = new HttpLink({ uri: '/graphql' });
    return new ApolloClient({ link: authLink.concat(httpLink), cache: new InMemoryCache() });
  }, [getAccessToken]);

  return <ApolloProvider client={client}>{children}</ApolloProvider>;
}

Sub-Path Exports

For convenience, you can import from sub-paths to reduce bundle size when using only specific modules:

// Import from specific sub-paths
import { AuthProvider, useAuth } from '@pawells/react-auth/context';
import { useAuthAxios } from '@pawells/react-auth/hooks';
import { createAuthApolloLink } from '@pawells/react-auth/integrations';
import { parseJwt, isTokenExpired } from '@pawells/react-auth/utils';

Utilities

parseJwt<T extends Record<string, unknown>>(token: string): T | null

Decodes a JWT payload without verifying the signature. Intended for reading client-visible claims only — signature verification must always be performed server-side. Returns null if the token is malformed, does not have exactly three parts, or the payload cannot be parsed as a plain object.

const payload = parseJwt<{ sub: string; exp: number }>(token);
if (!payload) throw new Error('Invalid token');
console.log(payload.sub); // typed as string

isTokenExpired(token: string, clockSkewSeconds?: number): boolean

Returns true if a JWT is expired or malformed. An optional clockSkewSeconds buffer adjusts the validity window: a token with exp = T is considered expired only when now > T + clockSkewSeconds. Positive values extend the validity window (token treated as valid for up to that many seconds past its stated expiry); negative values make the token expire earlier (stricter validation). Default is 0 (no buffer).

Types

KeycloakAuthConfig

Configuration interface for AuthProvider. Extends oidc-client-ts UserManagerSettings (omitting userStore and stateStore, which are managed internally) with Keycloak-specific required fields and the storageType option.

AuthContextValue

Value exposed by the auth context and returned by useAuth(). Extends AuthState with five methods: login(), loginWithPopup(), logout(), getAccessToken(), and clearSession().

AuthState

Snapshot of the current authentication state. Fields: isAuthenticated: boolean, isLoading: boolean, user: User | null, error: Error | null.

AuthProviderProps

Props accepted by AuthProvider. Extends KeycloakAuthConfig with children: React.ReactNode.

StorageType

'sessionStorage' | 'localStorage' — controls where the OIDC user session is persisted.

  • 'sessionStorage' (default) — cleared when the tab closes; isolated per tab.
  • 'localStorage' — survives page reloads; shared across same-origin tabs. Use with caution in XSS-prone environments; a security warning is logged to the console when this option is active.

License

MIT — See LICENSE for details.