npm package: @glitch_protocol/auth-react
Description: React hooks and components for glitch_protocol
Author: drcount-root
Latest version: 0.2.0

# @glitch_protocol/auth-react

React hooks and components for glitch_protocol. Provides a context provider, session management hooks, and pre-built UI components for displaying active sessions.

## Install

```bash
npm install @glitch_protocol/auth-react @glitch_protocol/auth-client react react-dom socket.io-client zustand
```

---

## Setup

### 1. Create auth client and store (see [`@glitch_protocol/auth-client`](../client/README.md))

```ts
// lib/auth.ts
import { createglitch_protocolClient, createAuthStore, createSocketClient } from "@glitch_protocol/auth-client";

let getToken = () => null as string | null;
let onRefreshed = (t: string) => {};
let onCleared = () => {};

export const client = createglitch_protocolClient({
  baseUrl: "http://localhost:4000",
  getAccessToken: () => getToken(),
  onTokenRefreshed: (t) => onRefreshed(t),
  onAuthCleared: () => onCleared(),
});

export const useAuthStore = createAuthStore({ client });
getToken = () => useAuthStore.getState().accessToken;
onRefreshed = (t) => useAuthStore.getState().setAccessToken(t);
onCleared = () => useAuthStore.getState().clearAuth();

export const socketClient = createSocketClient({
  serverUrl: "http://localhost:4000",
  getAccessToken: () => useAuthStore.getState().accessToken,
});
```

### 2. Wrap your app with glitch_protocolProvider

```tsx
// app.tsx
import { glitch_protocolProvider } from "@glitch_protocol/auth-react";
import { useAuthStore, socketClient } from "./lib/auth";

export default function App() {
  return (
    <glitch_protocolProvider store={useAuthStore} socketClient={socketClient}>
      <Dashboard />
    </glitch_protocolProvider>
  );
}
```

---

## Hooks

### useAuth

Access auth state and operations.

```ts
function useAuth(): {
  user: AuthUser | null;
  accessToken: string | null;
  isLoading: boolean;
  isInitialized: boolean;
  login: (email: string, password: string) => Promise<{ success: boolean; error?: string }>;
  register: (name: string, email: string, password: string) => Promise<{ success: boolean; error?: string }>;
  logout: () => Promise<void>;
}
```

**Example:**
```tsx
function Dashboard() {
  const { user, isLoading, isInitialized, logout } = useAuth();

  if (!isInitialized) return <Spinner />;
  if (!user) return <LoginForm />;

  return (
    <div>
      <p>Welcome, {user.name}</p>
      <button onClick={logout}>Sign out</button>
    </div>
  );
}
```

**login / register:**
Both return `{ success: true }` on success or `{ success: false, error: "message" }` on failure. Automatically set auth state on success.

```tsx
function LoginForm() {
  const { login } = useAuth();

  const handleSubmit = async (email: string, password: string) => {
    const result = await login(email, password);
    if (result.success) {
      // Redirect to dashboard (done by state change)
    } else {
      toast.error(result.error);
    }
  };

  return <form onSubmit={handleSubmit}>...</form>;
}
```

---

### useSessions

Access active sessions and termination.

```ts
function useSessions(): {
  sessions: SessionInfo[];
  terminateSession: (sessionId: string) => Promise<void>;
  terminateOtherSessions: () => Promise<void>;
}
```

**Example:**
```tsx
function SessionManager() {
  const { sessions, terminateSession, terminateOtherSessions } = useSessions();

  return (
    <div>
      <h2>Your Devices</h2>
      {sessions.map((session) => (
        <div key={session.id}>
          <p>{session.browser} on {session.os}</p>
          <p>{session.ipAddress}</p>
          <p>{new Date(session.lastActivity).toLocaleString()}</p>
          <button onClick={() => terminateSession(session.id)}>
            Sign out
          </button>
        </div>
      ))}
      <button onClick={terminateOtherSessions}>
        Sign out all other devices
      </button>
    </div>
  );
}
```

---

### useSocket

Access the Socket.IO client for custom event listeners.

```ts
function useSocket(): {
  socket: Socket | null;
  isConnected: boolean;
  getSocket: () => Socket | null;
}
```

