---
id: w4/m2/t001
title: Auth middleware: Hydra token introspection + `BEX_AUTH_MODE` flag
worker: worker4
status: done
estimate: 30m
depends_on: []
---

## Objective

Extend bex-api's auth layer so bearer tokens can be validated against Hydra's admin introspection endpoint (`POST /admin/oauth2/introspect`) instead of only comparing against the static `BEX_API_TOKEN`. Mode selected by `BEX_AUTH_MODE` (`static` — default, current behavior — or `ory`); static mode remains the fallback so nothing breaks.

## Context

- Current auth lives in `operator/internal/api/auth.go` (static bearer check) wired in `operator/internal/api/server.go`; entrypoint `operator/cmd/api/main.go`.
- Hydra admin URL comes from `BEX_HYDRA_ADMIN_URL` (t003 plumbs it); introspection responses should be cached briefly to avoid a per-request round trip.
- Requires w4/m1 shipped (Hydra running in-cluster).
- New Go files carry the Apache-2.0 header from `operator/hack/boilerplate.go.txt`.

## Steps

1. Add an introspection client + middleware branch in `operator/internal/api/auth.go` keyed on `BEX_AUTH_MODE`.
2. Reject on `active: false`, network failure (fail closed), or missing token; keep static mode byte-for-byte compatible.
3. Unit tests with a fake introspection server (active, inactive, timeout).

## Files

- `operator/internal/api/auth.go`
- `operator/internal/api/server.go`
- `operator/internal/api/api_test.go` (or a new `auth_test.go`)

## Acceptance criteria

- [ ] `BEX_AUTH_MODE=ory`: valid token passes, inactive/garbage token and introspection outage → 401/503 fail-closed.
- [ ] Unset/`static` mode behaves exactly as today (`make test` green).

## Out of scope

- Scope/permission mapping per token; Kratos sessions (t002).
