API keys were never validated — the middleware only checked session tokens,
so an X-API-Key request always 401'd. Add auth.Service.ValidateAPIKey (looks
up the key hash, enforces enabled/revoked/expiry, loads the user + roles,
stamps last_used_utc) and route X-API-Key / bearer auth through it.
Add per-key scopes (migration 006): "read" (GET/HEAD only) or "readwrite"
(full access, default). The middleware rejects mutating requests from a
read-scoped key with 403. Create accepts a scope; list and create responses
include it; the UI create dialog has a scope selector and the list shows a
scope chip. Disable/re-enable and revoke (permanent) were already correct.
Verified live: RW key GET/POST ok; read key GET ok, POST 403; disable→401,
re-enable→200; revoke→401 and re-enable blocked.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add backend OIDC: /api/v1/auth/oidc/status|login|callback (authorization code +
PKCE, state+nonce cookies, ID-token verification via go-oidc) and admin
/auth/oidc/config (GET/PUT, validates the issuer via discovery on enable).
Config resolves app_settings (UI) > env > default, so SSO can be configured from
the UI while env still seeds it. LoginOIDC provisions/links federated users by
the stable (issuer, subject) pair, refusing to reuse a username held by a
different account. Verified: discovery + real authorize redirect + PKCE cookies;
config API with DB-over-env precedence; provisioning unit-tested.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Add password_reset_required column (migration 004) + repository support
- auth.Service.ChangePassword verifies current, hashes new, clears flag,
emits PasswordChange audit events for success and failure
- Bootstrap: when no ORCHESTRAD_BOOTSTRAP_PASSWORD[_FILE] is set, seed
admin/admin with password_reset_required=true and log a one-time warn
banner; env/file-supplied passwords keep the flag clear
- Expose passwordResetRequired in UserInfo / /auth/me / login response
- POST /api/v1/auth/change-password behind the authenticated group
- Frontend: /change-password page + ChangePasswordForm, AuthLogin and
RequireAuth bounce any other route to it while the flag is set
- Docs: DesignSpecification 8.6/8.8 and Template 7.6/7.7 rewritten,
Trusted Proxy renumbered to 7.8 in the template, acceptance items
updated to match the new default-credential behavior
When the users table is empty on startup (typical first boot into a
fresh data directory), create a single Admin-role user called admin
so the operator has something to log in with before an IdP is wired.
Password source precedence:
1. ORCHESTRAD_BOOTSTRAP_PASSWORD (or _FILE) if set
2. A 24-byte hex string generated at random and printed once to
stdout and to the structured log
The generated-password path logs a bright one-time banner with the
username and password, intended to be captured from the console on the
very first run and then forgotten. No password is ever written to a
file on disk by this flow.
Wired from cmd/run via cli.go so that every run invocation checks the
seed state after migrations have been applied.
Phase 1 foundations:
- Go backend with Chi router framework
- SQLite database with WAL mode and foreign keys
- Database migrations for users, roles, credentials, AD connections, schedules, rules, and audit
- CLI commands: init, run, install, uninstall, start, stop, migrate, backup, restore, doctor
- Configuration loading from environment variables
- Centralized logging with file rotation (lumberjack)
- Crypto package for Argon2id password hashing and AES-GCM encryption
- Auth service with session management
- Audit service for event logging
- Scheduler with 6-field cron support
- REST API routes scaffolded for all major resources
- CORS support with localhost defaults for development
- Docker support with Dockerfile and docker-compose.yml
- Multi-platform build script (PowerShell)
- Project structure per design specification
Version format: yyyy.MM.dd.HHmm
All PKs are UUIDv4, all timestamps UTC