Project

General

Profile

Actions

Sub-Task #254

open

Epic #3: Implement a secure user authentication system

Feature #160: User Login

User Story #176: As a user, I want to log in via OAuth/OIDC, so that I can use existing providers

Backend: OIDC provider configuration

Sub-Task #254: Backend: OIDC provider configuration

Added by Bricklou 26 days ago. Updated 20 days ago.

Status:
Done
Priority:
Normal
Assigned To:
Target version:
Start date:
08/30/2026
Due date:
% Done:

100%

Estimated time:

Description

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), client_secret given as an explicit ${ENV_VAR_NAME} placeholder resolved against the process env at load time — matches the repo's existing config style (Config::from_env in apps/server/cli/src/config.rs, and apps/server/api/src/app/context/*.rs, both read std::env::var directly, no config-merging crate anywhere today).

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: client_secret values are ${ENV_VAR_NAME} placeholders, resolved against the process env at load time — admin picks the var name explicitly per provider (not derived from the provider slug, which would break for hyphenated keys since env var names can't portably contain hyphens).

Format spec:

# 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
    client_secret: "${OIDC_GOOGLE_CLIENT_SECRET}"
    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
    client_secret: "${OIDC_KEYCLOAK_CLIENT_SECRET}"
    scopes: [openid, email]
    icon_url: https://sso.example.com/icon.svg # override for providers not in the known icon set

Env vars (from Secret, per provider — names are admin-chosen, matching whatever each client_secret placeholder above references):

OIDC_GOOGLE_CLIENT_SECRET=GOCSPX-xxxxx
OIDC_KEYCLOAK_CLIENT_SECRET=s3cr3t

Deployment mounts the ConfigMap as /etc/kubestro/config.yaml (readOnly volume) and injects each provider's secret as an env var via secretKeyRef, using whichever var name that provider's ${...} placeholder names. 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 — this is the "OpenID Provider Metadata" document, distinct from OAuth Dynamic Client Registration metadata, which this design deliberately does not use since providers are statically pre-registered by an admin).
  • 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 placeholder mechanism works standalone (set whatever env var each ${...} reference names), config file path still required via OIDC_CONFIG_FILE. If OIDC_CONFIG_FILE is unset, OIDC is disabled entirely (empty provider registry, no boot failure).

Startup discovery is fail-fast: if any configured provider's .well-known/openid-configuration fetch fails at boot, the whole server aborts (matches existing DATABASE_URL/APP_SECRET "bad config = don't start" behavior) — no partial-availability state to reason about.

Layering: Domain gets a ProviderKey/IssuerUrl/ClientId/ClientSecret/OidcProviderConfig value-object set plus a port pair — OidcProviderRegistry (lookup discovered client + config by ProviderKey) and a minimal marker-shaped OidcClient trait (just proves a discovered client is reachable through the port; #256 extends it with the actual authorize/token-exchange methods once that ticket designs the flow). Infra implements both via openidconnect-rs (discovery, CoreClient) — no openidconnect-rs/serde_yaml dependency leaks into Domain or Application. Config loading itself has no Application-layer use case (it's boot-time wiring, same category as Core::build(), not a request-time Command/Query).


Related issues 2 (2 open — 0 closed)

Blocks Server - Sub-Task #255: Backend: discovery endpoint for login pageDoneBricklou09/01/2026

Actions
Blocks Server - Sub-Task #256: Backend: OIDC endpoints (login start, callback, session)DoneBricklou09/01/2026

Actions

Updated by Bricklou 26 days ago Author Actions #1

  • Description updated (diff)

Updated by Bricklou 26 days ago Author Actions #2

  • Status changed from Draft to To Do

Updated by Bricklou 26 days ago Author Actions #3

  • Status changed from To Do to Planned
  • Assigned To set to Bricklou
  • Target version set to Server 0.1.0
  • Start date deleted (08/30/2026)

Updated by Bricklou 26 days ago Author Actions #4

  • Status changed from Planned to In Progress
  • Start date set to 08/30/2026

Updated by Bricklou 26 days ago Author Actions #5

  • Description updated (diff)

Updated by Bricklou 26 days ago Author Actions #6

  • Description updated (diff)

Updated by Bricklou 25 days ago Author Actions #7

  • Description updated (diff)

Updated by Bricklou 25 days ago Author Actions #8

  • Blocks Sub-Task #255: Backend: discovery endpoint for login page added

Updated by Bricklou 25 days ago Author Actions #9

  • Blocks Sub-Task #256: Backend: OIDC endpoints (login start, callback, session) added

Updated by Bricklou 24 days ago Author Actions #10

  • Status changed from In Progress to In Review
  • % Done changed from 0 to 100

Updated by Bricklou 20 days ago Author Actions #11

  • Status changed from In Review to Done
Actions

Also available in: PDF Atom