@khanh2k7/shield-cloud-node
v0.1.0
Published
Server-side SDK for Shield Cloud — verify shield-token with site secret_key (Node, Edge, Express, Next.js).
Maintainers
Readme
@khanh2k7/shield-cloud-node
Server-side SDK for Shield Cloud bot protection.
Verifies the browser shield-token (set after JS challenge / PoW) using your site secret key against POST /v1/verify.
The platform HMAC secret never leaves your Shield deployment. This package is a thin, typed API client + middleware helpers.
Install
npm install @khanh2k7/shield-cloud-node
# or
pnpm add @khanh2k7/shield-cloud-node
yarn add @khanh2k7/shield-cloud-nodeRequirements: Node.js 18+ (native fetch).
Quick start
import { ShieldClient } from "@khanh2k7/shield-cloud-node";
const shield = new ShieldClient({
secretKey: process.env.SHIELD_SECRET_KEY!, // sk_live_...
apiBase: process.env.SHIELD_API_BASE!, // https://your-shield-app.vercel.app
});
// From cookie / header
const result = await shield.verifyNodeRequest(req);
if (!result.ok) {
// 401/403 — run JS challenge on the client
return res.status(result.status).json({ error: result.error });
}
console.log(result.site, result.token.sid, result.token.score);Environment variables (optional if you pass options explicitly):
| Env | Description |
|-----|-------------|
| SHIELD_SECRET_KEY | Site secret (sk_live_...) |
| SHIELD_API_BASE | Shield API origin (or custom https://token.customer.com) |
Express
import express from "express";
import { shieldMiddleware } from "@khanh2k7/shield-cloud-node";
const app = express();
app.use(
shieldMiddleware({
secretKey: process.env.SHIELD_SECRET_KEY!,
apiBase: process.env.SHIELD_API_BASE!,
})
);
app.get("/api/me", (req, res) => {
res.json({ visitor: req.shield });
});On failure the middleware responds:
{ "ok": false, "code": 1124, "error": "...", "msg": "JS challenge required" }with header x-shield-action: challenge.
Next.js App Router
// app/api/private/route.ts
import { assertShield, ShieldAuthError } from "@khanh2k7/shield-cloud-node";
export async function GET(req: Request) {
try {
const visitor = await assertShield(req, {
secretKey: process.env.SHIELD_SECRET_KEY!,
apiBase: process.env.SHIELD_API_BASE!,
});
return Response.json({ site: visitor.site, sid: visitor.token.sid });
} catch (e) {
if (e instanceof ShieldAuthError) return e.toResponse();
throw e;
}
}Or non-throwing:
import { checkShield } from "@khanh2k7/shield-cloud-node";
const result = await checkShield(req, { secretKey, apiBase });
if (!result.ok) return Response.json(result, { status: result.status });Cloudflare Worker / Edge
import { extractFromRequest, verifyShieldToken } from "@khanh2k7/shield-cloud-node";
export default {
async fetch(request, env) {
const token = extractFromRequest(request);
const result = await verifyShieldToken({
secretKey: env.SHIELD_SECRET_KEY,
apiBase: env.SHIELD_API_BASE,
token: token || "",
});
if (!result.ok) {
return new Response("challenge", {
status: 403,
headers: { "x-shield-action": "challenge" },
});
}
return fetch(env.ORIGIN); // proxy
},
};API
new ShieldClient(options)
| Option | Type | Description |
|--------|------|-------------|
| secretKey | string | Site secret (or SHIELD_SECRET_KEY) |
| apiBase | string | API base URL (or SHIELD_API_BASE) |
| fetch | typeof fetch | Custom fetch |
| timeoutMs | number | Default 10000 |
client.verify({ token, deviceId? })
Returns VerifyResult:
// success
{
ok: true,
valid: true,
site: "example.com",
site_id: "...",
site_key: "pk_live_...",
tenant: "Acme",
tenant_id: "...",
plan: "pro",
token: { sid, device_id, score, exp, iat }
}
// failure
{ ok: false, error: "expired" | "missing_token" | ..., status: 401 }Helpers
extractShieldToken({ cookieHeader, shieldTokenHeader })extractFromRequest(req)— FetchRequestextractFromNodeRequest(req)— Express / NodeverifyShieldToken({ token, secretKey, apiBase })— one-shotshieldMiddleware(opts)— ExpressassertShield(req, opts)/checkShield(req, opts)— Next / EdgeShieldAuthError— has.toResponse()
Constants
SHIELD_TOKEN_COOKIE→"shield-token"SHIELD_TOKEN_HEADER→"x-shield-token"
Browser side
Keep using the embed script (not this package):
<script src="https://YOUR_SHIELD/v1/challenge.js"
data-site-key="pk_live_..."
data-api="https://YOUR_SHIELD"
data-mode="auto"></script>Development (monorepo)
cd packages/node
npm install
npm test
npm run buildPublish
# Once: create org https://www.npmjs.com/org/create → shield-cloud
cd packages/node
npm login
npm publish --access publicLicense
MIT
