Skip to content

feat(api): optional X-Service-Token auth for backend-only access#34

Merged
spideystreet merged 1 commit into
developfrom
feat/linker-service-token
Apr 21, 2026
Merged

feat(api): optional X-Service-Token auth for backend-only access#34
spideystreet merged 1 commit into
developfrom
feat/linker-service-token

Conversation

@spideystreet
Copy link
Copy Markdown
Collaborator

Summary

Adds a shared-secret gate on the FastAPI REST API so that only the ost-backend MCP gateway can reach it in production. Paired with ost-backend#51 which sends the header.

What changes

  • New: `src/services/api/auth.py` — FastAPI dependency `require_service_token`. Reads `OST_LINKER_SERVICE_TOKEN` directly from `os.environ` and validates `X-Service-Token` request header via `secrets.compare_digest`.
  • Wired: `src/services/api/main.py` — dependency applied to `projects`, `recommendations`, `references` routers. `/health` stays OPEN for uptime monitors.
  • Config: `OST_LINKER_SERVICE_TOKEN` added to `APIConfig` (for docs/completeness) and `.env.example`.
  • Tests — `tests/api/test_service_token.py` — 5 test classes covering: open mode (env unset), enforced missing/mismatched/matching headers (parametrized across every protected endpoint), health always open.

Rollout safety

Open by default. When `OST_LINKER_SERVICE_TOKEN` is unset, endpoints behave exactly as today — anyone can hit them. Only when the env is set on prod do the endpoints require the header. Deploy sequence (your call, not this PR's job):

  1. Merge both PRs (this + ost-backend#51).
  2. Deploy both services — no behavior change while env is unset on both.
  3. Set `OST_LINKER_SERVICE_TOKEN=` on backend + linker at the same time.
  4. Linker now rejects any caller without the header. Backend sends it. Game over for bypass.

Verification

```
✓ ruff check src/services/api/ tests/api/ — All checks passed
✓ ruff format --check — clean
✓ mypy src/services/api/ — Success: no issues found in 13 source files
✓ pytest -m api tests/api/test_service_token.py — 12 passed
```

Test plan

  • `uv sync && pytest -m api` green.
  • Locally: `export OST_LINKER_SERVICE_TOKEN=foo; uvicorn ... --port 8000` then `curl /projects/search?q=foo` → 401; `curl -H 'X-Service-Token: foo' /projects/search?q=foo` → 200; `curl /health` → 200 (never gated).
  • Unset env, restart → all endpoints open again.

When OST_LINKER_SERVICE_TOKEN is set, protected endpoints require
X-Service-Token matching (constant-time compare). When unset, the API
behaves as before — preserves backward compat for gradual rollout.
/health stays open for uptime monitors.

The token is read directly from os.environ in the dependency so tests
stay simple and the check never touches the pydantic config path.
@spideystreet spideystreet merged commit f0e8e36 into develop Apr 21, 2026
1 check failed
@spideystreet spideystreet deleted the feat/linker-service-token branch April 21, 2026 22:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant