Better Auth läuft auf Ihrem eigenen Server, also geht die Anfrage, die einen Besucher registriert, durch Ihren Code. Ein databaseHooks.user.create.before-Hook läuft, bevor Better Auth einen Nutzer anlegt, ob aus dem Formular mit E-Mail und Passwort oder aus dem Callback eines Social Providers, und bekommt die Header der Anfrage: Dort fragt er Bot Protection und lehnt den Nutzer bei block ab. Diese Anleitung gilt für Better Auth 1.x.
Der Tag
Wie in der Anleitung Ihres Frameworks (Next.js, Express), im Layout Ihrer Seiten, damit die Registrierungsseite ihn hat:
<script src="https://korzen.dev/v1/tag.js" data-site-key="YOUR_SITE_KEY" async></script>
Der Aufruf
Der Client ist eine einzige Datei ohne Abhängigkeiten: Er fragt /v1/decide, gibt bei Ihrem Timeout auf und lässt den Besucher bei jedem Fehler durch. Laden Sie ihn neben Ihre Better-Auth-Konfiguration und lesen Sie ihn, bevor Sie ihn committen:
curl -o lib/bot-protection.js https://korzen.dev/bot-protection/sdk/korzen.js curl -o lib/bot-protection.d.ts https://korzen.dev/bot-protection/sdk/korzen.d.ts # types, for TypeScript
Dann der Hook, und ein Feld am Nutzer, das die request_id der Entscheidung aufbewahrt:
// lib/auth.js import { betterAuth } from "better-auth"; import { APIError } from "better-auth/api"; import { createClient, fromHeaders, ipFrom } from "./bot-protection.js"; const korzen = createClient({ apiKey: process.env.KORZEN_API_KEY, url: "https://korzen.dev", timeoutMs: 200 }); export const auth = betterAuth({ // your database, emailAndPassword, socialProviders ... user: { additionalFields: { korzenRequestId: { type: "string", required: false, input: false } }, }, databaseHooks: { user: { create: { before: async (user, ctx) => { if (!ctx?.headers) return; // a user your own code creates, outside a request const d = await korzen.decide( fromHeaders(ctx.headers, { ip: ipFrom.vercel, // your platform's: see "The visitor's IP" path: "/signup", method: ctx.request?.method ?? "POST", }), ); if (d?.action === "block") { throw new APIError("BAD_REQUEST", { message: "We could not create your account." }); } // "challenge": ask for a phone number, or another check, before the account does anything return { data: { ...user, korzenRequestId: d?.request_id ?? null } }; }, }, }, }, });
Dann einmal npx auth@latest generate (oder migrate, mit dem eigenen Kysely-Adapter von Better Auth), damit die Datenbank die neue Spalte hat.
Die IP des Besuchers
Der Hook läuft auf Ihrem Server, also ist die IP die, die die Anleitung Ihrer Plattform nennt: ipFrom.vercel auf Vercel, ipFrom.cloudflare, wenn Ihr Server Traffic ausschließlich von Cloudflare annimmt, ipFrom.lastForwarded hinter Ihrem eigenen Load Balancer oder nginx. Ein Header, den der Besucher schreiben kann, lässt einen Bot seine IP selbst wählen.
Die Aktion anwenden
Lesen Sie nur action. Bei block antworten Sie mit demselben Fehler, den die Aktion zeigt, wenn sie aus einem anderen Grund scheitert, damit ein Bot nichts lernt. Bei challenge nutzen Sie die Prüfung, die Sie schon haben (ein CAPTCHA, ein Code per E-Mail oder Telefon); ohne eine machen Sie weiter. Das Konto beginnt im Monitor-Modus, in dem action immer allow ist: Für Besucher ändert sich nichts, bis Sie den Block-Modus einschalten.
Die request_id aufbewahren
Jede Antwort hat eine request_id. Bewahren Sie sie bei dem auf, was die Aktion erstellt hat, in einer Spalte des Kontos oder der Bestellung, oder in einer Logzeile mit deren ID. Sie benennt diese Entscheidung, wenn Sie uns dazu etwas fragen, und lässt Sie später herausfinden, welche Antwort ein Fake-Konto durchgelassen hat.
Auf Ihrem Rechner testen
Starten Sie Ihren Server und öffnen Sie die Registrierungsseite auf localhost. Seiten auf localhost akzeptieren den Tag immer und erscheinen nie als Domain. Lokal ist die IP 127.0.0.1 oder ::1, und das ist in Ordnung. Läuft die App auf Ihrem Rechner in Docker, sieht sie stattdessen die Adresse der Bridge, etwa 172.17.0.1: Die Challenge-Seite bringt Sie trotzdem zu localhost zurück, denn der Tag hat Ihre Seite dort gesehen.
- Setzen Sie
KORZEN_API_KEYin.envund öffnen Sie die Registrierungsseite. In den Entwicklertools des Browsers erscheint nach ein paar Sekunden das_kz-Cookie. - Registrieren Sie sich. Die Entscheidung erscheint auf der Seite Decisions des Dashboards unter
/signup, und der neue Nutzer hat seinekorzenRequestId. - Prüfen Sie fail-open: Setzen Sie einen falschen Schlüssel und registrieren Sie sich erneut. Der Aufruf antwortet
401, und die Registrierung geht durch.
Häufige Fehler
- Ein
hooks.beforenur auf/sign-up/email. Registrierungen mit Google oder GitHub erreichen ihn nie; der Database Hook sieht jeden neuen Nutzer. - Auch in
session.createfragen. Jeder Aufruf zählt als Entscheidung: Hier genügt eine pro neuem Konto; schützen Sie Login und Checkout in ihren eigenen Handlern. /v1/decideaus dem Browser aufrufen. Der API key würde jeden Besucher erreichen; der Aufruf gehört auf Ihren Server.- Kein Timeout, oder ein Fehler, der die Registrierung stoppt. Jede andere Antwort als
200, und keine Antwort rechtzeitig, bedeutet: den Besucher durchlassen. - Eine Zeile in Ihrem Log, die mit
korzen: the visitor's IP sent to Bot Protectionbeginnt. Der Client schreibt sie einmal, bei der ersten Entscheidung, deren IP aus Ihrem eigenen Netz stammt (die eines Load Balancers, eines Proxys, eines Containers), was Bot Protection wie keine IP wertet: Setzen Sieipauf die Adresse des Besuchers (die IP des Besuchers). In Docker auf Ihrem Rechner ist es die Adresse der Bridge, und dort harmlos.