# Contract: Proxy HTTP Responses **Feature**: 001-oidc-proxy-script **File**: `src/proxyScripts/proxy.js` **Endpoint**: Any path handled by the adapter (all requests delegated to proxy.js by server.js) **Date**: 2025-07-17 --- ## Overview `proxy.js` responds to every inbound HTTP request with exactly one of two outcomes: a success response (authentication succeeded) or an error response (authentication failed for any reason). The contract defines the exact shape of both outcomes. --- ## Success Response **Trigger**: OIDC token successfully obtained (fresh fetch or valid cached token). ``` HTTP/1.1 200 OK Content-Type: text/plain Authorized ``` | Property | Value | |----------|-------| | Status code | `200` | | Status text | `OK` | | `Content-Type` header | `text/plain` | | Body | Literal string `Authorized` (no trailing newline) | **Acceptance test** (FR-007, SC-001, SC-002): ```javascript assert.strictEqual(res.statusCode, 200); assert.strictEqual(res.body, 'Authorized'); ``` --- ## Error Response **Trigger**: Any of the following (FR-008, SC-004): - Token service returns HTTP 4xx or 5xx - Token service is unreachable (network error) - Token request times out after 5 seconds (FR-014) - Token service response is missing `id_token` or `expires_in` - `adapter_settings` is missing a required field ``` HTTP/1.1 401 Unauthorized Content-Type: text/plain Unauthorized: ``` | Property | Value | |----------|-------| | Status code | `401` | | Status text | `Unauthorized` | | `Content-Type` header | `text/plain` | | Body prefix | `Unauthorized: ` (literal, followed by the error message) | | Body | Never empty; always includes a human-readable description | **Example bodies by error cause**: | Cause | Example body | |-------|-------------| | Invalid credentials (401 from token service) | `Unauthorized: HTTP 401` | | Token service unavailable | `Unauthorized: connect ECONNREFUSED 127.0.0.1:443` | | 5-second timeout | `Unauthorized: token service timeout` | | Response missing `id_token` | `Unauthorized: id_token missing from response` | | Response missing `expires_in` | `Unauthorized: expires_in missing from response` | | Missing `tokenUrl` in settings | `Unauthorized: missing required field: tokenUrl` | **Acceptance test** (FR-008): ```javascript assert.strictEqual(res.statusCode, 401); assert.match(res.body, /^Unauthorized: .+/); ``` --- ## Invariants These MUST hold for every request, regardless of outcome: 1. **One response per request**: `res.writeHead()` MUST be called exactly once; `res.end()` MUST be called exactly once. 2. **Never 500**: `proxy.js` MUST NOT emit a 500 or leave the connection open. All errors, including unexpected runtime errors, MUST result in a `401` (not a crash or hang). 3. **No imports/exports** (FR-009): The script MUST contain zero `import` or `export` statements — verified by static analysis. 4. **No forbidden globals** (FR-010): No `config`, `global.config`, or `process.env` references — verified by static analysis. 5. **Response within 5 seconds** (SC-001, FR-014): The HTTP timeout on the token POST is 5 000 ms. Combined with synchronous error handling, every request resolves within 5 seconds under normal network conditions. --- ## Out of Scope - The proxy script does NOT validate the inbound request (method, path, headers, body). Its sole responsibility is OIDC authentication. - The response does NOT include the OIDC token in the body. The `200 OK / Authorized` body is sufficient to confirm authentication succeeded (spec assumption, line 106). - No `Authorization` response header is set. The adapter's caller does not require it.