> ## Documentation Index
> Fetch the complete documentation index at: https://docs.buildwithtrace.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication

> Shared Trace JWT authentication for the Symbols and Footprints platforms — token storage, login, refresh, fetchWithAuth, GitHub OAuth, and plan gating.

Both platforms share the **same Trace account system** as the main app (same Supabase
project, same JWT). A token minted anywhere in the Trace ecosystem works on either site.

## Token storage

`src/lib/auth.ts` keeps tokens in `localStorage`:

| Key                   | Purpose                      |
| --------------------- | ---------------------------- |
| `trace_access_token`  | Short-lived JWT access token |
| `trace_refresh_token` | Long-lived refresh token     |

```ts theme={null}
import {
  getAccessToken, getRefreshToken, setTokens, clearTokens,
  isAuthenticated, fetchWithAuth, BACKEND_URL,
} from "@/lib/auth";
```

`BACKEND_URL` resolves to `process.env.NEXT_PUBLIC_BACKEND_URL || "https://api.buildwithtrace.com"`.

## Email + password login

The login page POSTs directly to the backend, then stores the returned tokens:

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.buildwithtrace.com/api/v1/auth/login \
    -H "Content-Type: application/json" \
    -d '{"email":"you@example.com","password":"••••••","captcha_token":""}'
  ```

  ```ts TypeScript theme={null}
  const res = await fetch(`${BACKEND_URL}/api/v1/auth/login`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ email, password, captcha_token: "" }),
  });
  const data = await res.json();
  const token = data.access_token ?? data.session?.access_token;
  const refresh = data.refresh_token ?? data.session?.refresh_token ?? "";
  setTokens(token, refresh);
  ```

  ```json Response theme={null}
  { "access_token": "eyJ…", "refresh_token": "…" }
  ```
</CodeGroup>

## GitHub OAuth

`handleGitHubLogin()` redirects to the backend, passing a `callback` that tells it which
site to return to:

```ts theme={null}
window.location.href =
  `${BACKEND_URL}/api/v1/auth/github/login?callback=symbols`;     // symbols site
  // …?callback=footprints  on the footprints site
```

<Warning>
  The footprints site sends `callback=footprints`. The backend must have that callback
  registered for `footprints.buildwithtrace.com` or GitHub OAuth will fail there.
  Email/password login works regardless.
</Warning>

## Authenticated requests & auto-refresh

`fetchWithAuth(url, options)` attaches the bearer token and, on a `401`, transparently
refreshes via `POST /api/v1/auth/refresh` and retries once:

```ts theme={null}
const res = await fetchWithAuth("/api/my-symbols?type=symbol", { method: "GET" });
```

Refresh flow:

```json theme={null}
POST /api/v1/auth/refresh   { "refresh_token": "…" }
→ 200                       { "access_token": "eyJ…", "refresh_token": "…" }
```

If refresh fails, `clearTokens()` is called and the user is signed out.

## Which endpoints require auth

<ResponseField name="Public (no token)" type="GET">
  Browse, search, semantic search, suggestions, library detail, categories, thumbnails,
  download, community list, comment **reads**, rating **reads**, health.
</ResponseField>

<ResponseField name="Authenticated (Bearer JWT)" type="POST / PATCH / DELETE">
  Generate, generate/save, contribute/submit, my-symbols / my-footprints (GET/PATCH/DELETE),
  posting comments, posting ratings.
</ResponseField>

## Plan gating

Generation is plan-restricted on the backend. Proxy routes surface this directly:

| Status        | Meaning                                                   |
| ------------- | --------------------------------------------------------- |
| `401`         | Missing/expired token — sign in again                     |
| `402` / `403` | Plan/quota does not include generation (upgrade required) |
| `502` / `504` | Backend error / generation timeout                        |

The site renders an "Upgrade required" state on `402/403` and links to pricing.
