@noidme/consent
v0.1.0-beta.0
Published
DPDP consent SDK — one consent engine, many consumers. Renders the tenant's approved §5 notice with per-purpose §6 consent, captures the §6(10) receipt, and exposes a window.noidme decision bridge (+ per-purpose lawful-basis flags) that every noidme SDK (
Maintainers
Readme
DPDP Consent SDK
A tiny (~5 KB, zero-dependency) consent banner for any website. It renders the tenant's approved §5 notice with per-purpose (§6(1)) choices, captures §6 consent against the DPDP Suite using a publishable key, and stores the §6(10) receipt id locally so it won't re-prompt until the notice version changes.
Install
Copy dpdp-consent.js to your site (or serve it from your CDN) and:
<script src="/dpdp-consent.js"></script>
<script>
DPDPConsent.init({
apiBase: 'https://dpdp.yourco.com', // the DPDP Suite base URL
publicKey: 'dpdp_xxx.yyy', // a PUBLISHABLE key (role 'public') — safe in the browser
noticeId: 'n1', version: 1, language: 'en',
// principalId: 'user-123', // optional; else an anonymous id is generated + stored
// ageBand: 'adult', ageAssurance: 'verified',
onConsent: function (state) { /* enable tags for state.purposes */ },
});
</script>How it works
GET /v1/public/notice/:id/:version/:language→ renders purposes + body text.- On accept →
POST /v1/public/consent/begin(nonce) →POST /v1/public/consent/capture(records the selected purposes; §9(3) screening + the §6(10) receipt happen server-side). - The receipt id + chosen purposes are stored in
localStorage(dpdp.consent.<noticeId>.<version>); the banner won't show again for that version. Bump the notice version to re-prompt.
DPDPConsent.hasConsented(noticeId, version, purpose) → boolean — gate your own tags/SDKs on it.
Getting a publishable key
Mint one from the admin console (Org & API keys → role public) or:
POST /v1/keys (operator) { "tenantId": "acme", "role": "public", "label": "website" }A public key can ONLY call the /v1/public/consent/* endpoints — it cannot read consent trails or reach
any admin/tenant surface. CORS is open on those endpoints (they're meant for browsers).
One consent engine, many consumers (window.noidme bridge)
After init(), the SDK exposes a stable decision surface and installs a window.noidme-compatible bridge, so
any noidme SDK gates on the SAME consent decision without embedding its own consent logic — glassprint
(device identification), noidme.js (network enforcement), or your own tags:
DPDPConsent.isGranted('analytics') // → boolean (consented, or a permitted non-consent basis)
DPDPConsent.basisFor('fraud-prevention')// → 'consent' | 'terms' | 'legal-obligation' | ...
DPDPConsent.grantedPurposes([...]) // → the subset currently granted
window.noidme.isGranted('device-identification') // the bridge glassprint reads (never clobbers a real noidme.js)glassprint then works with zero glassprint-side code — its ConsentGate already reads window.noidme.isGranted.
Map an SDK's technical purpose to your notice purpose with purposeMap:
DPDPConsent.init({
apiBase, publicKey, noticeId:'n1', version:1,
jurisdiction: 'IN', // drives the lawful-basis guardrail (see below)
purposeMap: { 'device-identification':'analytics', 'security-fraud':'fraud-prevention' },
});Lawful basis — track under consent OR terms & conditions (basis)
By default every purpose needs affirmative §6 consent (the checkbox). Declare a non-consent lawful basis
per purpose to run it bannerless — 'terms' (accepted T&C / contract necessity), 'legal-obligation', or
'legitimate-interest' (GDPR only):
DPDPConsent.init({ apiBase, publicKey, noticeId:'n1', version:1, jurisdiction:'IN',
basis: { 'fraud-prevention':'terms', 'security':'terms', 'device-identification':'terms', analytics:'consent' },
});Honest guardrail. A tracking purpose (analytics, marketing, personalisation, device-identification) under a
non-consent basis is not lawful in a consent-first jurisdiction — DPDP §6 requires free, specific, unbundled
consent (bundling tracking into T&C is not valid consent) and DPDP §7's legitimate-uses list does not cover
commercial tracking. So in jurisdiction:'IN'/'EU' with strictJurisdiction (default true) a tracking
purpose under basis:'terms' fails closed and warns; only service-necessary purposes (essential/security/
fraud) run bannerless. strictJurisdiction:false permits it but warns loudly and records the basis — the data
fiduciary owns that call; the SDK provides the audit trail, not legal cover. Every basis is sent to the Suite on
capture (lawfulBasis) so it lands in the §6(10) trail.
Notes
- Reject records nothing (no affirmative action = no §6 consent). Accept records only the ticked purposes.
- §9(3): if you pass
ageBand:'child'you must also pass a verifiedparentVerificationfor capture to succeed; a self-declaredadultwill not clear tracking/targeted-ad purposes (they're returned inblocked). - Try it: open
demo.html, point it at a running Suite + a publishable key. - Tests:
node --test test/consent.test.js(decision surface + lawful-basis guardrail + bridge).
