@arow-software/auth-client
v3.3.1
Published
Reusable auth package for ArowAuth SSO integration
Downloads
826
Maintainers
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) toresponse_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 nowasync(returnsPromise<void>)
Migration Guide
Before (v1.x):
const { login } = useAuth();
login('/dashboard'); // SynchronousAfter (v2.x):
const { login } = useAuth();
await login('/dashboard'); // Now async
// OR
login('/dashboard'); // Fire-and-forget still worksBackward 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-clientPeer Dependencies
Make sure you have these installed:
npm install react axiosQuick 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
