# Authentication

**Page:** kit/exec/authentication

[Download Raw Markdown](./kit/exec/authentication.md)

---

# Authentication

**Add one comment. Your endpoint is protected.** No middleware, no auth library, no JWT infrastructure. Hoody Exec's `@token` magic comment adds shared-secret authentication to any script — HTTP and WebSocket.

```javascript
// @token my-secret-key-123

return { data: 'only authenticated requests see this' };
```

Unauthenticated requests get `401 Unauthorized`. Authenticated requests run your script normally. That's it.

---

## How It Works

1. You add `// @token <secret>` at the top of your script
2. Every incoming request is checked for a matching token **before** your code runs
3. If the token matches → script executes normally
4. If the token is missing or wrong → `401 Unauthorized` (your code never runs)

The token check happens at the server level, before VM creation and metadata construction. There is no way for a script to see or override the gate.

---

## Credential Methods

Clients can provide the token four different ways. All are checked automatically — the first match wins.

### Priority Order

| Priority | Method | Header / Parameter |
|----------|--------|--------------------|
| 1a | **Bearer token** | `Authorization: Bearer <token>` |
| 1b | **Basic auth** (password field) | `Authorization: Basic base64(user:token)` |
| 2 | **X-Token header** | `X-Token: <token>` |
| 3 | **Query parameter** | `?token=<token>` |

If multiple sources are present, only the highest-priority one is used. For example, if both `Authorization: Bearer` and `X-Token` are sent, only the Bearer value is checked.

---

### Bearer Token

The standard approach for API clients and SDKs.



```bash
curl -H "Authorization: Bearer my-secret-key-123" \
  "https://PROJECT-CONTAINER-exec-1.SERVER.containers.hoody.icu/api/data"
```


```javascript
const res = await fetch('https://your-endpoint.containers.hoody.icu/api/data', {
  headers: { 'Authorization': 'Bearer my-secret-key-123' }
});
```


```python
import requests
res = requests.get('https://your-endpoint.containers.hoody.icu/api/data',
    headers={'Authorization': 'Bearer my-secret-key-123'})
```



The scheme name is case-insensitive (`Bearer`, `bearer`, `BEARER` all work).

---

### Basic Auth

The `@token` value is matched against the **password** field of HTTP Basic auth. The username is completely ignored — send anything or nothing.

This means `@token` works out-of-the-box with:
- `curl -u` flag
- Browser native auth dialogs
- HTTP clients that only support Basic auth
- Legacy systems and webhook integrations



```bash
# Username "admin", password is the token — username is ignored
curl -u admin:my-secret-key-123 \
  "https://PROJECT-CONTAINER-exec-1.SERVER.containers.hoody.icu/api/data"
```


```bash
# No username, just the token as password
curl -u :my-secret-key-123 \
  "https://PROJECT-CONTAINER-exec-1.SERVER.containers.hoody.icu/api/data"
```


```bash
# Any username works — only the password matters
curl -u monitoring-bot:my-secret-key-123 \
  "https://PROJECT-CONTAINER-exec-1.SERVER.containers.hoody.icu/api/data"
```




Basic auth is transmitted as `base64(username:password)`. The server decodes it, finds the colon, and extracts everything after it as the token. Passwords containing colons work fine — only the **first** colon is used as the delimiter.


---

### X-Token Header

A simple custom header — useful when `Authorization` is reserved by a proxy or gateway.

```bash
curl -H "X-Token: my-secret-key-123" \
  "https://PROJECT-CONTAINER-exec-1.SERVER.containers.hoody.icu/api/data"
```

---

### Query Parameter

Pass the token in the URL. Convenient for quick testing, webhook callbacks, and browser links.

```bash
curl "https://PROJECT-CONTAINER-exec-1.SERVER.containers.hoody.icu/api/data?token=my-secret-key-123"
```


Query parameters appear in server logs, browser history, and Referer headers. For production use, prefer `Authorization: Bearer` or `X-Token` headers. Hoody Exec redacts `?token=` from all logs, but external proxies and CDNs may not.


---

## Rejection Response

When authentication fails, the server returns:

```json
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="hoody-exec"
Content-Type: application/json

{
  "error": "Unauthorized",
  "message": "This endpoint requires authentication. Provide a valid token via Authorization: Bearer <token> header, X-Token header, ?token= query parameter, or HTTP Basic Auth."
}
```

- **`WWW-Authenticate: Bearer`** — triggers native auth dialogs in browsers
- Same response body whether the token is missing or wrong (no information leakage)
- If CORS is configured on the script (`@cors`), CORS headers are included in the 401 response

---

## CORS + Token

When using `@token` with `@cors`, CORS preflight requests (`OPTIONS`) are handled **before** the token check:

```javascript
// @token my-secret-key-123
// @cors reflective

return { data: 'protected + CORS-enabled' };
```

**Why:** Browsers automatically send an `OPTIONS` preflight before cross-origin requests. Preflight requests cannot carry credentials — that's the HTTP spec. If the server required a token on the preflight, CORS would be completely broken.

