Sub-Task #254
Updated by Bricklou 26 days ago
Support multi-provider OIDC configuration, config-file based, not DB-backed env/config-file based (not DB-backed) — matches how Grafana (`GF_AUTH_GENERIC_OAUTH_*` / named provider sections) and not CRD-based (Kubestro is a standalone Postgres-backed server, no operator/controller infra exists, so a CRD would mean building watch/reconcile machinery just to read config at boot). Outline (`OIDC_CLIENT_ID`/`OIDC_CLIENT_SECRET`/`OIDC_AUTH_URI`) do it. Pattern (used Primary mechanism: YAML file mounted via k8s ConfigMap/Secret volume, path given by Grafana, Loki, Gitea): unified structured config file (YAML) + environment variable overrides `OIDC_PROVIDERS_FILE` (GitOps-friendly, readable, diffable). Fallback: `OIDC_PROVIDERS` env var with the same structure as JSON (safer than YAML for secrets, loaded via `config-rs` or `figment`, a single-line env var). Parsed at startup into in-memory provider values, following the existing `Config::from_env` pattern (apps/server/cli/src/config.rs). Kubernetes/GitOps split: - ConfigMap holds Client secrets stay in env/k8s Secret, not the full provider structure (issuer, client_id, scopes, icon) *except* `client_secret` DB — fully git-committed, plaintext, PR-reviewable. - Secret holds each provider's `client_secret`, injected as an env var per provider (works as-is with SOPS, Sealed Secrets, External Secrets Operator, Vault, etc — no format changes needed on the secret side). - One mechanism for overrides: `config-rs`'s `Environment::default().separator("__")`, e.g. `OIDC_PROVIDERS__GOOGLE__CLIENT_SECRET` overrides `oidc_providers.google.client_secret`. encryption-at-rest or admin-CRUD/drift problem to solve. No `${VAR}` interpolation syntax in the file itself — one override mechanism, not two. DB entity/migration needed. Format spec: <pre> ```yaml # config.yaml (ConfigMap, git-committed, no secrets) oidc-providers.yaml oidc_providers: - id: google google: name: Google issuer_url: https://accounts.google.com # discovery doc = {issuer_url}/.well-known/openid-configuration client_id: xxxxx.apps.googleusercontent.com client_secret: GOCSPX-xxxxx scopes: [openid, email, profile] icon: google # slug into github.com/homarr-labs/dashboard-icons keycloak: - id: keycloak name: Company SSO issuer_url: https://sso.example.com/realms/kubestro client_id: kubestro-dashboard client_secret: s3cr3t scopes: [openid, email] icon_url: https://sso.example.com/icon.svg # override for providers not in the known icon set </pre> ``` Env vars (from Secret, per provider): <pre> OIDC_PROVIDERS__GOOGLE__CLIENT_SECRET=GOCSPX-xxxxx OIDC_PROVIDERS__KEYCLOAK__CLIENT_SECRET=s3cr3t </pre> Deployment mounts the ConfigMap as `/etc/kubestro/config.yaml` (readOnly volume) and injects each `client_secret` as an env var via `secretKeyRef`. A config/secret change plus a rollout (as GitOps tools like Flux/ArgoCD already do) picks up the new config on next boot — no hot-reload, no CRD, no extra RBAC. - Provider key (`google`, `keycloak`, ...) is the `id`: stable slug: slug, used in redirect URL (`/auth/oidc/{id}/callback`) and in the discovery-endpoint response. Map key replaces the earlier `id:` list-item field. - `issuer_url`: OIDC issuer identifier, not the discovery endpoint itself — backend fetches `{issuer_url}/.well-known/openid-configuration` at startup to resolve authorize/token/jwks endpoints (standard OIDC discovery). - `icon`: slug resolved against homarr-labs/dashboard-icons (https://github.com/homarr-labs/dashboard-icons), ([https://github.com/homarr-labs/dashboard-icons](https://github.com/homarr-labs/dashboard-icons)), served via their CDN. `icon_url`: explicit override for custom/self-hosted providers not in that set. Non-k8s/simple deployments: same env-var override mechanism still works standalone (set `OIDC_PROVIDERS__<ID>__*` directly), config file path still required via e.g. `OIDC_CONFIG_FILE`.