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

@arow-software/auth-client

v3.3.1

Published

Reusable auth package for ArowAuth SSO integration

Downloads

826

Readme

@arow-software/auth-client

Reusable authentication package for ArowAuth SSO integration. Provides React context, hooks, and utilities for seamless SSO authentication with automatic token refresh.

⚠️ v2.0.0 Breaking Changes (PKCE Flow)

v2.0.0 migrates from OAuth 2.0 Implicit Flow to Authorization Code + PKCE for enhanced security.

What Changed

  • OAuth Flow: Changed from response_type=token (implicit) to response_type=code (authorization code with PKCE)
  • Security: Tokens no longer exposed in URL hash fragment; authorization code exchanged server-side for tokens
  • PKCE: Code verifier/challenge pairs automatically generated and verified
  • API Change: login() method is now async (returns Promise<void>)

Migration Guide

Before (v1.x):

const { login } = useAuth();
login('/dashboard');  // Synchronous

After (v2.x):

const { login } = useAuth();
await login('/dashboard');  // Now async
// OR
login('/dashboard');  // Fire-and-forget still works

Backward Compatibility

v2.0 maintains backward compatibility with v1.x implicit flow during transition:

  • Still parses tokens from URL hash fragment as fallback
  • Existing v1.x clients can continue using the library without changes
  • Prioritizes new PKCE flow (query param code) over legacy implicit flow (hash fragment tokens)

Why PKCE?

  • More Secure: Eliminates token exposure in browser history and referrer headers
  • Industry Standard: OAuth 2.1 deprecates implicit flow in favor of authorization code + PKCE
  • Mobile-Ready: PKCE is required for native mobile apps and recommended for SPAs

Features

  • 🔐 PKCE Flow - Secure Authorization Code + PKCE (RFC 7636)
  • 🔒 Token Management - Secure storage, automatic refresh, JWT decoding
  • 🔄 API Interceptors - Axios interceptors with 401 handling and request queuing
  • ⚛️ React Integration - Context provider and hooks for easy auth state management
  • 🎯 TypeScript - Full type definitions included
  • 📦 Lightweight - Peer dependencies only (React, Axios)

Installation

# npm
npm install @arow-software/auth-client

# yarn
yarn add @arow-software/auth-client

# pnpm
pnpm add @arow-software/auth-client

Peer Dependencies

Make sure you have these installed:

npm install react axios

Quick Start

1. Wrap your app with AuthProvider

// App.tsx
import { AuthProvider } from '@arow-software/auth-client';

function App() {
  return (
    <AuthProvider
      ssoBaseUrl="https://sso.arowsoftware.co.uk"
      clientId="arowtrades"
      apiBaseUrl="http://localhost:5001"
    >
      <YourApp />
    </AuthProvider>
  );
}

2. Use the useAuth hook

// LoginButton.tsx
import { useAuth } from '@arow-software/auth-client';

function LoginButton() {
  const { isAuthenticated, isLoading, user, login, logout } = useAuth();

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

  if (isAuthenticated) {
    return (
      <div>
        <span>Welcome, {user?.displayName || user?.email}!</span>
        <button onClick={logout}>Logout</button>
      </div>
    );
  }

  return <button onClick={() => login()}>Login with SSO</button>;
}

3. Make authenticated API calls

// DataComponent.tsx
import { useAuthClient } from '@arow-software/auth-client';
import { useState, useEffect } from 'react';

function DataComponent() {
  const client = useAuthClient();
  const [data, setData] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await client.get('/api/data');
        setData(response.data);
      } catch (error) {
        console.error('Failed to fetch data', error);
      }
    };
    
    fetchData();
  }, [client]);

  return <div>{JSON.stringify(data)}</div>;
}

API Reference

AuthProvider Props

| Prop | Type | Required | Description | |------|------|----------|-------------| | ssoBaseUrl | string | ✅ | Base URL of ArowAuth SSO server | | clientId | string | ✅ | Client ID registered with ArowAuth | | apiBaseUrl | string | ❌ | Base URL for your API (for axios client) | | redirectUri | string | ❌ | Custom redirect URI (defaults to origin + /callback) | | scopes | string[] | ❌ | OAuth scopes (defaults to ['openid', 'email', 'profile']) | | storagePrefix | string | ❌ | Storage key prefix (defaults to 'arowauth') | | useSessionStorage | boolean | ❌ | Use sessionStorage instead of localStorage | | onTokenRefresh | function | ❌ | Callback when tokens are refreshed | | onAuthError | function | ❌ | Callback when auth error occurs | | onLogout | function | ❌ | Callback when user is logged out |

useAuth Hook

Returns an object with:

