feat(identity): offer account creation to visitors who have none #344

Sammanfogat
supernaut sammanfogade 2 incheckningar från feat/forgejo-create-account-link in i main 2026-08-02 19:26:59 +00:00
Ägare

Closes #336.

A visitor who clicks "Sign In" on the git host is redirected into the identity provider's username
prompt. Local registration is off — the portal is the only sign-up path — so someone without an
account reaches a form they cannot complete and is offered nothing else.

The fix goes in the git host's navbar, not the identity provider. Kanidm has no configuration
for this: its documented customisation surface is site display name, site image, per-application
display names and images, and a custom stylesheet — and CSS content: cannot produce a clickable
anchor. Rewriting the response body at the proxy to inject one is technically viable and explicitly
rejected: it would put a pre-1.0 third-party module on the TLS-terminating edge for a cosmetic gain.

Three parts:

  1. A real anchor, via a custom/extra_links.tmpl hook rendering into the navbar's left menu,
    gated on the visitor being signed out. Label and path both follow ctx.Locale.Lang, so an English
    reader gets "Create account" → /en/signup and a Swedish one "Skapa konto" → /signup. The
    application's own register key is deliberately unused: it labels the disabled local sign-up
    route, so it would name the wrong destination.
  2. A plain-text note on the sign-in page for people arriving by bookmark or mail, via the
    existing stylesheet override. The comment states that this is text and not a link, and that
    generated content is not user-selectable in Chromium or WebKit, so the URL is kept short enough to
    retype.
  3. A health-gate entry covering the sign-in page. The concealment CSS has already broken once
    when selectors moved; a rule that silently no-ops after an upgrade is worse than no rule.

The selector is scoped, and that was verified mechanically

form#login alone is not precise — it is reused on the password, TOTP, backup-code,
mechanism-choose and device-login templates. Running a real :has() engine over every 1.10.4 login
template plus the live page:

login.html                 form#login=1   our-selector=1
login_password.html        form#login=1   our-selector=0
login_totp.html            form#login=1   our-selector=0
login_backupcode.html      form#login=1   our-selector=0
login_mech_choose.html     form#login=1   our-selector=0
oauth2_device_login.html   form#login=1   our-selector=0

Verification

The navbar hook was rendered by a real instance and curled: the link appears between Explore and
Help when signed out and is absent when signed in. The sign-in note was screenshotted in headless
Chromium in both dark and light schemes, and confirmed absent when the same page is rebuilt with the
password-step form.

ansible-playbook site.yml --syntax-check clean, ansible-lint 0 failures, Prettier and
markdownlint clean. The health gate stays a pure variable comparison — no network, no session — so it
behaves identically under --check rather than fabricating a pass.

Note the sign-in page no longer offers account recovery, which was disabled in production earlier
today; the rule assumes nothing about that link.

Closes #336. A visitor who clicks "Sign In" on the git host is redirected into the identity provider's username prompt. Local registration is off — the portal is the only sign-up path — so someone without an account reaches a form they cannot complete and is offered nothing else. **The fix goes in the git host's navbar, not the identity provider.** Kanidm has no configuration for this: its documented customisation surface is site display name, site image, per-application display names and images, and a custom stylesheet — and CSS `content:` cannot produce a clickable anchor. Rewriting the response body at the proxy to inject one is technically viable and explicitly rejected: it would put a pre-1.0 third-party module on the TLS-terminating edge for a cosmetic gain. Three parts: 1. **A real anchor**, via a `custom/extra_links.tmpl` hook rendering into the navbar's left menu, gated on the visitor being signed out. Label and path both follow `ctx.Locale.Lang`, so an English reader gets "Create account" → `/en/signup` and a Swedish one "Skapa konto" → `/signup`. The application's own `register` key is deliberately unused: it labels the disabled local sign-up route, so it would name the wrong destination. 2. **A plain-text note on the sign-in page** for people arriving by bookmark or mail, via the existing stylesheet override. The comment states that this is text and not a link, and that generated content is not user-selectable in Chromium or WebKit, so the URL is kept short enough to retype. 3. **A health-gate entry** covering the sign-in page. The concealment CSS has already broken once when selectors moved; a rule that silently no-ops after an upgrade is worse than no rule. ### The selector is scoped, and that was verified mechanically `form#login` alone is **not** precise — it is reused on the password, TOTP, backup-code, mechanism-choose and device-login templates. Running a real `:has()` engine over every 1.10.4 login template plus the live page: ``` login.html form#login=1 our-selector=1 login_password.html form#login=1 our-selector=0 login_totp.html form#login=1 our-selector=0 login_backupcode.html form#login=1 our-selector=0 login_mech_choose.html form#login=1 our-selector=0 oauth2_device_login.html form#login=1 our-selector=0 ``` ### Verification The navbar hook was rendered by a real instance and curled: the link appears between Explore and Help when signed out and is absent when signed in. The sign-in note was screenshotted in headless Chromium in both dark and light schemes, and confirmed absent when the same page is rebuilt with the password-step form. `ansible-playbook site.yml --syntax-check` clean, `ansible-lint` 0 failures, Prettier and markdownlint clean. The health gate stays a pure variable comparison — no network, no session — so it behaves identically under `--check` rather than fabricating a pass. Note the sign-in page no longer offers account recovery, which was disabled in production earlier today; the rule assumes nothing about that link.
supernaut lade till 2 incheckningar 2026-08-02 15:56:09 +00:00
Local registration on the Forgejo app is disabled — the portal is the only
sign-up path — so a visitor who clicks "Sign In" is redirected into Kanidm's
username prompt with no route to creating an account.

