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

# Authentication

> Token-based authentication for Jean API

## Overview

Jean uses token-based authentication to secure HTTP and WebSocket connections. Authentication can be optionally enabled/disabled via server configuration.

## Token Generation

Tokens are cryptographically random 32-byte strings encoded with base64url:

```rust theme={null}
fn generate_token() -> String {
    let mut bytes = [0u8; 32];
    rand::thread_rng().fill(&mut bytes);
    base64::Engine::encode(&base64::engine::general_purpose::URL_SAFE_NO_PAD, bytes)
}
```

**Example token**: `Kj8vN2Qw1xYzM4P7R6Sf9T3Uc0Vd5We8Xf1Yg4Zh7Ai2Bj`

## Server Configuration

Authentication is configured when starting the HTTP server:

<ParamField path="token" type="string" required>
  The authentication token (generated automatically)
</ParamField>

<ParamField path="token_required" type="boolean" default={true}>
  Whether authentication is enforced. Set to `false` to disable authentication.
</ParamField>

## Authentication Methods

### HTTP Query Parameter

Provide the token as a query parameter:

```bash theme={null}
curl "http://127.0.0.1:8420/api/init?token=YOUR_TOKEN"
```

### WebSocket Query Parameter

Include the token when upgrading to WebSocket:

```javascript theme={null}
const ws = new WebSocket('ws://127.0.0.1:8420/ws?token=YOUR_TOKEN');
```

## Endpoints

### Validate Token

<CodeGroup>
  ```bash cURL theme={null}
  curl "http://127.0.0.1:8420/api/auth?token=YOUR_TOKEN"
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch(
    'http://127.0.0.1:8420/api/auth?token=YOUR_TOKEN'
  );
  const result = await response.json();
  ```

  ```python Python theme={null}
  import requests

  response = requests.get(
      'http://127.0.0.1:8420/api/auth',
      params={'token': 'YOUR_TOKEN'}
  )
  result = response.json()
  ```
</CodeGroup>

<ResponseField name="ok" type="boolean" required>
  `true` if the token is valid, `false` otherwise
</ResponseField>

<ResponseField name="token_required" type="boolean">
  Whether authentication is enabled on the server. Only present when `token_required = false`.
</ResponseField>

<ResponseField name="error" type="string">
  Error message when token validation fails. Only present on failure.
</ResponseField>

**Success Response (200)**

```json theme={null}
{
  "ok": true
}
```

**Success Response (No Auth Required)**

```json theme={null}
{
  "ok": true,
  "token_required": false
}
```

**Error Response (401)**

```json theme={null}
{
  "ok": false,
  "error": "Invalid token"
}
```

## WebSocket Authentication

### Connection Flow

1. **Upgrade Request**: Client sends HTTP GET to `/ws?token=YOUR_TOKEN`
2. **Validation**: Server validates the token (if `token_required = true`)
3. **Upgrade**: On success, connection is upgraded to WebSocket
4. **Event Stream**: Client receives real-time events

### JavaScript Example

```javascript theme={null}
class JeanAPI {
  constructor(baseUrl, token) {
    this.baseUrl = baseUrl;
    this.token = token;
    this.ws = null;
    this.pending = new Map();
  }

  async connect() {
    return new Promise((resolve, reject) => {
      const wsUrl = this.baseUrl.replace('http', 'ws');
      this.ws = new WebSocket(`${wsUrl}/ws?token=${this.token}`);

      this.ws.onopen = () => {
        console.log('Connected to Jean API');
        resolve();
      };

      this.ws.onerror = (error) => {
        console.error('WebSocket error:', error);
        reject(error);
      };

      this.ws.onmessage = (event) => {
        const message = JSON.parse(event.data);
        this.handleMessage(message);
      };

      this.ws.onclose = () => {
        console.log('Disconnected from Jean API');
      };
    });
  }

  handleMessage(message) {
    if (message.type === 'event') {
      // Handle real-time event
      this.emit(message.event, message.payload);
    } else if (message.type === 'response' || message.type === 'error') {
      // Handle command response
      const callback = this.pending.get(message.id);
      if (callback) {
        this.pending.delete(message.id);
        if (message.type === 'response') {
          callback.resolve(message.data);
        } else {
          callback.reject(new Error(message.error));
        }
      }
    }
  }

  async invoke(command, args = {}) {
    const id = crypto.randomUUID();
    return new Promise((resolve, reject) => {
      this.pending.set(id, { resolve, reject });
      this.ws.send(JSON.stringify({ id, command, args }));
    });
  }
}

// Usage
const api = new JeanAPI('http://127.0.0.1:8420', 'YOUR_TOKEN');
await api.connect();

// Now you can invoke commands
const projects = await api.invoke('list_projects');
```