**Example:**
```tsx
function ConnectionStatus() {
  const { isConnected, socket } = useSocket();

  useEffect(() => {
    if (!socket) return;

    socket.on("custom:event", (data) => {
      console.log("Received:", data);
    });

    return () => socket.off("custom:event");
  }, [socket]);

  return <span>{isConnected ? "🟢 Online" : "🔴 Offline"}</span>;
}
```

---

## Components

### SessionCard

Displays a single session with termination button.

```tsx
interface SessionCardProps {
  session: SessionInfo;
  onTerminate?: (sessionId: string) => Promise<void>;
  isCurrent?: boolean;
}

export function Dashboard() {
  const { sessions, terminateSession } = useSessions();

  return (
    <>
      {sessions.map((session) => (
        <SessionCard
          key={session.id}
          session={session}
          isCurrent={session.id === currentSessionId}
          onTerminate={terminateSession}
        />
      ))}
    </>
  );
}
```

**Rendered:**
```
┌─ Chrome on Windows ──────────────────┐
│ 192.168.1.100                        │
│ Last activity: 2 minutes ago          │
│ [Signed in 3 days ago]       [Sign out] │
└──────────────────────────────────────┘
```

---

### SessionManager

Pre-built session management UI with list and bulk actions.

```tsx
export function SettingsPage() {
  return (
    <div>
      <h1>Settings</h1>
      <SessionManager />
    </div>
  );
}
```

**Features:**
- Lists all active sessions
- Marks current session as "This device"
- Button to terminate each session individually
- Button to "Sign out all other devices"
- Real-time updates when new session created or terminated

---

## Real-Time Events

The provider automatically connects to Socket.IO and listens for events:

- **`session:new`** — New login detected on another device
- **`session:terminated`** — A session was terminated
- **`session:list-updated`** — Session list changed (e.g., activity update)

State is automatically updated when these events arrive. No manual handling needed.

**Custom listeners:**
```tsx
function MyComponent() {
  const { socket } = useSocket();

  useEffect(() => {
    if (!socket) return;

    socket.on("session:new", (session) => {
      toast.info(`New login from ${session.browser}`);
    });

    return () => socket.off("session:new");
  }, [socket]);

  return null;
}
```

---

## Example: Complete App

```tsx
// app.tsx
import { useEffect } from "react";
import { glitch_protocolProvider, useAuth, useSessions } from "@glitch_protocol/auth-react";
import { useAuthStore, socketClient } from "./lib/auth";

function App() {
  return (
    <glitch_protocolProvider store={useAuthStore} socketClient={socketClient}>
      <Layout />
    </glitch_protocolProvider>
  );
}

function Layout() {
  const initialize = useAuthStore((s) => s.initialize);
  const isInitialized = useAuthStore((s) => s.isInitialized);

  useEffect(() => {
    initialize();
  }, [initialize]);

  if (!isInitialized) return <Spinner />;

  return (
    <div>
      <Header />
      <Router>
        <Route path="/" element={<Dashboard />} />
        <Route path="/settings" element={<Settings />} />
      </Router>
    </div>
  );
}

function Header() {
  const { user, logout } = useAuth();

  return (
    <nav>
      <div>Hello, {user?.name}</div>
      <button onClick={logout}>Sign out</button>
    </nav>
  );
}

function Dashboard() {
  const { user } = useAuth();
  const { sessions } = useSessions();

  return (
    <div>
      <h1>Dashboard</h1>
      <p>You have {sessions.length} active session(s)</p>
      {sessions.map((session) => (
        <SessionCard key={session.id} session={session} />
      ))}
    </div>
  );
}

function Settings() {
  return (
    <div>
      <h1>Settings</h1>
      <SessionManager />
    </div>
  );
}

export default App;
```

---

## Peer Dependencies

- `react: >=18.0.0` — React library
- `react-dom: >=18.0.0` — React DOM library
- `@glitch_protocol/auth-client: ^0.1.0` — Browser SDK
- `socket.io-client: ^4.8.0` — Real-time events
- `zustand: ^5.0.0` — State management

## Node Version

**ESM-only.** Requires Node.js >=18.