Changeset af2cc73 in Klonkt for src/views/pages/fan-gate.ejs


Ignore:
Timestamp:
08/18/2026 06:50:08 PM (3 weeks ago)
Author:
Bart <bart@…>
Branches:
main
Children:
d85b66f
Parents:
9da8027
git-author:
Bart <bart@…> (08/18/2026 06:05:22 PM)
git-committer:
Bart <bart@…> (08/18/2026 06:50:08 PM)
Message:

Gastlogin via OpenWebAuth: een fan is een volger, geen accounthouder

fan_only betekende altijd al "mijn volgers op de fediverse", maar de poort vroeg
om een KLONKT-ACCOUNT. Dat is de verkeerde vraag, en hij sloot precies de mensen
buiten voor wie de poort openstond. Nu kan een bezoeker bij zijn EIGEN server
bewijzen dat hij @iemand@ergens is (FEP-61cf), en volgt hij deze site, dan is
hij binnen. Geen account hier, geen wachtwoord hier, geen cookie van een derde.

Wij zijn alleen de TARGET instance. Dat is de prettige helft: de home instance
heeft prive-sleutels nodig, wij alleen publieke. Er staat hier dus geen geheim
van iemand anders. De /magic-kant (Klonkt-gebruikers laten inloggen OP andere
sites) is bewust niet gebouwd -- andere functie.

De handtekening-verificatie is NIET opnieuw geschreven: AP.verifyRequest() doet
dit al voor de inbox, inclusief het vastpinnen van de sleutel op de herkomst van
de actor, een replay-venster en een verplichte digest. Een tweede implementatie
van "is deze aanvraag echt van wie hij zegt" is precies wat je niet wilt.

De drie aanvallen die de FEP noemt, hebben elk een toets:

  • IMPERSONATIE: ?zid= bepaalt niets, alleen het ingewisselde ?owt= telt. Mallory kan een link maken met zid=bob, maar komt terug met een token dat Mallory zegt.
  • OPEN REDIRECT: het ontdekte endpoint moet dezelfde host hebben als het adres dat de bezoeker intypte.
  • DoS: tokens vervallen in minuten, gaan na een keer gebruiken weg, en elke uitgifte veegt de oude op.

Onderweg gemeten en vastgelegd: PKCS#1 v1.5 GOOIT GEEN FOUT bij een verkeerde
sleutel. OpenSSL 3 doet aan implicit rejection en geeft afgeleide onzin terug,
juist zodat niemand aan het foutgedrag kan aflezen of zijn gok klopte. 200
vreemde sleutels: 0 fouten, 0 keer het token. De toets test dus "er komt iets
anders uit", niet "het knalt" -- anders schrijft de volgende lezer weer een
assert.throws die per ongeluk slaagt.

Webfinger op de eigen wortel wijst een home instance naar /owa/token. Alleen
origin + '/'; een ACTOR-uri met een pad blijft een 400, want dat legt
webfinger-bare-host.test.js vast en die keuze draai ik niet om als bijvangst.

De fanpoort toont nu het adresveld als hoofdweg en de lokale inlog als tweede,
en fgate.sub zegt niet langer "ingelogde vrienden" maar wat de poort werkelijk
vraagt.

1135 toetsen groen (was 1106). End-to-end nagelopen op een KOPIE van de
database: token inwisselen zet de sessie en haalt het token uit de URL, een
bewezen volger krijgt de tekst, een bewezen niet-volger krijgt de poort en geen
byte van de inhoud, en anoniem idem.

Co-Authored-By: Claude Opus 5 <claude@…>

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/views/pages/fan-gate.ejs

    r9da8027 raf2cc73  
    1212    <% if (typeof fgTitle !== 'undefined' && fgTitle) { %><p class="fg-which">"<%= fgTitle %>"</p><% } %>
    1313    <p class="fg-sub"><%= t('fgate.sub') %></p>
    14     <p><a class="fg-btn" href="/auth/login?next=<%= encodeURIComponent(fgNext || '/') %>"><%= t('fgate.login') %></a></p>
     14
     15    <%# De hoofdweg: bewijs bij je EIGEN server wie je bent (OpenWebAuth,
     16        FEP-61cf). Dit is de vraag die fan_only altijd al stelde -- volg je deze
     17        site? -- in plaats van "heb je hier een account", wat juist de mensen
     18        buitensloot voor wie de poort openstond. Geen account hier, geen
     19        wachtwoord hier, geen cookie van een derde. %>
     20    <form class="fg-owa" method="post" action="/owa/login">
     21      <input type="hidden" name="next" value="<%= fgNext || '/' %>">
     22      <label class="fg-label" for="fg-handle"><%= t('fgate.owa_label') %></label>
     23      <div class="fg-row">
     24        <input class="fg-input" id="fg-handle" name="handle" type="text" inputmode="email"
     25               autocomplete="username" spellcheck="false" placeholder="@jij@jouwserver.nl" required>
     26        <button class="fg-btn" type="submit"><%= t('fgate.owa_go') %></button>
     27      </div>
     28      <p class="fg-hint"><%= t('fgate.owa_hint') %></p>
     29      <% if (typeof owaError !== 'undefined' && owaError) { %>
     30        <p class="fg-error"><%= t('fgate.owa_failed') %></p>
     31      <% } %>
     32    </form>
     33
     34    <%# De lokale inlog blijft, maar als tweede: die is voor wie hier echt een
     35        account heeft, en dat is de uitzondering. %>
     36    <p class="fg-alt"><a href="/auth/login?next=<%= encodeURIComponent(fgNext || '/') %>"><%= t('fgate.login') %></a></p>
    1537  </div>
    1638</section>
     
    2648  .fg-which { font-style: italic; opacity: .8; margin: 0 0 12px; }
    2749  .fg-sub { opacity: .85; line-height: 1.6; margin: 0 0 20px; }
    28   .fg-btn { display: inline-block; padding: 12px 22px; border-radius: 10px; background: var(--accent,#6b8f71); color: #fff; text-decoration: none; font-weight: 600; }
     50  .fg-btn { display: inline-block; padding: 12px 22px; border-radius: 10px; background: var(--accent,#6b8f71); color: #fff; text-decoration: none; font-weight: 600; border: 0; font-family: inherit; font-size: 1rem; cursor: pointer; }
     51  .fg-owa { margin: 0 0 18px; text-align: left; }
     52  .fg-label { display: block; font-size: .85rem; opacity: .8; margin-bottom: 6px; }
     53  .fg-row { display: flex; gap: 8px; flex-wrap: wrap; }
     54  .fg-input { flex: 1 1 12rem; min-width: 0; padding: 12px 14px; border-radius: 10px;
     55              border: 1px solid rgba(128,128,128,.35); background: transparent; color: inherit;
     56              font-family: inherit; font-size: 1rem; }
     57  .fg-hint { font-size: .8rem; opacity: .65; margin: 8px 0 0; line-height: 1.5; }
     58  .fg-error { font-size: .85rem; margin: 8px 0 0; color: #d66; }
     59  .fg-alt { margin: 0; font-size: .85rem; opacity: .7; }
    2960</style>
Note: See TracChangeset for help on using the changeset viewer.