AbuseGraphDocs
Guides

AbuseGraph + Supabase

Score Supabase Auth signup and login from an Edge Function — forward browser toCheckBody for a full score.

Quick path

  1. Set ABUSEGRAPH_* secrets on your Edge Function.
  2. Deploy a function that POSTs to https://abusegraph.com/api/v1/check.
  3. On the client, collect SDK signals and call the function before/after signUp / signInWithPassword.

Keys

KeyEnvUse
SecretABUSEGRAPH_SECRET_KEY (sk_test_… / sk_live_…)Server /api/v1/check
PublishableNEXT_PUBLIC_ABUSEGRAPH_PUBLISHABLE_KEY (pk_test_…)Browser SDK
SiteABUSEGRAPH_SITEDNS-verified domain

Free includes 1,000 live API checks / month after DNS verify. Test keys never bill.

Env

Edge Function secrets:

ABUSEGRAPH_SITE=yourdomain.com
ABUSEGRAPH_CHECK_URL=https://abusegraph.com/api/v1/check
ABUSEGRAPH_SECRET_KEY=sk_test_…
NEXT_PUBLIC_ABUSEGRAPH_PUBLISHABLE_KEY=pk_test_…

Use sk_live_… / pk_live_… in production after DNS verify.

Server check

Supabase Edge Functions use Deno fetch — POST to https://abusegraph.com/api/v1/check. Events: signup | login | password_reset | email_change. Optional sessionId.

init + collect + evaluate + toCheckBody — NEVER bare sdk.check() alone as the authoritative decision.

import { init } from "@abusegraph/sdk"

const sdk = init({
  publicKey: import.meta.env.VITE_ABUSEGRAPH_PUBLISHABLE_KEY!, // pk_test_…
})

export async function collectForAuth(
  email: string,
  event: "signup" | "login" | "password_reset" | "email_change",
) {
  const collected = await sdk.collect()
  const edge = await sdk.evaluate(collected, { email })
  return sdk.toCheckBody(collected, {
    email,
    event,
    verdictToken: edge.verdictToken.token,
  })
}

Supabase Edge Function

import "jsr:@supabase/functions-js/edge-runtime.d.ts"

Deno.serve(async (req) => {
  const { email, event = "signup", userId, sessionId, ...browserFields } =
    await req.json()

  const site = Deno.env.get("ABUSEGRAPH_SITE")!
  const res = await fetch(
    Deno.env.get("ABUSEGRAPH_CHECK_URL") ??
      "https://abusegraph.com/api/v1/check",
    {
      method: "POST",
      headers: {
        "content-type": "application/json",
        "x-api-key": Deno.env.get("ABUSEGRAPH_SECRET_KEY")!,
        "x-abusegraph-site": site,
      },
      body: JSON.stringify({
        email,
        userId,
        sessionId,
        event,
        site,
        ...browserFields,
      }),
    },
  )

  if (res.status === 402) {
    return new Response("credits_exhausted", { status: 402 })
  }

  const result = await res.json()
  if (result.verdict === "block") {
    return Response.json(result, { status: 403 })
  }

  return Response.json(result)
})

Client call before signup:

const browserFields = await collectForAuth(email, "signup")
const { data, error } = await supabase.functions.invoke("abusegraph-check", {
  body: { email, event: "signup", ...browserFields },
})
if (error || data?.verdict === "block") throw new Error("Signup blocked")
await supabase.auth.signUp({ email, password })

Lifecycle events

  • login — invoke the function with event: "login" after signInWithPassword.
  • password_reset — call on reset request with event: "password_reset".
  • email_change — call after email update with event: "email_change".

Platform

Lists, rules, and linked accounts apply on every check — configure in the console. Optional session: POST /api/v1/session. Native apps: iOS, Android, React Native.

Verify

Sign up with sk_test_…, confirm the Edge Function returns a verdict, and see the row in Activity.