### Python Example

```python theme={null}
import asyncio
import websockets
import json
import uuid

class JeanAPI:
    def __init__(self, base_url, token):
        self.base_url = base_url
        self.token = token
        self.ws = None
        self.pending = {}
        self.event_handlers = {}

    async def connect(self):
        ws_url = self.base_url.replace('http', 'ws')
        self.ws = await websockets.connect(f'{ws_url}/ws?token={self.token}')
        asyncio.create_task(self._receive_loop())

    async def _receive_loop(self):
        async for message in self.ws:
            data = json.loads(message)
            if data['type'] == 'event':
                handler = self.event_handlers.get(data['event'])
                if handler:
                    handler(data['payload'])
            elif data['type'] in ('response', 'error'):
                future = self.pending.pop(data['id'], None)
                if future:
                    if data['type'] == 'response':
                        future.set_result(data['data'])
                    else:
                        future.set_exception(Exception(data['error']))

    async def invoke(self, command, args=None):
        request_id = str(uuid.uuid4())
        future = asyncio.Future()
        self.pending[request_id] = future
        await self.ws.send(json.dumps({
            'id': request_id,
            'command': command,
            'args': args or {}
        }))
        return await future

    def on(self, event_name, handler):
        self.event_handlers[event_name] = handler

# Usage
async def main():
    api = JeanAPI('http://127.0.0.1:8420', 'YOUR_TOKEN')
    await api.connect()
    
    # Listen for events
    api.on('project:updated', lambda payload: print('Project updated:', payload))
    
    # Invoke commands
    projects = await api.invoke('list_projects')
    print(f'Found {len(projects)} projects')

asyncio.run(main())
```

## Token Validation

The server uses constant-time comparison to prevent timing attacks:

```rust theme={null}
fn validate_token(provided: &str, expected: &str) -> bool {
    if provided.len() != expected.len() {
        return false;
    }
    provided
        .as_bytes()
        .iter()
        .zip(expected.as_bytes().iter())
        .fold(0u8, |acc, (a, b)| acc | (a ^ b))
        == 0
}
```

## Security Considerations

<AccordionGroup>
  <Accordion title="Transport Security">
    The API currently uses unencrypted HTTP/WebSocket. For production deployments:

    * Use a reverse proxy with TLS (nginx, Caddy)
    * Enable `localhost_only` mode for local development
    * Consider implementing HTTPS support in the server
  </Accordion>

  <Accordion title="Token Storage">
    * Store tokens securely (environment variables, secure config files)
    * Never commit tokens to version control
    * Rotate tokens periodically
    * Use different tokens for different environments
  </Accordion>

  <Accordion title="Network Exposure">
    When `localhost_only = false`, the server is accessible on the LAN:

    * Ensure firewall rules are configured properly
    * Use strong, random tokens
    * Consider IP allowlisting
  </Accordion>

  <Accordion title="Disabling Authentication">
    Setting `token_required = false` disables all authentication:

    * Only use in trusted environments
    * Never expose to the internet without authentication
    * Useful for internal tools and testing
  </Accordion>
</AccordionGroup>

## Error Codes

| Status                      | Description                                  |
| --------------------------- | -------------------------------------------- |
| `200 OK`                    | Token is valid or authentication is disabled |
| `401 Unauthorized`          | Token is missing, empty, or invalid          |
| `500 Internal Server Error` | Server-side error during validation          |

## Next Steps

<CardGroup cols={2}>
  <Card title="API Overview" icon="book" href="/api/overview">
    Learn about the API architecture
  </Card>

  <Card title="Projects API" icon="folder-tree" href="/api/projects">
    Start managing projects
  </Card>
</CardGroup>
