Skip to content

OIDC + CF Access auth flow

PocketID es Identity Provider único del homelab. Dos rutas de auth según la app.

PocketID — 11 OIDC clients activos (verificado 2026-08-01)

PocketID (:1411 en VM 208) tiene 11 clientes OIDC registrados (confirmado en vivo, select count(*) from oidc_clients en la DB SQLite del container). La tabla lista los principales/validados E2E; el resto son clientes internos de otros servicios.

Cliente Hostname Estado E2E Notas
Karakeep bookmarks.monxas.casa ✅ Login E2E validado Auto-provisioning de usuarios
Grafana grafana.monxas.casa ✅ Login E2E validado Mapping role via groups
Paperless-ngx paperless.monxas.casa ✅ Login E2E validado OIDC social account
Vaultwarden pass.monxas.casa ✅ Login E2E validado (actualizado 2026-08-01: ≥1 usuario autorizado en DB) SSO config en admin
Immich pics.monxas.casa ✅ Login E2E validado (actualizado 2026-08-01: ≥1 usuario autorizado en DB) Auto-launch toggle
Audiobookshelf podcasts.monxas.casa ✅ Login E2E validado (actualizado 2026-08-01: ≥1 usuario autorizado en DB) OpenID Connect
Remote-Pulse Dashboard rp.monxas.casa (u host equivalente) ✅ ≥1 usuario autorizado No estaba en esta tabla antes
wp-pulse / wp-pulse-app WP-Pulse CP 🟡 Cliente OIDC registrado, 0 usuarios autorizados en DB No estaba en esta tabla antes
Piso Colón ✅ ≥1 usuario autorizado Cliente adicional no documentado antes
Cloudflare Access monxas.cloudflareaccess.com ✅ Backing 54 apps self_hosted (verificado 2026-07-19 vía CF API, ver reference/cf-access.md) Sin email PIN. El ~31 de antes estaba desactualizado (la lista real es ~54 y sigue cambiando)

Ver Creating an OIDC client para el procedimiento de alta.

Apps con OIDC nativo (Karakeep, Grafana, Paperless...)

sequenceDiagram
    autonumber
    actor User
    participant CF as Cloudflare edge
    participant CFT as cloudflared (LXC 123)
    participant VIP as VIP .250 keepalived
    participant Caddy as Caddy LXC 270/271
    participant App as App (Grafana/Karakeep/Paperless)
    participant PID as PocketID

    User->>CF: GET https://grafana.monxas.casa
    CF->>CFT: Tunnel (HTTP/2)
    CFT->>VIP: https://192.168.0.250:443
    VIP->>Caddy: TCP (primary)
    Caddy->>App: reverse_proxy a VM 208:3030
    App-->>User: 302 /login (no session)

    User->>App: Click "Sign in with PocketID"
    App->>PID: 302 /authorize?client_id=X&PKCE
    PID-->>User: Login form (passkey o credenciales)
    User->>PID: Auth OK
    PID->>App: 302 /callback?code=AUTH
    App->>PID: POST /api/oidc/token (auth_code + secret)
    PID-->>App: access_token + id_token (JWT)
    App->>PID: GET /api/oidc/userinfo
    PID-->>App: {email, name, sub}
    App->>App: Session created (sub_id linked to local user via email)
    App-->>User: Logged in

Notas: - Token exchange usa client_secret_basic (HTTP Basic auth) o client_secret_post. PocketID acepta ambos. - PKCE obligatorio (pkce_enabled=1 en cliente PocketID). - Email link: si email del JWT matchea local user → linkea sin crear duplicado. Si no, crea user nuevo (puede requerir promoción manual a admin, ver guides/creating-oidc-client.md).

Apps con login propio + CF Access perimeter (~54 apps mgmt, verificado 2026-07-19)

sequenceDiagram
    autonumber
    actor User
    participant CF as Cloudflare edge
    participant CFA as CF Access policy
    participant PID as PocketID
    participant CFT as cloudflared
    participant App as App (Sonarr, Dashboard, Dockge...)

    User->>CF: GET https://sonarr.monxas.casa
    CF->>CFA: Check session cookie

    alt Sin sesión CF Access
        CFA->>PID: 302 /authorize (OIDC client "Cloudflare Access")
        PID-->>User: Login (passkey)
        User->>PID: Auth OK
        PID->>CFA: code → token
        CFA-->>User: Set session cookie (24h)
    end

    CFA->>CFT: Forward + headers JWT
    CFT->>App: HTTPS via tunnel
    App->>App: Auth own (Sonarr API key cookie, etc.)
    App-->>User: App UI (sesión propia)

Notas: - Sesión CF Access dura 24h, compartida entre las apps protegidas (~54, verificado 2026-07-19 — ver reference/cf-access.md). - App sigue requiriendo su propio login (CF Access es perimeter, no SSO de app). - No email PIN — PocketID es único IdP en CF Zero Trust → Settings → Authentication.

Apps que SE QUEDAN FUERA de ambos

  • PocketID itself — sería loop infinito si se autoprotege.
  • monxas.casa apex — landing público.
  • cors, kiwix, loki, rag — públicas o bearer-token API.

Routing cross-cutting

graph LR
    subgraph "Internet"
        U[User]
    end

    subgraph "Cloudflare"
        E[CF edge]
        A[CF Access
54 apps] end subgraph "Homelab pmx-50" VIP{{VIP .250
keepalived}} C1[Caddy LXC 270
.40] VM[VM 208 backends
~90 containers] PID[PocketID
:1411] end subgraph "Homelab pmx-51" CFT[cloudflared
LXC 123
.207] C2[Caddy LXC 271
.41] end U -->|HTTPS| E E -->|54 apps| A A -->|OIDC| PID E -->|tunnel| CFT CFT --> VIP VIP -.master.-> C1 VIP -.backup.-> C2 C1 --> VM C2 --> VM VM -->|reverse_proxy| PID style PID fill:#9c27b0,color:#fff style A fill:#ff6b6b,color:#fff style VIP fill:#ffd43b

Decisiones que importan

Decisión Por qué
PocketID en VM 208 Docker (no LXC propio) Stack ~/stacks/pocket-id/, DB SQLite local. Compatible con backups dockge habituales. Sin tunnel.* labels: NO se mete en routing automation (acceso via pocketid.monxas.casa por DNS + Caddy general rule).
CF Access NO detrás de Caddy LXC CF Access intercepta en edge antes de tunnel — Caddy nunca ve apps protegidas hasta que CF Access aprueba.
Caddy LXC full-routing (post-F4.f, 2026-05-20) LXC 270/271 son source-of-truth — importan managed.caddy (host:port targets a 192.168.0.208) + LE wildcard cert vía DNS-01. El container Caddy en VM 208 fue eliminado. Si VM 208 cae, backends caen pero el ingress (LXC 270/271) sigue respondiendo (puede servir error pages custom).
24h session CF Access + token PocketID stateless Balance UX (no re-login frecuente) vs security (limited blast radius si cookie leaked).