Sub-Task #254
Updated by Bricklou 26 days ago
Support multi-provider OIDC configuration, config-file based, not DB-backed 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).
Pattern (used by Grafana, Loki, Gitea): unified structured config file (YAML) + environment variable overrides for secrets, loaded via `config-rs` or `figment`, following the existing `Config::from_env` pattern (apps/server/cli/src/config.rs).
Kubernetes/GitOps split:
- ConfigMap holds the full provider structure (issuer, client_id, scopes, icon) *except* `client_secret` — 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`. No `${VAR}` interpolation syntax in the file itself — one override mechanism, not two.
Format spec:
```yaml <pre>
# config.yaml (ConfigMap, git-committed, no secrets)
oidc_providers:
google:
name: Google
issuer_url: https://accounts.google.com # discovery doc = {issuer_url}/.well-known/openid-configuration
client_id: xxxxx.apps.googleusercontent.com
scopes: [openid, email, profile]
icon: google # slug into github.com/homarr-labs/dashboard-icons
keycloak:
name: Company SSO
issuer_url: https://sso.example.com/realms/kubestro
client_id: kubestro-dashboard
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):
```bash
<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 stable 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), 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`.