AbuseGraphDocs
Guides

AbuseGraph + Firebase Auth

Block risky Firebase signups and logins with beforeUserCreated and beforeUserSignedIn blocking functions.

Quick path

  1. Set ABUSEGRAPH_* secrets on Cloud Functions.
  2. Deploy beforeUserCreated (signup) and beforeUserSignedIn (login) blocking functions.
  3. Optionally collect browser signals client-side and pass via Callable Function.

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

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

Use sk_live_… in production after DNS verify.

Server check

Cloud Functions use 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: process.env.NEXT_PUBLIC_ABUSEGRAPH_PUBLISHABLE_KEY!, // pk_test_…
})

const collected = await sdk.collect()
const edge = await sdk.evaluate(collected, { email })
// Send sdk.toCheckBody(collected, { email, event: "signup",
//   verdictToken: edge.verdictToken.token }) to a Callable Function

Firebase blocking functions

Use ESM imports. Throw HttpsError on block.

beforeUserCreated (signup)

import { beforeUserCreated } from "firebase-functions/v2/identity"
import { HttpsError } from "firebase-functions/v2/https"

export const abusegraphBeforeCreate = beforeUserCreated(async (event) => {
  const email = event.data.email
  if (!email) return

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

  if (res.status === 402) {
    throw new HttpsError("resource-exhausted", "credits_exhausted")
  }

  const result = await res.json()
  if (result.verdict === "block") {
    throw new HttpsError("permission-denied", "Signup blocked")
  }
})

beforeUserSignedIn (login)

import { beforeUserSignedIn } from "firebase-functions/v2/identity"
import { HttpsError } from "firebase-functions/v2/https"

export const abusegraphBeforeSignIn = beforeUserSignedIn(async (event) => {
  const email = event.data.email
  if (!email) return

  const site = process.env.ABUSEGRAPH_SITE!
  const res = await fetch(
    "https://abusegraph.com/api/v1/check",
    {
      method: "POST",
      headers: {
        "content-type": "application/json",
        "x-api-key": process.env.ABUSEGRAPH_SECRET_KEY!,
        "x-abusegraph-site": site,
      },
      body: JSON.stringify({
        email,
        userId: event.data.uid,
        event: "login",
        ip: event.ipAddress,
        site,
      }),
    },
  )

  const result = await res.json()
  if (result.verdict === "block") {
    throw new HttpsError("permission-denied", "Sign-in blocked")
  }
})

Lifecycle events

  • loginbeforeUserSignedIn with event: "login".
  • password_reset — call check from a Callable Function on reset 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

Create a test user with sk_test_…. Blocked signups should throw before the user is created; check Activity for the row.