feat/native-oidc-sso - OPTIONAL SSO #1

Merged
stephen merged 2 commits from feat/native-oidc-sso into main 2026-06-09 19:52:38 -07:00
Owner

Optional SSO

Optional SSO
New `sso` boolean (default false). When on, GrampsWeb's native OIDC
client logs in against Zitadel; users with the `homefree-admin`
project role come back as GrampsWeb admins (role 5).

  - Pin image to 26.6.0 — the bundled oidc.py patch is a textual
    delta against that exact upstream file. Bumping the tag means
    re-extracting and re-applying.
  - apps/grampsweb/patches/oidc.py (bind-mounted over the venv copy):
    * flatten Zitadel's nested project-roles claim
      ({role: {org_id: org_name}}) into a list of role names —
      upstream only handles list/str shapes.
    * import-time shim that relaxes Authlib's strict azp validation
      so Zitadel-issued ID tokens stop tripping InvalidClaimError.
  - preStart synthesizes /var/lib/grampsweb-podman/sso.env from the
    secrets zitadel-provision writes to
    /var/lib/homefree-secrets/grampsweb/. Empty file pre-provisioning
    so the container starts cleanly with OIDC off.
  - preStart also synthesizes a CA bundle (system roots + Caddy local
    CA) and bind-mounts it as /etc/ssl/certs/ca-certificates.crt so
    the in-container OIDC discovery fetch trusts sso.<domain>.
  - postStart programmatically seeds a placeholder owner user
    (`homefree-bootstrap`, random password at
    /var/lib/homefree-secrets/grampsweb/bootstrap-password) via
    /api/token/create_owner/ + /api/users/.../create_owner/. Without
    this the GrampsWeb frontend renders its first-run create-admin
    wizard, blocking the OIDC auto-redirect.
  - Status file at /var/lib/grampsweb-podman/sso-status.txt records
    what preStart found (sso toggle, secrets-on-disk, env-file
    populated) and what postStart's API probes returned — `cat` it
    instead of grepping journalctl.
  - sso.kind in the service-config catalog flips between
    "native_oidc" (on) and "none" (off); reverse-proxy.oauth2 stays
    off (no outer Caddy gate — GrampsWeb handles OIDC itself).

KNOWN GAP: the plugin assumes /var/lib/homefree-secrets/grampsweb/
already contains an oidc-client-{id,secret} pair. Today, the
zitadel-provision service in the base homefree repo only provisions
OIDC apps for entries in its hardcoded `services = [ … ]` list
(apps/zitadel/provision.nix). There is no per-app extension point a
plugin can use to declare its own redirect_uris / grant_types /
post_logout_uris — that wiring needs to land in the base repo as a
separate change before this plugin's `sso = true` path can work on
a stock install. The plugin itself stays self-contained.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Push the homefree-grampsweb OIDC descriptor (redirect/logout URIs,
grant types, post-restart units) into the shared homefree.sso.clients
registry instead of relying on a hardcoded entry in the base repo's
zitadel/provision.nix. provision.nix consumes the resolved list to
create the Zitadel app and write client_id/secret to
/var/lib/homefree-secrets/grampsweb/, which preStart already reads.

Gated on the plugin's own sso toggle (no dead client when SSO is off);
not gated on enable, matching the registry convention. The registry
dedups by internal_name, so this coexists safely with the still-present
base-repo entry until that entry is removed.
erahhal changed title from feat/native-oidc-sso to feat/native-oidc-sso - OPTIONAL SSO 2026-06-09 18:10:17 -07:00
stephen approved these changes 2026-06-09 19:52:29 -07:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
homefree-plugins/homefree-grampsweb!1
No description provided.