npm package: @siyamhosan/auth-system
Description: Minimal client for the auth-system API (session cookies + React helpers)
Author: Siyam Hosan
Latest version: 1.0.1

# @siyamhosan/auth-system

A comprehensive, multi-tenant authentication SDK for React, Svelte, Next.js, and server environments.

## Features

- 🔐 **Multi-tenant authentication** - Support for multiple applications with isolated databases
- 🎯 **Framework agnostic** - Works with React, Svelte, Next.js, and vanilla JavaScript
- 🔄 **Token management** - Automatic refresh token handling with family-based invalidation
- 🛡️ **Security first** - Argon2 password hashing, JWT secrets per tenant
- 🌐 **OAuth integration** - Built-in support for Google, GitHub, Discord, and more
- 📱 **Cross-platform** - Browser, Node.js, Deno, Bun support
- 🎨 **Plugin architecture** - Extensible authentication methods

## Installation

```bash
npm install @siyamhosan/auth-system
```

## Quick Start

### Universal (Framework Agnostic)

```typescript
import { createAuthClient } from '@siyamhosan/auth-system'

const client = createAuthClient({
  baseUrl: 'https://your-auth-server.com',
  clientId: 'your-app-id', // Your application ID
  apiKey: 'your-api-key'
})

// Login
const response = await client.login({
  email: 'user@example.com',
  password: 'password'
})

console.log('Logged in:', response.user)
```

### React

```tsx
import { AuthProvider, useAuth, useLogin } from '@siyamhosan/auth-system/react'

function App() {
  return (
    <AuthProvider config={{
      baseUrl: 'https://your-auth-server.com',
      clientId: 'your-app-id',
      apiKey: 'your-api-key'
    }}>
      <LoginForm />
    </AuthProvider>
  )
}

function LoginForm() {
  const { login, isLoading, error } = useLogin()
  const [credentials, setCredentials] = useState({ email: '', password: '' })

  const handleSubmit = async (e) => {
    e.preventDefault()
    try {
      await login(credentials)
      // Redirect to dashboard
    } catch (err) {
      console.error('Login failed:', err)
    }
  }

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={credentials.email}
        onChange={e => setCredentials(prev => ({ ...prev, email: e.target.value }))}
      />
      <input
        type="password"
        value={credentials.password}
        onChange={e => setCredentials(prev => ({ ...prev, password: e.target.value }))}
      />
      <button disabled={isLoading}>
        {isLoading ? 'Logging in...' : 'Login'}
      </button>
      {error && <div>{error.message}</div>}
    </form>
  )
}
```

### Svelte

```svelte
<script>
  import { createAuth, createAuthHelpers } from '@siyamhosan/auth-system/svelte'

  const auth = createAuth({
    baseUrl: 'https://your-auth-server.com',
    clientId: 'your-app-id',
    apiKey: 'your-api-key'
  })

  const { user, isAuthenticated, isLoading } = createAuthHelpers(auth)

  let email = ''
  let password = ''

  async function handleLogin() {
    try {
      await auth.login({ email, password })
    } catch (error) {
      console.error('Login failed:', error)
    }
  }
</script>

{#if $isLoading}
  <p>Loading...</p>
{:else if $isAuthenticated}
  <p>Welcome, {$user?.name}!</p>
  <button on:click={() => auth.logout()}>Logout</button>
{:else}
  <form on:submit|preventDefault={handleLogin}>
    <input bind:value={email} type="email" placeholder="Email" />
    <input bind:value={password} type="password" placeholder="Password" />
    <button type="submit">Login</button>
  </form>
{/if}
```

### Next.js

```tsx
// app/api/auth/login/route.ts
import { NextAuthAPI } from '@siyamhosan/auth-system/nextjs'

const authAPI = new NextAuthAPI({
  baseUrl: process.env.AUTH_SERVER_URL!,
  clientId: process.env.AUTH_CLIENT_ID!,
  apiKey: process.env.AUTH_API_KEY!
})

export async function POST(request: Request) {
  return authAPI.handleLogin(request)
}

// app/login/page.tsx
'use client'

import { useNextAuth } from '@siyamhosan/auth-system/nextjs'

export default function LoginPage() {
  const { login, isAuthenticated } = useNextAuth({
    baseUrl: process.env.NEXT_PUBLIC_AUTH_SERVER_URL!,
    clientId: process.env.NEXT_PUBLIC_AUTH_CLIENT_ID!,
    apiKey: process.env.NEXT_PUBLIC_AUTH_API_KEY!
  })

  // ... login form implementation
}
```

### Server-side (Node.js/Express)

```typescript
import { createServerAuth, createExpressMiddleware } from '@siyamhosan/auth-system/server'
import express from 'express'

const auth = createServerAuth({
  baseUrl: process.env.AUTH_SERVER_URL!,
  clientId: process.env.AUTH_CLIENT_ID!,
  apiKey: process.env.AUTH_API_KEY!,
  tokenCache: { enabled: true, ttl: 3600 },
  rateLimit: { enabled: true, maxRequests: 10, windowMs: 60000 }
})

const app = express()

// Authentication middleware
app.use('/api/protected', createExpressMiddleware(auth))

// Protected route
app.get('/api/protected/profile', (req, res) => {
  res.json({ user: req.user })
})

app.listen(3000)
```

