Creating an OIDC client (PocketID)¶
Cómo registrar una aplicación que soporta OIDC nativo (e.g. Grafana) en PocketID, el IdP que usa el homelab para SSO LAN (ver ADR-0007 §F5).
PocketID corre en VM 208 (stacks/pocket-id, puerto host 1411). Datos en
/home/monxas/stacks/pocket-id/data/pocket-id.db (SQLite).
Workflow normal (via UI)¶
- Abrir
https://pass.monxas.casa/(PocketID UI). - Login como admin.
- OIDC Clients → New.
- Rellenar:
- Name:
Grafana - Callback URLs:
https://grafana.monxas.casa/login/generic_oauth - Logo URL (opcional): icono del servicio
- Guardar. Anotar:
client_idclient_secret(solo se muestra una vez)- Configurar el servicio destino (Grafana, Immich, etc.) con esos valores + los endpoints OIDC de PocketID:
- Authorization URL:
https://pass.monxas.casa/authorize - Token URL:
https://pass.monxas.casa/api/oidc/token - Userinfo URL:
https://pass.monxas.casa/api/oidc/userinfo - JWKS URL:
https://pass.monxas.casa/.well-known/jwks.json - Guardar secrets en SOPS (ver Rotating secrets):
bash sops secrets/pocketid.sops.yaml # añadir: # grafana: # client_id: <id> # client_secret: <secret>
Workflow alternativo: SQL directo¶
Si la UI de PocketID no es accesible (e.g. recovery, bootstrap inicial), se
puede INSERT directamente en la DB SQLite.
Solo en caso necesario
Tocar la DB a mano es frágil. La UI de PocketID valida cosas (FK, esquema)
que INSERT directo no. Toma snapshot ZFS antes.
Bcrypt gotcha¶
PocketID almacena el client_secret hasheado con bcrypt (cost 10, $2a$ o $2b$).
Hay un gotcha crítico al generarlo:
NO generes bcrypt vía SSH desde la Mac
Comando como este se rompe:
bash
ssh monxas@192.168.0.XXX 'python3 -c "..."'
El shell remoto interpreta $2a$10$... como variable shell vacía y
mutila el hash. Resultado: el INSERT mete un hash inválido, login
silenciosamente roto, y debugging muy doloroso.
Solución: SSH primero, generar el hash en sesión interactiva en VM 208, copiar a mano, luego INSERT.
Procedimiento correcto¶
```bash
1) SSH a VM 208¶
ssh monxas@192.168.0.XXX
2) Generar el secret plano + su bcrypt EN la VM¶
python3 <<'EOF' import secrets, crypt plain = secrets.token_urlsafe(32) hashed = crypt.crypt(plain, crypt.mksalt(crypt.METHOD_BLOWFISH, rounds=10)) print("PLAIN: " + plain) print("HASH: " + hashed) EOF
Salida ejemplo:¶
PLAIN: xK9_aP3F...¶
HASH: $2b$10$abcdef...¶
```
Guarda el
PLAINen SOPS (es lo que el cliente final usa); elHASHes lo que va a PocketID.
INSERT pattern¶
```bash
3) (todavía en VM 208) INSERT en la DB¶
docker compose -f ~/stacks/pocket-id/docker-compose.yml exec pocket-id sqlite3 /app/data/pocket-id.db <<EOF
INSERT INTO oidc_clients (
id,
name,
secret,
callback_urls,
logo_url,
is_public,
pkce_enabled,
created_at,
updated_at
) VALUES (
'
4) Restart PocketID para recargar¶
docker compose -f ~/stacks/pocket-id/docker-compose.yml restart ```
El
client-id-uuidlo generas conpython3 -c "import uuid; print(uuid.uuid4())".
Verificar¶
```bash
desde VM 208¶
sqlite3 /home/monxas/stacks/pocket-id/data/pocket-id.db \ 'SELECT id, name, length(secret) FROM oidc_clients;'
length(secret) debe ser 60 (bcrypt $2b$10$22charsalt53charshash). Si es <60, mal.¶
```
Test end-to-end:
```bash curl -sS https://pass.monxas.casa/.well-known/openid-configuration | jq
Pegar luego el client_id/client_secret en Grafana → probar login¶
```
Apps que NO soportan OIDC nativo¶
Para apps sin soporte OIDC (Pi-hole, qBittorrent, dozzle, etc.) usamos
forward_auth de Caddy contra PocketID, no un OIDC client. Eso se
configura en managed.caddy con:
caddy
forward_auth pass.monxas.casa {
uri /verify
copy_headers X-Forwarded-User
}
Y se activa con la label tunnel.auth=pocketid (ver Adding a service).
No necesita registro como OIDC client.
Rotación del client secret¶
```bash
1) Generar nuevo plain+hash (en VM 208, no via SSH inline)¶
2) UPDATE en la DB:¶
sqlite3 /home/monxas/stacks/pocket-id/data/pocket-id.db \
"UPDATE oidc_clients SET secret = '
3) Update el plain en SOPS + push¶
4) Update Grafana config con el nuevo plain¶
5) Restart Grafana¶
```