October 13, 2026

Twee deuren: zo werkt inloggen in Justflows 0.3.6

Je klanten en je redacteuren loggen nu op verschillende plekken in, en de server zorgt ervoor dat geen van beide deuren de andere verraadt. Waarom we ze hebben gesplitst, en wat daarvoor nodig was.

Twee deuren: zo werkt inloggen in Justflows 0.3.6
De meeste CMS'en hebben één inlogpagina. Je klanten gebruiken die, je redacteuren gebruiken die, en elke bot die wachtwoorden probeert ook. Dat is handig, maar het zet twee heel verschillende groepen achter één deur, en het maakt veel beveiligingsadvies hol.
Neem de klassieke tip: verplaats je admin naar een geheime URL. In Justflows kon dat al onder Beveiliging → Admin-URL. Maar tot 0.3.6 accepteerde /login nog steeds wachtwoorden van medewerkers, en na het inloggen stuurde het je vrolijk door naar het verborgen adminpad. Het verplaatsen van de admin hield dus geen wachtwoordraders tegen en hield het pad ook niet geheim. In 0.3.6 hebben we dat goed opgelost, en onderweg gaven we leden een inlogpagina die er echt uitziet als je site.

Elke flow bestaat twee keer

Er zijn nu twee ingangen, één per doelgroep:
  • Leden (bezoekers, klanten, abonnees) gebruiken /login, /register, /forgot-password en /reset-password. Deze pagina's gebruiken je thema en worden bewerkt in de pagebuilder.
  • Medewerkers (iedereen die in de admin terechtkomt) gebruiken <adminpad>/login, /forgot-password en /reset-password, in de eigen vormgeving van de admin. Medewerkers kunnen zich niet registreren: ze worden uitgenodigd.
Zodra het adminpad is verplaatst, geven /admin/login en de rest van /admin/* een 404. De ledenpagina's linken nooit naar het adminpad en noemen het ook niet.

Een pagina verbergen is niet genoeg

Beide ingangen sturen hun verzoeken naar dezelfde API, dus alleen de pagina voor medewerkers verbergen zou voor een script niets veranderen. Elk verzoek zegt daarom van welke ingang het komt: de adminpagina's sturen het adminpad mee waarvandaan ze zijn geladen, de ledenpagina's sturen niets. Een verzoek telt alleen als adminverzoek als dat pad exact overeenkomt.
Vanaf daar zijn de regels streng:
  • Inloggen. Een medewerkersaccount op de ledenpagina krijgt precies wat een fout wachtwoord krijgt: dezelfde status, dezelfde inhoud, dezelfde vertraging. Dat gebeurt vóór een vraag om tweestapsverificatie, dus niets bevestigt dat het wachtwoord klopte of dat het om een medewerker gaat. De poging wordt wel gelogd.
  • Wachtwoord vergeten. De ledenpagina mailt nooit een medewerkersaccount, en de adminpagina mailt nooit een lid. In beide gevallen is het antwoord het gebruikelijke "als er een account bestaat, is er een link onderweg".
  • Wachtwoord resetten. Een resetlink werkt alleen op de ingang waarvoor hij is gemaild. Een link voor medewerkers wijst naar het verborgen adminpad, dus dat pad komt alleen terecht bij de eigen eigenaar.
Het resultaat: wachtwoorden van medewerkers raden op /login heeft geen zin, en niets aan de openbare kant van de site verraadt waar de admin zit.

Inlogpagina's die eruitzien als je site

De ledenpagina's zijn vier gewone pagina's van een nieuw ingebouwd contenttype Inloggen, onder Content → Inloggen. Elke pagina bevat een blok Accountformulier dat het eigenlijke formulier toont: inloggen, account aanmaken, wachtwoord vergeten of wachtwoord resetten. Daaromheen zet je wat je wilt, met de header, footer en stijlen van je thema.
Omdat deze pagina's formulieren met inloggegevens bevatten, zet het contenttype een paar dingen vast die geen instelling, thema of plugin kan afzwakken: ze worden verstuurd met private, no-store, ze zijn noindex, en ze blijven buiten de paginacache, zoekresultaten, sitemaps, statische exports en openbare content-API's. De resetpagina haalt ook het token uit de adresbalk voordat er iets anders draait.

Ingelogd, op een volledig gecachete site

Nu het lastige deel. Justflows serveert openbare pagina's uit een gedeelde cache, en dat is een groot deel van de reden waarom sites snel zijn. Maar een gecachete pagina kan niet weten wie ernaar kijkt. Hoe laat je dan Mijn account zien in plaats van Inloggen?
De server rendert altijd de uitgelogde versie. Als je inlogt, zet Justflows ook een klein leesbaar cookie, jf_signed_in=1, dat helemaal geen identiteit bevat. Een klein script ziet dat cookie, vraagt /api/auth/status wie je bent en past de links aan in de browser: inloglinks worden Mijn account, registratielinks verdwijnen en de header krijgt Uitloggen. Anonieme bezoekers doen dat verzoek nooit, en de pagina blijft voor iedereen gecachet.
// What /api/auth/status returns: no role, never the admin path
{ "signedIn": true, "email": "you@example.com", "accountUrl": "/account" }

Als de site dicht is

Een site in onderhoudsmodus, of een site die nog niet openbaar is, sluit nu ook de deur voor leden: de inlog- en accountpagina's tonen de onderhoudspagina, en het ledendeel van de API antwoordt met 503 voordat het een account opzoekt. Inloggen voor medewerkers, de beheer-API en MCP blijven open, zodat jij, je deployscripts en je AI-agents aan de site kunnen blijven werken terwijl bezoekers wachten.

Wat je moet doen

Meestal niets: bestaande sites krijgen de vier ledenpagina's zodra een van de URL's voor het eerst wordt geopend, en pagina's die je al hebt aangepast, blijven ongemoeid. Eén gewoonte moet veranderen. Medewerkers loggen nu in op /admin/login (of je eigen adminpad), ook op sites die de admin nooit hebben verplaatst. Typen je vingers automatisch /login, pas dan je bladwijzer aan.
Release notes van Justflows 0.3.6