## URL-based Client Identification

This SDK now uses direct URL-based client identification for cleaner, more RESTful APIs. Instead of complex headers or query parameters, your client ID is embedded directly in the URL path.

### How it Works

```typescript
// Client configuration
const client = createAuthClient({
  baseUrl: 'https://auth.example.com',
  clientId: 'my-app-123', // Your app ID
  apiKey: 'your-api-key'
})

// Requests are made to URLs like:
// POST https://auth.example.com/client/my-app-123/auth/login
// POST https://auth.example.com/client/my-app-123/auth/register
// GET  https://auth.example.com/client/my-app-123/auth/session

await client.login({ email, password })
```

### Benefits

- **Direct Access**: APIs are directly accessible without complex routing logic
- **RESTful**: Client identification is part of the resource path
- **Cache-Friendly**: Different clients get different cache keys
- **Simple**: No need to manage headers or query parameters for client identification
- **Backward Compatible**: Legacy tenant methods still work for existing integrations

### Migration from Tenant-based Configuration

If you're upgrading from the old tenant-based approach:

```typescript
// Old way (deprecated but still supported)
const client = createAuthClient({
  baseUrl: 'https://auth.example.com',
  apiKey: 'your-api-key',
  tenant: {
    type: 'site_id',
    value: 'my-app-123'
  }
})

// New way (recommended)
const client = createAuthClient({
  baseUrl: 'https://auth.example.com',
  clientId: 'my-app-123', // Direct from tenant.value
  apiKey: 'your-api-key'
})
```

The `clientId` should be your app's unique identifier (UUID or slug) that you get when you create an app in your auth service dashboard.

## Configuration

### AuthClientConfig

```typescript
interface AuthClientConfig {
  baseUrl: string           // Your auth server URL
  clientId: string         // Your application ID (required)
  apiKey: string           // API key for your application
  headers?: Record<string, string>  // Additional headers
  onTokenRefresh?: (tokens: AuthTokens) => void | Promise<void>
  onAuthError?: (error: AuthError) => void
}
```

## Authentication Methods

### Email/Password

```typescript
// Register
await client.register({
  email: 'user@example.com',
  username: 'username', // optional
  password: 'password',
  name: 'Display Name'   // optional
})

// Login
await client.login({
  email: 'user@example.com', // or username
  password: 'password'
})
```

### OAuth

```typescript
// Initiate OAuth flow
const { url } = await client.initiateOAuth('google', 'https://yourapp.com/callback')

// Redirect user to the URL
window.location.href = url
```

### Session Management

```typescript
// Check current session
const session = await client.getSession()

// Refresh tokens
await client.refreshTokens()

// Logout
await client.logout()
```

## Advanced Usage

### Custom Storage

```typescript
import { AuthClient } from '@siyamhosan/auth-system'

class CustomStorage {
  get(key: string) { /* implementation */ }
  set(key: string, value: string) { /* implementation */ }
  remove(key: string) { /* implementation */ }
  clear() { /* implementation */ }
}

const client = new AuthClient(config, new CustomStorage())
```

### Token Events

```typescript
const client = createAuthClient({
  baseUrl: '...',
  apiKey: '...',
  onTokenRefresh: (tokens) => {
    console.log('Tokens refreshed:', tokens)
    // Save to custom storage, update UI, etc.
  },
  onAuthError: (error) => {
    console.error('Auth error:', error)
    // Handle auth errors (redirect to login, etc.)
  }
})
```

## API Reference

### Core Classes

- `AuthClient` - Main authentication client
- `ServerAuth` - Server-side authentication manager

### React Hooks

- `useAuth()` - Access auth context
- `useUser()` - Get current user
- `useAuthState()` - Get auth state
- `useLogin()` - Login functionality
- `useRegister()` - Registration functionality
- `useLogout()` - Logout functionality
- `useOAuth()` - OAuth functionality

### Svelte Stores

- `createAuth()` - Create auth store
- `createAuthHelpers()` - Helper functions for auth store

### Next.js Utilities

- `createNextAuthClient()` - Next.js compatible client
- `NextAuthServer` - Server-side utilities
- `NextAuthAPI` - API route handlers

## Security Features

- **Token Rotation** - Automatic refresh token rotation
- **Family-based Revocation** - Invalidate entire token families
- **Rate Limiting** - Built-in rate limiting for server environments
- **Secure Cookies** - HTTP-only cookies in Next.js
- **Argon2 Hashing** - Server-side password hashing
- **JWT per Tenant** - Isolated JWT secrets per application

## Contributing

1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request

## License

MIT © Siyam Hosan

## Support

For questions and support, please open an issue on GitHub.