AgeGate is the browser-side integration for AgeCheck, a privacy-preserving age-verification tool.
AgeCheck issues cryptographically signed age-tier credentials (e.g., 16+, 18+, 21+) without exposing a user's birthdate or identity. Users authenticate with passkeys (WebAuthn) and receive a verifiable credential that websites can validate server-side in milliseconds. It separates demo and production trust paths and makes verification explicit and auditable. AgeCheck is a Derived Credential Issuer.
Within eIDAS 2.0, AgeCheck functions as an attribute attestation issuer for age-related claims, aligned with the European Digital Identity (EUDI) Wallet model. It issues threshold-based age attributes (e.g., "over 18") as verifiable credentials that relying parties can validate without accessing the underlying identity data. In that ecosystem, it plays the role of a specialized attribute provider rather than a full identity wallet provider.
This repository ships two CDN-friendly bundles under v1/:
agegate.js/agegate.min.js: the production-grade flow (popup + postMessage + token decoding + optional backend verification).easy-agegate.js/easy-agegate.min.js: an optional UI wrapper that delegates tolaunchAgeGate()and provides a modal/button.
Both are exposed as a global AgeCheck when loaded via <script>.
These bundles are served at:
https://cdn.agecheck.me/agegate/v1/agegate.jshttps://cdn.agecheck.me/agegate/v1/agegate.min.jshttps://cdn.agecheck.me/agegate/v1/easy-agegate.jshttps://cdn.agecheck.me/agegate/v1/easy-agegate.min.js
Add a referrer policy to prevent the relying-party URL from being sent to the issuer:
<meta name="referrer" content="no-referrer" />
<script src="https://cdn.agecheck.me/agegate/v1/agegate.min.js"></script>Launch an age verification popup and receive a signed credential (JWT VC) in your callback:
<script>
AgeCheck.launchAgeGate({
include: ["session", "pidProvider", "verificationMethod", "loa"],
onSuccess: async (jwt, payload, backendResult) => {
// jwt: the verifiable credential (JWT)
// payload: { agegateway_session, include, authenticationResponse }
// backendResult: whatever your backend returns (if enabled)
console.log("Age credential:", jwt);
},
onFailure: (err) => {
console.error("AgeGate failed:", err);
},
});
</script>AgeGate can call your backend to validate/audit the credential server-side. The default policy is same-origin only, so the safest config is a relative URL:
AgeCheck.launchAgeGate({
backendVerifyUrl: "/api/agecheck/verify",
onSuccess: (jwt, payload, backendResult) => { /* ... */ },
});To allow a cross-origin backend URL, you must opt in and provide an allowlist:
AgeCheck.launchAgeGate({
backendVerifyUrl: "https://api.example.com/agecheck/verify",
backendVerifyAllowCrossOrigin: true,
backendVerifyAllowedOrigins: ["https://api.example.com"],
});This does not leak your relying-party origin to AgeCheck. The popup still talks only to https://agecheck.me/verify.html. Any backend verification call is initiated by your page to your backend.
Load the UI wrapper:
<meta name="referrer" content="no-referrer" />
<script src="https://cdn.agecheck.me/agegate/v1/easy-agegate.min.js"></script>Render a modal (or mount a button) and run the same hardened flow:
<div id="agecheck-mount"></div>
<script>
AgeCheck.AgeGate.init({
ui: { mount: "#agecheck-mount", buttonText: "Verify age" },
backendVerifyUrl: "/api/agecheck/verify",
onSuccess: (jwt) => console.log("verified", jwt),
onFailure: (err) => console.error(err),
});
</script>You can configure and test Easy AgeGate interactively at:
https://demo.agegate.me/easy-agegate.html
- Add
<meta name="referrer" content="no-referrer">(recommended). This prevents the relying-party URL from being sent to the issuer. backendVerifyUrlis same-origin by default; cross-origin requires explicit opt-in + allowlist.- The popup response is bounded and decoded defensively to avoid memory/CPU amplification.
pnpm install
pnpm run typecheck
pnpm run test
pnpm run buildBuild output is written to v1/ for CDN deployment. Sourcemaps are off by default, and pnpm run build removes any stale v1/*.map files so you do not accidentally publish them.
Apache-2.0. See LICENSE.txt, NOTICE.txt, and THIRD_PARTY_NOTICES.txt.