# @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
```bash
pnpm add @lilith/auth-provider
```
## Usage
### 1. Wrap your app with AuthProvider
```tsx
import { AuthProvider } from '@lilith/auth-provider';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient();
function App() {
return (
);
}
```
### 2. Use the useAuth hook
```tsx
import { useAuth } from '@lilith/auth-provider';
function MyComponent() {
const { user, isAuthenticated, isLoading, login, logout } = useAuth();
if (isLoading) {
return
Loading...
;
}
if (!isAuthenticated) {
return (
);
}
return (
Welcome, {user.username}!
);
}
```
## 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` - Log in a user
- `register(data)`: `Promise` - Register a new user
- `logout()`: `Promise` - Log out current user
- `refreshAuth()`: `Promise` - 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
```tsx
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 (
);
}
```
## License
MIT