AbuseGraph + Firebase Auth
Block risky Firebase signups and logins with beforeUserCreated and beforeUserSignedIn blocking functions.
Quick path
- Set
ABUSEGRAPH_*secrets on Cloud Functions. - Deploy
beforeUserCreated(signup) andbeforeUserSignedIn(login) blocking functions. - Optionally collect browser signals client-side and pass via Callable Function.
Keys
| Key | Env | Use |
|---|---|---|
| Secret | ABUSEGRAPH_SECRET_KEY (sk_test_… / sk_live_…) | Server /api/v1/check |
| Publishable | NEXT_PUBLIC_ABUSEGRAPH_PUBLISHABLE_KEY (pk_test_…) | Browser SDK |
| Site | ABUSEGRAPH_SITE | DNS-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.
Browser signals (recommended)
Load the browser SDK from the AbuseGraph CDN before your signup/login code. The publishable key is safe to expose; send the resulting body to your own backend, which makes the authoritative check with its server-held secret key.
<script src="https://abusegraph.com/sdk/abusegraph.min.js"></script>
<script>
const sdk = AbuseGraph.init({ publicKey: "pk_test_…" });
async function collectForAuth(email, event = "signup") {
const collected = await sdk.collect();
const evaluated = await sdk.evaluate(collected, { email });
return sdk.toCheckBody(collected, {
email,
event,
verdictToken: evaluated.verdictToken?.token,
});
}
</script>POST the returned JSON to your own endpoint. Never treat the browser result as the authoritative decision or put an sk_ key in browser code.
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
- login —
beforeUserSignedInwithevent: "login". - password_reset — call check from a Callable Function on reset with
event: "password_reset". - email_change — call before applying the 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.