## Summary - Create Dex reference card (`docs/reference/services/dex.md`) with quick reference, architecture, identity source, storage, OIDC clients, secrets, and endpoints - Write federated login explanation article (`docs/explanation/federated-login.md`) covering the Dex + Forgejo two-layer auth model, login flow, and break-glass access - Add Dex to `services-check` (HTTP health endpoint + k3s pod check) - Update Grafana docs with new Authentication section documenting SSO via Dex - Update Forgejo docs with OAuth2 Provider section documenting its role as upstream identity source - Add Dex to ringtail workloads table and reference service index - Move `adopt-oidc-provider` plan to `completed/` with final design reflecting actual implementation ## Test plan - [ ] `mise run services-check` passes (includes new Dex checks) - [ ] `docs-check-links` passes (all wiki-links resolve) - [ ] `docs-check-index` passes (new docs are indexed) Reviewed-on: https://forge.ops.eblu.me/eblume/blumeops/pulls/223
3.3 KiB
| title | modified | tags | |||
|---|---|---|---|---|---|
| Dex | 2026-02-19 |
|
Dex
OIDC identity provider for BlumeOps. Dex federates authentication — downstream services (Grafana, future ArgoCD, etc.) delegate login to Dex, and Dex delegates to forgejo as the upstream OAuth2 provider.
Quick Reference
| Property | Value |
|---|---|
| URL | https://dex.ops.eblu.me |
| Tailscale URL | https://dex.tail8d86e.ts.net |
| Namespace | dex |
| Cluster | k3s (ringtail) |
| Image | registry.ops.eblu.me/blumeops/dex:v1.0.0-nix |
| Upstream | https://github.com/dexidp/dex |
| Manifests | argocd/manifests/dex/ |
| Container build | containers/dex/default.nix |
Architecture
Dex runs on ringtail's k3s cluster, isolated from the main services on indri's minikube. This means the IdP is independent of the minikube cluster lifecycle — if minikube goes down, Dex stays up and services can still authenticate once restored.
User Browser
|
v
Grafana (indri/minikube) --OIDC--> Dex (ringtail/k3s) --OAuth2--> Forgejo (indri/native)
^ |
| |
+---------------------- redirect back with token -------------------+
Cross-cluster communication works because Grafana reaches Dex via https://dex.ops.eblu.me (Caddy → Tailscale → ringtail), not k8s-internal DNS.
Identity Source
Dex uses a Gitea connector pointed at forgejo (https://forge.ops.eblu.me). Users authenticate with their Forgejo credentials. There are no static passwords — user management happens entirely in Forgejo.
This means adding a new user to BlumeOps SSO is just creating a Forgejo account.
Storage
SQLite3 with an emptyDir volume. This stores refresh tokens and auth codes. A pod restart invalidates active sessions (users re-login), which is acceptable for a homelab. No PVC needed.
OIDC Clients
| Client | Redirect URIs | Status |
|---|---|---|
| grafana | grafana.ops.eblu.me/login/generic_oauth, grafana.tail8d86e.ts.net/login/generic_oauth |
Active |
Future clients: argocd, forgejo, miniflux, zot
Secrets
All sensitive configuration is injected via external-secrets from the "Dex (blumeops)" 1Password item. The entire config.yaml is templated in the ExternalSecret — nothing sensitive is committed to git.
| 1Password Field | Purpose |
|---|---|
forgejo-client-id |
OAuth2 app client ID from Forgejo |
forgejo-client-secret |
OAuth2 app client secret from Forgejo |
grafana-client-secret |
OIDC client secret for Grafana |
Endpoints
| Path | Purpose |
|---|---|
/.well-known/openid-configuration |
OIDC discovery |
/auth |
Authorization (browser redirect) |
/token |
Token exchange |
/userinfo |
User info |
/keys |
JWKS (public keys) |
/callback |
OAuth2 callback from Forgejo |
/healthz |
Health check |
Related
- federated-login - How authentication works across BlumeOps
- forgejo - Upstream OAuth2 provider
- grafana - First OIDC client
- routing - How Dex is exposed via Caddy
- external-secrets - Secrets injection from 1Password