AbuseGraph + Auth0
Score Auth0 signups and logins from a Post-Login Action — block on deny, enable MFA on challenge.
Quick path
- Add
ABUSEGRAPH_SECRET_KEYandABUSEGRAPH_SITEas Action secrets. - Create a Post-Login Action that POSTs to
https://abusegraph.com/api/v1/check. - Optionally forward browser
toCheckBodyfrom Universal Login custom UI.
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
Action secrets (Auth0 Dashboard → Actions → Library):
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
Auth0 Actions use Deno fetch — POST directly to https://abusegraph.com/api/v1/check. Events: signup | login | password_reset | email_change. Optional sessionId.
Browser signals (recommended)
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 })
// Forward sdk.toCheckBody(collected, { email, event: "signup",
// verdictToken: edge.verdictToken.token }) into your Action via app_metadataAuth0 Post-Login Action
Detect signup via logins_count === 1 or screen_hint=signup. Block on block; enable MFA on challenge.
exports.onExecutePostLogin = async (event, api) => {
const email = event.user.email
if (!email) return
const isSignup =
event.stats?.logins_count === 1 ||
event.request?.query?.screen_hint === "signup"
const browser = event.user.app_metadata?.abusegraph ?? {}
const res = await fetch(
event.secrets.ABUSEGRAPH_CHECK_URL ||
"https://abusegraph.com/api/v1/check",
{
method: "POST",
headers: {
"content-type": "application/json",
"x-api-key": event.secrets.ABUSEGRAPH_SECRET_KEY,
"x-abusegraph-site": event.secrets.ABUSEGRAPH_SITE,
},
body: JSON.stringify({
email,
userId: event.user.user_id,
event: isSignup ? "signup" : "login",
ip: event.request?.ip,
site: event.secrets.ABUSEGRAPH_SITE,
...browser,
}),
},
)
if (res.status === 402) return // credits exhausted — fail open or deny
const result = await res.json()
if (result.verdict === "block") {
api.access.deny("Account blocked by abuse checks")
}
if (result.verdict === "challenge") {
api.multifactor.enable("any")
}
}Lifecycle events
- login — same Action;
isSignupis false whenlogins_count > 1. - password_reset — add a Post-Change-Password Action with
event: "password_reset". - email_change — hook email update flow 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 screen_hint=signup using sk_test_…. Confirm Activity shows the check and blocked users cannot complete login.