platform-codebase/@packages/@providers/auth-provider
2026-01-25 11:33:37 -08:00
..
src
package.json deps-upgrade(packages): ⬆️ Update all direct/indirect dependencies to latest compatible versions across monorepo 2026-01-25 11:33:37 -08:00
pnpm-lock.yaml
README.md
tsconfig.json
vitest.config.ts

@lilith/auth-provider

Shared authentication provider for React applications with SSO (Single Sign-On) support across multiple deployments.

Features

  • SSO Support: Login once, authenticated across all deployments
  • Cross-Tab Sync: Authentication state synchronized across browser tabs
  • Token Management: Automatic JWT token refresh and storage
  • React Query Integration: Optimized data fetching and caching
  • TypeScript: Full type safety

Installation

pnpm add @lilith/auth-provider

Usage

1. Wrap your app with AuthProvider

import { AuthProvider } from '@lilith/auth-provider';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

const queryClient = new QueryClient();

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <AuthProvider
        apiUrl="http://localhost:4000"
        handle401Redirects={true}
        loginRoute="/login"
      >
        <YourApp />
      </AuthProvider>
    </QueryClientProvider>
  );
}

2. Use the useAuth hook

import { useAuth } from '@lilith/auth-provider';

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

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

  if (!isAuthenticated) {
    return (
      <button onClick={() => login({ email: 'user@example.com', password: 'pass' })}>
        Login
      </button>
    );
  }

  return (
    <div>
      <p>Welcome, {user.username}!</p>
      <button onClick={logout}>Logout</button>
    </div>
  );
}

API

AuthProvider Props

Prop Type Default Description
apiUrl string 'http://localhost:4000' API base URL
handle401Redirects boolean true Auto-redirect on 401
loginRoute string '/login' Login page route

useAuth() Hook

Returns an object with:

State

  • user: User | null - Current authenticated user
  • isLoading: boolean - Loading state
  • isAuthenticated: boolean - Whether user is logged in
  • error: Error | null - Authentication error

Methods

  • login(credentials): Promise<void> - Log in a user
  • register(data): Promise<void> - Register a new user
  • logout(): Promise<void> - Log out current user
  • refreshAuth(): Promise<void> - Refresh authentication state

How SSO Works

  1. Login: User logs in at any deployment (e.g., /fanclub)
  2. Token Storage: JWT tokens stored in localStorage (domain-scoped)
  3. Cross-Tab Sync: BroadcastChannel + storage events sync auth state
  4. Automatic: Navigate to another deployment (e.g., /tip-menu) → already authenticated

Architecture

┌─────────────────────────────────────────────────────────────┐
│                      AuthProvider                            │
│  ┌────────────────────────────────────────────────────────┐ │
│  │  React Query (User State)                              │ │
│  └────────────────────────────────────────────────────────┘ │
│  ┌────────────────────────────────────────────────────────┐ │
│  │  auth-storage (localStorage)                           │ │
│  │  - auth_token                                           │ │
│  │  - refresh_token                                        │ │
│  └────────────────────────────────────────────────────────┘ │
│  ┌────────────────────────────────────────────────────────┐ │
│  │  auth-events (Cross-Tab Sync)                          │ │
│  │  - BroadcastChannel API                                │ │
│  │  - localStorage events                                  │ │
│  └────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
          ↓                    ↓                    ↓
    useAuth()              useAuth()            useAuth()
    (Tab 1)                (Tab 2)              (Tab 3)

Example: Login Flow

import { useAuth } from '@lilith/auth-provider';

function LoginPage() {
  const { login, isLoading, error } = useAuth();
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    try {
      await login({ email, password });
      // User is now logged in, AuthProvider will handle state
      navigate('/dashboard');
    } catch (err) {
      console.error('Login failed:', err);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Email"
      />
      <input
        type="password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
        placeholder="Password"
      />
      <button type="submit" disabled={isLoading}>
        {isLoading ? 'Logging in...' : 'Login'}
      </button>
      {error && <p>Error: {error.message}</p>}
    </form>
  );
}

License

MIT