Files
mars3142 f5a8c63bf6 Add personal access tokens for machine callers
For scripts and tools that call findr-api's HTTP endpoints directly
without an interactive OIDC login - a KiCad plugin, a backup job, a
CLI import script. Not used by the ESP32 controller, which only ever
speaks MQTT to Mosquitto and has nothing to do with this.

- api_tokens table: subject (OIDC sub) + name + a hashed fdr_... token
  + scopes + optional expiry. Plaintext is generated once at creation
  and never stored.
- plugins/auth.ts gains a second verification path: a fdr_-prefixed
  bearer is looked up by hash instead of JWT-verified, then mapped to
  the same request.user shape the existing scope checks already use.
- A token's scopes must be a subset of whatever the creating credential
  itself currently holds (services/api-tokens.service.ts) - no
  self-escalation, enforced server-side regardless of what a client UI
  shows.
- /v1/tokens (list/create/revoke), gated by plain authentication rather
  than a SCOPES.* requireScope - this is about identity, not a findr
  domain permission.

Verified end-to-end against a real Postgres: unauthenticated 401,
valid-token 200/201, scope escalation 403, revoke 204, revoked-token
reuse 401.

Signed-off-by: Peter Siegmund <mars3142@noreply.mars3142.dev>
2026-09-04 23:21:30 +02:00

55 lines
2.8 KiB
Bash

# ── HTTP ────────────────────────────────────────────────────────────────────
NODE_ENV=development
HOST=0.0.0.0
PORT=3000
LOG_LEVEL=info
# Comma-separated list of allowed browser origins for CORS (findr-web).
CORS_ORIGIN=http://localhost:5173
# ── Postgres ────────────────────────────────────────────────────────────────
# Superuser — only used by docker compose to bootstrap the cluster.
POSTGRES_SUPERUSER_PASSWORD=postgres
POSTGRES_PORT=5432
# DDL role: owns schema "findr", used ONLY to run migrations at deploy time.
DATABASE_DDL_URL=postgres://findr_migrator:findr_migrator@localhost:5432/findr
FINDR_MIGRATOR_PASSWORD=findr_migrator
# DML role: runtime queries. No schema-changing privileges.
DATABASE_URL=postgres://findr_app:findr_app@localhost:5432/findr
FINDR_APP_PASSWORD=findr_app
# Run pending migrations (with the DDL role) on startup. Required in deployments.
RUN_MIGRATIONS_ON_START=true
# ── OIDC (currently Keycloak realm "mars3142", on mars3142-02) ──────────────
# Plain OIDC resource-server config (src/plugins/auth.ts) - issuer/audience
# and JWKS-via-discovery work against any standards-compliant IdP, same
# OIDC_ISSUER value findr-web's stack.env uses.
OIDC_ISSUER=https://auth.mars3142.dev/realms/mars3142
# The "findr-audience" client scope maps this into the token's aud.
OIDC_AUDIENCE=findr-api
# Permission scopes the API enforces (Keycloak client scopes on findr-web):
# findr:parts:read findr:parts:write findr:stock:write findr:light:control
# ── MQTT (Mosquitto broker on mars3142-01, same host as findr) ──────────────
# In the deployment the API reaches Mosquitto by its compose service name over
# the shared network — plain, port 1883, no TLS (faster).
# mqtts://mqtt.mars3142.dev:8883 is the public endpoint (browsers / devices).
MQTT_URL=mqtt://mosquitto:1883
# Broker user `findr-api`. The ESP32 firmware uses `findr-controller`.
MQTT_USERNAME=findr-api
MQTT_PASSWORD=
# Base topic. Contract in src/lib/mqtt-topics.ts / docs/mqtt.md:
# findr/box/<n>/{config,cmd/light,state/light,evt/button}
# findr/controller/<esp>/{state/online,state/info,cmd/identify}
MQTT_TOPIC_PREFIX=findr
# ── Rate limiting (src/plugins/rate-limit.ts) ───────────────────────────────
RATE_LIMIT_MAX=100
RATE_LIMIT_WINDOW_MS=60000
# Empty (default) = in-memory, fine with exactly one findr-api instance. Set
# this once there's more than one replica behind Traefik, or each instance
# counts on its own and the real allowed rate multiplies by the replica count.
REDIS_URL=