---
id: w4/m9/t003
title: Headless consent acceptor as a dashboard server route
worker: worker4
status: done
estimate: 60m
depends_on: [w4/m9/t001]
---

## Objective

Add the one hand-built piece the flow needs: a **headless** consent endpoint
(`/auth/consent`) in the dashboard's server layer. It fetches Hydra's consent
request, auto-accepts when `skip=true` or the client is trusted (implicit
consent, granting the requested scopes, `remember: true`), denies unknown
clients, and 302s back to Hydra. No UI.

## Context

- Kratos handles the login challenge (t001) but **not** consent — this acceptor
  is the explicitly-allowed exception in `DO_NOT_DO.md`.
- Home: dashboard server route (TanStack Start server handler) per the
  "dashboard/auth is the IdP surface" decision — not bex-api.
- Needs Hydra admin (`GET/PUT /admin/oauth2/auth/requests/consent[…/accept]`):
  a server-only env (e.g. `HYDRA_ADMIN_URL`, in-cluster
  `http://hydra-admin.auth.svc:4445`; port-forward locally). Must never reach
  the client bundle (not `VITE_*`).
- Trusted-client list: server env (e.g. `OAUTH_TRUSTED_CLIENTS`); Hydra's
  `skip_consent` client flag also short-circuits via `skip=true`.
- Behavior to implement (the reverted 2026-07-07 attempt validated this shape
  against a live Hydra; the code was never committed, so re-derive): lookup →
  `skip || trusted` ? accept(grant requested scope/audience, remember) :
  403 → redirect to Hydra's `redirect_to`. Missing/stale challenge → redirect
  to `/` (never an error page a human has to parse).

## Steps

1. Add the server route + a small Hydra-admin fetch helper (server-only module).
2. Wire `HYDRA_ADMIN_URL` + trusted-clients env (SSR-only); document in `dashboard/.env.example`.
3. Point local Hydra `urls.consent` at it and hand-verify accept + deny paths.

## Files

- `dashboard/src/routes/auth.consent.tsx` (or the server-route equivalent)
- `dashboard/src/common/server-fn/` (Hydra admin helper)
- `dashboard/.env.example`

## Acceptance criteria

- [x] Trusted/`skip` consent requests are accepted headlessly (correct scopes granted) and the browser returns to Hydra.
- [x] An unknown client_id is denied (403), not silently granted.
- [x] `HYDRA_ADMIN_URL` never appears in the client bundle.

## Out of scope

- A human consent UI for real third parties (future work when one exists).