Kanidm has no configuration for adding a link there: its documented
customisation surface is the site display name, the site image, per-application
display names/images, and a custom stylesheet, and CSS `content` cannot produce
a clickable anchor. So the fix goes where a supported hook does exist.

- Forgejo navbar: a new custom/extra_links.tmpl hook renders a "Create account"
  link to the portal's sign-up page, gated on the visitor being signed out.
  Added to the prod render loop and to the local preview's.
- Kanidm sign-in page: an override.css rule appends a short sign-up line under
  the username form, scoped to that step with
  `form#login:has(input#username[name="username"])` — Kanidm reuses `form#login`
  for the password, TOTP, backup-code, mechanism-choice and OAuth2 device steps,
  and the :has() qualifier is what keeps the note off them. Text, not a link,
  and English only, for the reasons recorded next to the rule.
- Health gate: the ADR 0038 concealment gate now lists the sign-in page and the
  signed-out navbar among the surfaces to re-verify on an image bump. A CSS rule
  that no-ops after an upgrade fails silently, and this is what makes it visible.
- Runbook: a section on both surfaces and why the Kanidm one is text-only.

Rewriting Kanidm's response body at the edge to inject a real anchor stays out
of scope: a body-rewriting module in the TLS-terminating proxy is too much for a
cosmetic gain.

Closes #336
fix(forgejo): translate the navbar account link and send it to the right locale
Alla kontroller lyckades
ci / ci (pull_request) Successful in 1m36s
1427742f20
The link was a fixed English string pointing at the Swedish sign-up page, so a
reader using the interface in English saw an untranslated label and landed on
Swedish content. The footer hook already derives both from ctx.Locale.Lang; do
the same here.

Forgejo's own register key stays unused — it labels the disabled local sign-up
route — and a custom locale file would replace the built-in one rather than
extend it, so the label is branched inline.

Refs #336
supernaut tvångsskickade feat/forgejo-create-account-link från 1427742f20
Alla kontroller lyckades
ci / ci (pull_request) Successful in 1m36s
till 160c0cf958
Alla kontroller lyckades
ci / ci (pull_request) Successful in 1m35s
2026-08-02 16:28:38 +00:00
Jämför
supernaut tvångsskickade feat/forgejo-create-account-link från 160c0cf958
Alla kontroller lyckades
ci / ci (pull_request) Successful in 1m35s
till 9d69ce7e1d
Alla kontroller lyckades
ci / ci (pull_request) Successful in 1m32s
2026-08-02 16:35:53 +00:00
Jämför
supernaut tvångsskickade feat/forgejo-create-account-link från 9d69ce7e1d
Alla kontroller lyckades
ci / ci (pull_request) Successful in 1m32s
till ca2b8a11a7
Alla kontroller lyckades
ci / ci (pull_request) Successful in 1m35s
2026-08-02 16:53:47 +00:00
Jämför
supernaut tvångsskickade feat/forgejo-create-account-link från ca2b8a11a7
Alla kontroller lyckades
ci / ci (pull_request) Successful in 1m35s
till 4d04ceb002
Alla kontroller lyckades
ci / ci (pull_request) Successful in 2m8s
2026-08-02 18:30:14 +00:00
Jämför
supernaut tvångsskickade feat/forgejo-create-account-link från 4d04ceb002
Alla kontroller lyckades
ci / ci (pull_request) Successful in 2m8s
till 993a653414
Alla kontroller lyckades
ci / ci (pull_request) Successful in 1m40s
2026-08-02 19:13:49 +00:00
Jämför
supernaut tvångsskickade feat/forgejo-create-account-link från 993a653414
Alla kontroller lyckades
ci / ci (pull_request) Successful in 1m40s
till 324fd0b6b1
Alla kontroller lyckades
ci / ci (pull_request) Successful in 1m50s
2026-08-02 19:19:15 +00:00
Jämför
supernaut tvångsskickade feat/forgejo-create-account-link från 324fd0b6b1
Alla kontroller lyckades
ci / ci (pull_request) Successful in 1m50s
till 41c0551aa8
Alla kontroller lyckades
ci / ci (pull_request) Successful in 1m35s
2026-08-02 19:24:14 +00:00
Jämför
supernaut sammanfogade incheckning fcd91abd9f till main 2026-08-02 19:26:59 +00:00
supernaut tog bort grenen feat/forgejo-create-account-link 2026-08-02 19:26:59 +00:00
Logga in för att delta i denna konversation.
Inga granskare
Ingen milstolpe
Inget projekt
Inga tilldelade
1 deltagare
Notiser
Förfallodatum
Förfallodatumet är ogiltigt eller utanför gränserna. Använd formatet "åååå-mm-dd".

Inget förfallodatum satt.

Beroenden

Inga beroenden satta

Referens
bitborg/bitborg-infra!344
Ingen beskrivning angiven.