@verifast/node
v0.1.0
Published
VeriFast server-side SDK — KYC onboarding links, step-up re-verification, login-risk reporting, and hardened webhook verification. Thin wrapper over the REST API.
Maintainers
Readme
@verifast/node
VeriFast server-side SDK — a thin, zero-dependency (Node ≥ 18) wrapper over the REST
API. The REST API is the product: everything here is plain HTTP you could send with
curl (see /docs on your gateway); the SDK just adds typed shapes and a hardened
webhook verifier so the correct thing is the easy thing.
This runs on your server with your member API key. The key must never reach a
browser or mobile app — the browser half (React KYC component, hosted links, the device
fingerprint) lives in @verifast/kyc-sdk.
Install
npm install @verifast/nodeThe whole integration
import { VeriFast } from "@verifast/node";
const vf = new VeriFast({
apiKey: process.env.VERIFAST_KEY!,
baseUrl: "https://api.verifast.solutions",
});
// 1) KYC a new customer → send them the hosted link (or feed the token to the SDK):
const kyc = await vf.createOnboarding({
subjectRef: user.id, // your pseudonymous customer id
webhookUrl: "https://you.mn/vf-webhook", // outcome pushed to you, signed
});
sendToCustomer(kyc.hostedUrl);
// 2) On EVERY login (the account-takeover defense — "just connect"):
// the browser computed deviceId via @verifast/kyc-sdk/fingerprint or /fingerprint.js
const decision = await vf.reportLogin({ subjectRef: user.id, deviceId, ip: req.ip });
if (decision.action === "step_up") {
return res.json({ challenge: decision.reverificationUrl }); // face vs enrolled KYC
}
// 3) Webhook route — verify EVERY callback (raw body bytes, not re-parsed JSON!):
const secret = process.env.VERIFAST_WEBHOOK_SECRET!; // vf.fetchWebhookSecret(), cached
app.post("/vf-webhook", express.raw({ type: "*/*" }), (req, res) => {
const event = vf.verifyWebhook(req.headers, req.body, { secret }); // throws on forgery
if (event.event === "reverification.completed" && !event.verified) {
lockAccount(event.subjectRef); // the impostor signal
}
res.sendStatus(200);
});Why verifyWebhook matters
It implements the checks integrators get wrong by hand: timing-safe HMAC-SHA256
comparison, a replay window on the signed timestamp (default 300 s), and raw-body
discipline (re-serializing the JSON changes the bytes and breaks the signature — that's
by design). A WebhookVerificationError means forged or replayed: never process it.
The test suite verifies against a vector signed by the Python gateway implementation, so
the two sides can't drift.
API
createOnboarding({subjectRef, redirectUrl?, webhookUrl?})→ hosted KYC linkcreateReverification({subjectRef, …})→ step-up link (409 if no verified KYC anchor)reportLogin({subjectRef, deviceId?, ip?, country?, lat?, lon?, userAgent?})→allow | step_up(+ link)onboardingStatus(id)— polling fallbackfetchWebhookSecret()— fetch once, keep in your secret storeverifyWebhook(headers, rawBody, {secret, toleranceSeconds?})→ parsed event or throws
Not included (yet): the blind-screening leg (ALLOW/REVIEW/BLOCK with OPRF-blinded lookups) — that requires the VOPRF client, available today in the Python SDK; a JS port is a planned follow-up.
Build & test
npm run build # tsc → dist/
npm test # builds + node --test (includes the cross-language webhook vector)