Authelia OIDC as Forgejo login source (was deferred, now unblocked) #33

Closed
opened 2026-08-06 05:38:43 +00:00 by agent.odin · 2 comments
Collaborator

Stable ID: FM-FORGE-OIDC
Source: DEFERRED.md + backlog.md (was deferred "until forge is stable"); owner ruling 2026-08-06: unblocked
Disposition: open — prerequisite of mesh decommission (#32)
Blocked by: nothing (owner confirms forge is stable enough)
Epic: #32 (mesh decommission)

Problem

git clone over HTTPS is performed by git, not a browser. It cannot follow a
redirect to a login page. Putting jo.et0.pw behind Caddy forward_auth
breaks git over HTTPS, breaks just forgejo (receives login HTML instead of
JSON), and breaks any CI pulling from the forge.

The solution is OIDC as a Forgejo login source, not forward_auth. Authelia
serves as an OAuth2/OIDC provider; Forgejo consumes it as an auth source. Web
UI login goes through Authelia; git over HTTPS keeps using Forgejo tokens; git
over SSH keeps using keys; the API keeps using its token.

This was deferred until the forge was stable. The owner confirms it is now
unblocked.

Scope

Per backlog.md (fully scoped already):

  1. Turn the OIDC provider on. outputs/hosts/heimdallr/configuration.nix
    ~line 75 — uncomment oidcHmacSecretFile, oidcIssuerPrivateKeyFile, and
    the settingsFiles entry for authelia/oidc_clients. Secrets already
    exist and are decrypted; only the wiring is commented out.

  2. Register an authorization_code client. just identity register-service does NOT cover this — it mints
    grant_types: [client_credentials] service accounts. Forgejo needs
    authorization_code with
    redirect_uris = ["https://jo.et0.pw/user/oauth2/authelia/callback"].
    Either a new recipe or a hand-edit of authelia/oidc_clients.

  3. Add the auth source in Forgejo — not declarative. Forgejo stores auth
    sources in its database; NixOS has no option for it:
    forgejo-admin admin auth add-oauth --name authelia --provider openidConnect …, once, on the box.

  4. ENABLE_AUTO_REGISTRATION = true. Registration is disabled in the
    module, so without this an Authelia login lands on "no such account" and
    nobody can get in. The symptom does not point at the cause.

Known cost

Step 3 is not reproducible from the repo. A freshly provisioned bifrost has a
forge with no Authelia auth source until someone runs the command again. Same
exposure as the first admin account — not a new class of problem, just a
second instance of one.

Acceptance

  • OIDC provider active on heimdallr (uncommented wiring)
  • authorization_code client registered in authelia/oidc_clients
  • Forgejo auth source added (forgejo-admin admin auth add-oauth)
  • ENABLE_AUTO_REGISTRATION = true
  • Web UI login through Authelia works
  • git clone https://jo.et0.pw/... still works (token auth, unaffected)
  • just forgejo still works (API token auth, unaffected)
  • git clone git@... still works (SSH keys, unaffected)

Provenance

From backlog.md and DEFERRED.md, where this was fully scoped and deferred
until the forge stabilized. Owner ruling 2026-08-06: unblocked. It is now a
prerequisite of the mesh decommission (#32) — without it, moving the forge to
public ingress breaks git over HTTPS.

Stable ID: FM-FORGE-OIDC Source: DEFERRED.md + backlog.md (was deferred "until forge is stable"); owner ruling 2026-08-06: **unblocked** Disposition: open — prerequisite of mesh decommission (#32) Blocked by: nothing (owner confirms forge is stable enough) Epic: #32 (mesh decommission) ## Problem `git clone` over HTTPS is performed by git, not a browser. It cannot follow a redirect to a login page. Putting `jo.et0.pw` behind Caddy `forward_auth` breaks git over HTTPS, breaks `just forgejo` (receives login HTML instead of JSON), and breaks any CI pulling from the forge. The solution is OIDC as a Forgejo **login source**, not forward_auth. Authelia serves as an OAuth2/OIDC provider; Forgejo consumes it as an auth source. Web UI login goes through Authelia; git over HTTPS keeps using Forgejo tokens; git over SSH keeps using keys; the API keeps using its token. This was deferred until the forge was stable. The owner confirms it is now unblocked. ## Scope Per backlog.md (fully scoped already): 1. **Turn the OIDC provider on.** `outputs/hosts/heimdallr/configuration.nix` ~line 75 — uncomment `oidcHmacSecretFile`, `oidcIssuerPrivateKeyFile`, and the `settingsFiles` entry for `authelia/oidc_clients`. Secrets already exist and are decrypted; only the wiring is commented out. 2. **Register an `authorization_code` client.** `just identity register-service` does NOT cover this — it mints `grant_types: [client_credentials]` service accounts. Forgejo needs `authorization_code` with `redirect_uris = ["https://jo.et0.pw/user/oauth2/authelia/callback"]`. Either a new recipe or a hand-edit of `authelia/oidc_clients`. 3. **Add the auth source in Forgejo — not declarative.** Forgejo stores auth sources in its database; NixOS has no option for it: `forgejo-admin admin auth add-oauth --name authelia --provider openidConnect …`, once, on the box. 4. **`ENABLE_AUTO_REGISTRATION = true`.** Registration is disabled in the module, so without this an Authelia login lands on "no such account" and nobody can get in. The symptom does not point at the cause. ## Known cost Step 3 is not reproducible from the repo. A freshly provisioned bifrost has a forge with no Authelia auth source until someone runs the command again. Same exposure as the first admin account — not a new class of problem, just a second instance of one. ## Acceptance - [ ] OIDC provider active on heimdallr (uncommented wiring) - [ ] `authorization_code` client registered in `authelia/oidc_clients` - [ ] Forgejo auth source added (`forgejo-admin admin auth add-oauth`) - [ ] `ENABLE_AUTO_REGISTRATION = true` - [ ] Web UI login through Authelia works - [ ] `git clone https://jo.et0.pw/...` still works (token auth, unaffected) - [ ] `just forgejo` still works (API token auth, unaffected) - [ ] `git clone git@...` still works (SSH keys, unaffected) ## Provenance From backlog.md and DEFERRED.md, where this was fully scoped and deferred until the forge stabilized. Owner ruling 2026-08-06: unblocked. It is now a prerequisite of the mesh decommission (#32) — without it, moving the forge to public ingress breaks git over HTTPS.
Author
Collaborator

Implemented in flake-ops (heimdallr's closure moved there; the paths in this ticket's scope are the flaky-mesh ancestors). Status against the acceptance list:

Done in config (uncommitted in flake-ops, eval-verified, awaiting deploy):

  1. OIDC provider on — modules/hosts/heimdallr/configuration.nix wires oidcHmacSecretFile, oidcIssuerPrivateKeyFile, and settingsFiles = [ oidc_clients ], in the same change as the first client (this ticket's original step order was wrong; provider-before-client makes Authelia refuse to start).
  2. authorization_code client registered — authelia/oidc_clients in modules/hosts/heimdallr/secrets.yaml now holds:
    • client_id: forgejo, authorization_policy: one_factor, consent_mode: implicit
    • redirect_uris: [https://jo.et0.pw/user/oauth2/authelia/callback] (bound to the auth source being named authelia — rename one, rename both)
    • require_pkce: true / S256, token_endpoint_auth_method: client_secret_basic, scopes openid email profile groups
    • secret: minted with authelia crypto hash generate pbkdf2digest in heimdallr's client entry, plaintext in bifrost's modules/hosts/bifrost/secrets.yaml as forgejo/oidc_client_secret, wired as a root-owned sops secret on bifrost so the hand-run step can read it at /run/secrets/forgejo/oidc_client_secret. Neither value ever appeared on a terminal.
  3. (config half) services.forgejo.settings.oauth2_client.ENABLE_AUTO_REGISTRATION = true on bifrost.

Hand-run step (operator, on bifrost, after both hosts carry the new closures):

forgejo-admin admin auth add-oauth \
  --name authelia \
  --provider openidConnect \
  --key forgejo \
  --secret "$(cat /run/secrets/forgejo/oidc_client_secret)" \
  --auto-discover-url https://auth.et0.pw/.well-known/openid-configuration \
  --scopes "openid email profile"

Note the ordering: heimdallr must be deployed first (bifrost is unarmed anyway — flake-ops#44) — the auth source is harmless to add early, but a login attempt before heimdallr carries the client gets invalid_client at Authelia.

Verify (last four acceptance boxes): web login via the Authelia button; git clone over HTTPS with a token, just forgejo, and SSH clones are all untouched by design — the vhost stays out of forward_auth.

Known cost stands: the auth source lives in Forgejo's database; a re-provisioned bifrost needs the command run again (same class as the first admin account).

Implemented in **flake-ops** (heimdallr's closure moved there; the paths in this ticket's scope are the flaky-mesh ancestors). Status against the acceptance list: **Done in config (uncommitted in flake-ops, eval-verified, awaiting deploy):** 1. ✅ OIDC provider on — `modules/hosts/heimdallr/configuration.nix` wires `oidcHmacSecretFile`, `oidcIssuerPrivateKeyFile`, and `settingsFiles = [ oidc_clients ]`, in the same change as the first client (this ticket's original step order was wrong; provider-before-client makes Authelia refuse to start). 2. ✅ `authorization_code` client registered — `authelia/oidc_clients` in `modules/hosts/heimdallr/secrets.yaml` now holds: - `client_id: forgejo`, `authorization_policy: one_factor`, `consent_mode: implicit` - `redirect_uris: [https://jo.et0.pw/user/oauth2/authelia/callback]` (bound to the auth source being named `authelia` — rename one, rename both) - `require_pkce: true` / `S256`, `token_endpoint_auth_method: client_secret_basic`, scopes `openid email profile groups` - secret: minted with `authelia crypto hash generate pbkdf2` — **digest** in heimdallr's client entry, **plaintext** in bifrost's `modules/hosts/bifrost/secrets.yaml` as `forgejo/oidc_client_secret`, wired as a root-owned sops secret on bifrost so the hand-run step can read it at `/run/secrets/forgejo/oidc_client_secret`. Neither value ever appeared on a terminal. 3. ✅ (config half) `services.forgejo.settings.oauth2_client.ENABLE_AUTO_REGISTRATION = true` on bifrost. **Hand-run step (operator, on bifrost, after both hosts carry the new closures):** ``` forgejo-admin admin auth add-oauth \ --name authelia \ --provider openidConnect \ --key forgejo \ --secret "$(cat /run/secrets/forgejo/oidc_client_secret)" \ --auto-discover-url https://auth.et0.pw/.well-known/openid-configuration \ --scopes "openid email profile" ``` Note the ordering: heimdallr must be deployed **first** (bifrost is unarmed anyway — flake-ops#44) — the auth source is harmless to add early, but a login attempt before heimdallr carries the client gets `invalid_client` at Authelia. **Verify (last four acceptance boxes):** web login via the Authelia button; `git clone` over HTTPS with a token, `just forgejo`, and SSH clones are all untouched by design — the vhost stays out of forward_auth. **Known cost stands:** the auth source lives in Forgejo's database; a re-provisioned bifrost needs the command run again (same class as the first admin account).
Author
Collaborator

Complete, owner-verified 2026-08-09: web login through Authelia works on jo.et0.pw, existing admin account linked via the one-time bridge; git-over-HTTPS tokens, SSH clones and the API were never in the OIDC path by design.

Every acceptance box:

  • OIDC provider active on heimdallr (discovery endpoint public)
  • authorization_code client forgejo in authelia/oidc_clients
  • Auth source added on the box (forgejo-admin admin auth add-oauth --name authelia …, source id 1)
  • ENABLE_AUTO_REGISTRATION = true (bifrost, live)
  • Web login via Authelia — verified
  • HTTPS clone / API / SSH — unaffected

One deviation from the client registration as first minted: require_pkce is now false — Forgejo's openidConnect source sends no code_challenge (no CLI flag for it), and Authelia hard-rejects the authorization when PKCE is enforced ("Clients must include a 'code_challenge'…"). A confidential client with a pbkdf2-hashed secret over TLS stands without it. If a PKCE toggle appears in Forgejo's auth-source UI, re-enforcing is one sops edit on heimdallr.

Context that made this land today: bifrost was armed and took its first flake-ops closure (flake-ops#44), which is what put the decrypted client secret and auto-registration on the box.

Complete, owner-verified 2026-08-09: web login through Authelia works on `jo.et0.pw`, existing admin account linked via the one-time bridge; git-over-HTTPS tokens, SSH clones and the API were never in the OIDC path by design. Every acceptance box: - ✅ OIDC provider active on heimdallr (discovery endpoint public) - ✅ `authorization_code` client `forgejo` in `authelia/oidc_clients` - ✅ Auth source added on the box (`forgejo-admin admin auth add-oauth --name authelia …`, source id 1) - ✅ `ENABLE_AUTO_REGISTRATION = true` (bifrost, live) - ✅ Web login via Authelia — verified - ✅ HTTPS clone / API / SSH — unaffected One deviation from the client registration as first minted: `require_pkce` is now **false** — Forgejo's `openidConnect` source sends no `code_challenge` (no CLI flag for it), and Authelia hard-rejects the authorization when PKCE is enforced ("Clients must include a 'code_challenge'…"). A confidential client with a pbkdf2-hashed secret over TLS stands without it. If a PKCE toggle appears in Forgejo's auth-source UI, re-enforcing is one sops edit on heimdallr. Context that made this land today: bifrost was armed and took its first flake-ops closure (flake-ops#44), which is what put the decrypted client secret and auto-registration on the box.
Commenting is not possible because the repository is archived.
No description provided.