{
  user: User | null;           // Current user object
  isAuthenticated: boolean;    // Whether user is logged in
  isLoading: boolean;          // Initial auth check in progress
  error: string | null;        // Any auth error message
  login: (redirectPath?: string) => Promise<void>;  // Redirect to SSO login (async in v2.0+)
  logout: () => Promise<void>; // Clear tokens and logout
  refreshUser: () => Promise<void>;  // Refresh user from token
}

User Object

interface User {
  id: string;
  email: string;
  firstName?: string;
  lastName?: string;
  displayName?: string;
  avatarUrl?: string;
  emailVerified: boolean;
  roles?: string[];
  permissions?: string[];
}

useAuthClient Hook

Returns a configured Axios instance that:

  • Automatically attaches Bearer token to all requests
  • Handles 401 responses by refreshing tokens and retrying
  • Queues concurrent requests during token refresh

Token Manager Functions

For advanced use cases, you can import token management functions directly:

import {
  getAccessToken,
  getRefreshToken,
  setTokens,
  clearTokens,
  isTokenExpired,
  hasValidToken,
  getUserFromToken,
  refreshTokens,
  getValidAccessToken,
  decodeJwt,
} from '@arow-software/auth-client';

// Check if token exists and is valid
if (hasValidToken()) {
  const user = getUserFromToken();
  console.log('Current user:', user);
}

// Manually refresh tokens
const newTokens = await refreshTokens();

// Decode JWT without verification
const payload = decodeJwt(token);

createApiClient Function

Create a standalone axios instance with auth interceptors:

import axios from 'axios';
import { createAuthClient, initTokenManager } from '@arow-software/auth-client';

// Initialize token manager
initTokenManager({
  ssoBaseUrl: 'https://sso.arowsoftware.co.uk',
  clientId: 'myapp',
});

// Add auth to existing axios instance
const myAxios = axios.create({ baseURL: 'http://api.example.com' });
const authAxios = createAuthClient(myAxios, {
  ssoBaseUrl: 'https://sso.arowsoftware.co.uk',
  clientId: 'myapp',
});

SSO Callback Setup

Create a callback route in your app to handle the SSO redirect:

// pages/callback.tsx (or wherever your callback route is)
import { useEffect } from 'react';
import { useAuth } from '@arow-software/auth-client';
import { useNavigate } from 'react-router-dom';

function CallbackPage() {
  const { isAuthenticated, isLoading } = useAuth();
  const navigate = useNavigate();

  useEffect(() => {
    // AuthProvider automatically handles:
    // - PKCE code exchange (v2.x): extracts code from query params, exchanges for tokens
    // - Implicit flow (v1.x): extracts tokens from URL hash (backward compatibility)
    // Just wait for auth to complete and redirect
    if (!isLoading) {
      if (isAuthenticated) {
        navigate('/dashboard');
      } else {
        navigate('/login?error=auth_failed');
      }
    }
  }, [isAuthenticated, isLoading, navigate]);

  return <div>Completing login...</div>;
}

Protected Routes

Example with React Router:

import { useAuth } from '@arow-software/auth-client';
import { Navigate, useLocation } from 'react-router-dom';

function ProtectedRoute({ children }: { children: React.ReactNode }) {
  const { isAuthenticated, isLoading } = useAuth();
  const location = useLocation();

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

  if (!isAuthenticated) {
    // Save the attempted location for redirect after login
    return <Navigate to="/login" state={{ from: location }} replace />;
  }

  return <>{children}</>;
}

// Usage
<Route 
  path="/dashboard" 
  element={
    <ProtectedRoute>
      <Dashboard />
    </ProtectedRoute>
  } 
/>

Role-Based Access

import { useAuth } from '@arow-software/auth-client';

function AdminPanel() {
  const { user, isAuthenticated } = useAuth();
  
  if (!isAuthenticated) return null;
  
  const isAdmin = user?.roles?.includes('admin');
  
  if (!isAdmin) {
    return <div>Access denied. Admin role required.</div>;
  }
  
  return <div>Admin Panel Content</div>;
}

Error Handling

<AuthProvider
  ssoBaseUrl="https://sso.arowsoftware.co.uk"
  clientId="arowtrades"
  onAuthError={(error) => {
    console.error('Auth error:', error);
    // Show notification, redirect to login, etc.
  }}
  onLogout={() => {
    // Redirect to home, clear app state, etc.
    window.location.href = '/';
  }}
>
  <App />
</AuthProvider>

Environment Variables

For Vite projects:

VITE_SSO_BASE_URL=https://sso.arowsoftware.co.uk
VITE_SSO_CLIENT_ID=arowtrades
VITE_API_BASE_URL=http://localhost:5001
<AuthProvider
  ssoBaseUrl={import.meta.env.VITE_SSO_BASE_URL}
  clientId={import.meta.env.VITE_SSO_CLIENT_ID}
  apiBaseUrl={import.meta.env.VITE_API_BASE_URL}
>

License

MIT © ArowSoftware