Korzen
Bot Protection

Auth0

Bot Protection met Auth0: een pre-user-registration Action die het vraagt voordat de gebruiker wordt aangemaakt, en wat die wel en niet kan zien.

Auth0 draait een Action voordat het een gebruiker aanmaakt die zich registreert met een wachtwoord of een passwordless code (een Database- of Passwordless-connectie), en die Action kan Bot Protection aanroepen en de registratie weigeren. De registratiepagina is van Auth0, op het domein van Auth0, dus de Action heeft geen _kz-cookie: de beslissing steunt op het IP-adres en de User-Agent.

De tag

Zet de tag op je eigen pagina's, waar mensen binnenkomen voordat ze zich registreren en waar ze inloggen en betalen, zodat die beslissingen de token hebben:

<script src="https://korzen.dev/v1/tag.js" data-site-key="YOUR_SITE_KEY" async></script>

De call

De Action. Auth0 geeft hem in totaal 20 seconden; de call houdt zich aan je timeout, en al het andere laat de registratie door:

// Auth0 Dashboard: Actions, Triggers, pre-user-registration; a new Action.
// Add the secret KORZEN_API_KEY to the Action.
exports.onExecutePreUserRegistration = async (event, api) => {
  let d = null;
  try {
    const res = await fetch("https://korzen.dev/v1/decide", {
      method: "POST",
      headers: { Authorization: `Bearer ${event.secrets.KORZEN_API_KEY}`, "Content-Type": "application/json" },
      body: JSON.stringify({
        ip: event.request.ip,
        user_agent: event.request.user_agent,
        ja4: event.security_context?.ja4 || undefined, // behind Cloudflare only: left out without it
        headers: { "accept-language": event.request.language },
        path: "/signup",
        method: "POST",
      }),
      signal: AbortSignal.timeout(200),
    });
    if (res.ok) d = await res.json();
  } catch {} // fail open
  if (d?.action === "block") {
    api.access.deny("korzen_block", "We could not create your account.");
    return;
  }
  // "challenge": Auth0's own CAPTCHA or MFA, if your plan has them
  if (d?.request_id) api.user.setAppMetadata("korzen_request_id", d.request_id);
};

Auth0 geeft de Action de taal en de User-Agent van de browser, maar geen van de andere headers van het verzoek, dus de call stuurt alleen accept-language mee: de headers die één browserbuild gelijk stuurt, die Bot Protection leest in de calls van je eigen server, ontbreken hier.

Op een tenant waarvan het verkeer via Cloudflare loopt (de Enterprise-abonnementen van Auth0) heeft het event ook security_context.ja4: de JA4 van de TLS-handshake van de bezoeker met de edge van Auth0, in dezelfde vorm die Bot Protection van Cloudflare voor je eigen server leest. De call stuurt hem mee als ja4 als hij er is; zonder blijft het veld weg.

Het IP-adres van de bezoeker

event.request.ip is het adres dat Auth0 bereikte, en dat is al dat van de bezoeker: niets in te stellen. Gebruik je een eigen domein achter je eigen proxy, kijk dan in de instellingen voor custom domains van Auth0 hoe het IP-adres van de bezoeker wordt doorgegeven.

De beslissingen van Auth0 komen zonder het token van de tag. Heeft Bot Protection je tag op andere pagina's gezien, dan telt een ontbrekend token een beetje mee in de score (no_token), maar nooit genoeg voor bot op zichzelf.

Voer de actie uit

Lees alleen action. Bij block antwoord je met dezelfde foutmelding die de Action toont als hij om een andere reden mislukt, zodat een bot niets leert. Bij challenge gebruik je de controle die je al hebt (een CAPTCHA, een code per e-mail of telefoon); heb je er geen, ga dan door. Het account begint in monitor mode, waar action altijd allow is: voor bezoekers verandert er niets tot je block mode aanzet.

Bewaar de request_id

Elk antwoord heeft een request_id. Bewaar die bij wat de actie heeft aangemaakt, in een kolom van het account of de bestelling, of in een logregel met hun id. Zo weet je welke beslissing het was als je ons ernaar vraagt, en kun je later nagaan welk antwoord een nepaccount binnenliet.

Test het op je eigen machine

Gebruik je development tenant. In de Action-editor draait Test hem met een voorbeeld-event; registreer je daarna op je loginpagina, en de beslissing verschijnt op de pagina Beslissingen van het dashboard onder /signup. Controleer fail-open met een verkeerd KORZEN_API_KEY-secret: de registratie gaat door.

Veelgemaakte fouten

  • Registraties via social login. Google, GitHub en dergelijke draaien de pre-user-registration trigger niet; een post-login Action kan Bot Protection bij hun eerste inlog naar hen vragen.
  • /v1/decide aanroepen vanuit de browser. De API key zou dan bij elke bezoeker terechtkomen; de call hoort op je server.
  • Geen timeout, of een fout die de registratie tegenhoudt. Elk ander antwoord dan 200, en geen antwoord op tijd, betekent: laat de bezoeker door.