**What happens:**
1. Browser sends `OPTIONS` with `Origin` + `Access-Control-Request-Method` → server returns `204` with CORS headers (no token required)
2. Browser sends the real `GET`/`POST` with `Authorization: Bearer <token>` → server checks token, runs script

This is spec-correct behavior, not a bypass.

---

## WebSocket Authentication

The `@token` gate also protects WebSocket connections. The check runs on the HTTP `upgrade` request before the WebSocket handshake completes.

```javascript
// @token my-secret-key-123
// @mode worker
// @websocket

ws.on('message', (socket, data) => socket.send('echo:' + data));
```

**Connect with token:**



```javascript
// Works in all WebSocket clients (including browsers)
const ws = new WebSocket('wss://your-endpoint.containers.hoody.icu/ws?token=my-secret-key-123');
```


```javascript
// Node.js / Bun (browsers don't allow custom WebSocket headers)
const ws = new WebSocket('wss://your-endpoint.containers.hoody.icu/ws', {
  headers: { 'Authorization': 'Bearer my-secret-key-123' }
});
```



Without a valid token, the server writes `HTTP/1.1 401 Unauthorized` to the socket and closes the connection. The WebSocket `onerror` / `onclose` event fires on the client.


Browser WebSocket APIs do not support custom headers. For browser clients, use `?token=` in the WebSocket URL. For server-to-server connections, Bearer or X-Token headers work.


---

## Security Details

### Constant-Time Comparison

Token comparison uses SHA-256 hashing on both sides followed by `crypto.timingSafeEqual`:

```
SHA-256(provided) === SHA-256(expected)  // constant-time
```

This prevents timing attacks — the comparison takes the same amount of time regardless of how many characters match.

### Token Redaction

The token value is **never exposed** in any API response or log:

| Surface | Redaction |
|---------|-----------|
| Access logs | `?token=` replaced with `?token=[REDACTED]` |
| Referer headers in logs | `?token=` redacted |
| Scripts API (`/api/v1/exec/scripts/read`) | Raw content shows `// @token [REDACTED]` |
| Magic Comments API | `token` field returns `[REDACTED]` |
| `metadata.parameters` in your script | `?token=` query param is **removed** before your code runs |
| Encoded bypass attempts (`%74oken=`) | Caught by URL-parser fallback and redacted |

### Token Isolation

When a client authenticates via `?token=`, the token is immediately stripped from the request URL. Your script never sees it:

```javascript
// @token my-secret

// Client calls: /api/data?token=my-secret&page=2
// Your script sees:
metadata.parameters  // → { page: "2" }  (no "token" key)
metadata.path        // → /api/data       (clean)
```

---

## Examples

### Protect an API endpoint

```javascript
// @token sk_prod_a8f2e9c1d4b6
// @cors reflective

const userId = metadata.parameters.id;
const user = await db.getUser(userId);
return user;
```

### Webhook receiver with token

```javascript
// @token whsec_github_abc123

if (req.method !== 'POST') {
  res.statusCode = 405;
  return { error: 'Method Not Allowed' };
}

const payload = JSON.parse(await req.text());
await processWebhook(payload);
return { received: true };
```

```bash
# GitHub webhook configured with:
# URL: https://your-endpoint.containers.hoody.icu/webhooks/github?token=whsec_github_abc123
```

### Token + Worker mode + WebSocket

```javascript
// @token realtime-secret-456
// @mode worker
// @websocket
// @cors reflective

if (!shared.connections) shared.connections = new Set();

ws.on('open', (socket) => shared.connections.add(socket));
ws.on('close', (socket) => shared.connections.delete(socket));
ws.on('message', (socket, data) => {
  // Broadcast to all connected clients
  for (const client of shared.connections) {
    if (client !== socket) client.send(data);
  }
});
```

### Programmatic management


  
    ```bash
    # Read magic comments (token is redacted in response)
    hoody exec magic-comments read --path "api/data.ts"
    # → { "token": "[REDACTED]", "cors": "reflective" }
    ```
  
  
    ```typescript
    const containerClient = await client.withContainer({
      id: CONTAINER_ID, project_id: PROJECT_ID, server: SERVER
    });

    // Read magic comments (token is redacted)
    const comments = await containerClient.exec.magic.read({ path: 'api/data.ts' });
    console.log(comments.data.comments); // { token: "[REDACTED]", cors: "reflective" }

    // Update the token
    await containerClient.exec.magic.updateHandler({
      path: 'api/data.ts',
      comments: '{"token": "new-secret-key"}'
    });
    ```
  
  
    ```bash
    # Read magic comments (token is redacted in response)
    curl "https://PROJECT-CONTAINER-exec-1.SERVER.containers.hoody.icu/api/v1/exec/magic-comments/read?path=api/data.ts"
    # → { "comments": { "token": "[REDACTED]", "cors": "reflective" } }

    # Update token via API
    curl -X PUT "https://PROJECT-CONTAINER-exec-1.SERVER.containers.hoody.icu/api/v1/exec/magic-comments/update" \
      -H "Content-Type: application/json" \
      -d '{ "path": "api/data.ts", "comments": "{\"token\": \"new-secret-key\"}" }'
    ```
  


---

## What's Next