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

# Authentication

> How authentication works in the folksbase API using Supabase Auth and JWT tokens.

## Overview

The folksbase API uses [Supabase Auth](https://supabase.com/docs/guides/auth) for authentication. Clients obtain a JWT token from Supabase and pass it in the `Authorization` header on every request. The API validates the token server-side by calling `supabase.auth.getUser(token)`.

Authentication is not global — it's applied per-route. Public endpoints like the health check and OpenAPI spec don't require a token. All data endpoints (contacts, tags, imports, exports, settings, stats) do.

## How It Works

```mermaid theme={"dark"}
sequenceDiagram
    participant Client
    participant Supabase
    participant API

    Client->>Supabase: Sign in (email/password, OAuth, etc.)
    Supabase-->>Client: JWT access token + refresh token
    Client->>API: GET /api/contacts (Authorization: Bearer <token>)
    API->>Supabase: Validate token (getUser)
    Supabase-->>API: User object with metadata
    API-->>Client: 200 OK with contacts data
```

1. The client authenticates with Supabase using any supported method (email/password, magic link, OAuth)
2. Supabase returns a JWT access token and a refresh token
3. The client includes the access token in the `Authorization` header for API requests
4. The API middleware validates the token by calling Supabase's `getUser` endpoint
5. If valid, the middleware extracts `userId` and `workspaceId` from the user metadata and attaches them to the request context

## Passing the Token

Include the JWT in the `Authorization` header using the Bearer scheme:

```bash theme={"dark"}
curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  https://folksbase.onrender.com/api/contacts
```

The token must start with `Bearer ` (note the space). Requests without a valid header receive a `401` response.

## What the Middleware Extracts

When authentication succeeds, the middleware sets two values on the request context:

| Field         | Source                            | Description                        |
| ------------- | --------------------------------- | ---------------------------------- |
| `userId`      | `user.id`                         | The Supabase user ID (UUID)        |
| `workspaceId` | `user.user_metadata.workspace_id` | The workspace this user belongs to |

Route handlers access these via `c.get('user')`:

```typescript theme={"dark"}
const { userId, workspaceId } = c.get('user');
```

Every data query is scoped to the `workspaceId`, which is how folksbase implements workspace-level multi-tenancy. A user can only access contacts, tags, imports, and exports within their own workspace.

## Error Responses

| Status | Code           | When                                         |
| ------ | -------------- | -------------------------------------------- |
| `401`  | `UNAUTHORIZED` | Missing or malformed `Authorization` header  |
| `401`  | `UNAUTHORIZED` | Token is invalid or expired                  |
| `401`  | `UNAUTHORIZED` | User has no `workspace_id` in their metadata |

All auth errors follow the standard [error response format](/api/errors):

```json theme={"dark"}
{
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired token"
}
```

## Token Lifecycle

Supabase JWTs have a configurable expiry (default: 1 hour). The frontend uses Supabase's client library which handles token refresh automatically. If you're building a custom client:

* Store both the access token and refresh token
* Use the refresh token to obtain a new access token before the current one expires
* Handle `401` responses by refreshing the token and retrying the request

## Workspace Provisioning

When a new user signs up, a Supabase webhook fires to `/api/webhooks`. The webhook handler:

1. Creates a workspace for the user
2. Creates default settings for the workspace
3. Stores the `workspace_id` in the user's metadata
4. Queues a welcome email (sent after email confirmation)

This is all automatic — the client doesn't need to do anything beyond the initial sign-up.
