|
| 1 | +# AgeCheck Node SDK (`@agecheck/node`) |
| 2 | + |
| 3 | +[](https://github.com/agecheck/agecheck-node/actions/workflows/ci.yml) |
| 4 | +[](https://github.com/agecheck/agecheck-node/actions/workflows/compatibility.yml) |
| 5 | +[](https://github.com/agecheck/agecheck-node/actions/workflows/publish.yml) |
| 6 | +[](https://www.npmjs.com/package/@agecheck/node) |
| 7 | + |
| 8 | +Production-grade server SDK for AgeCheck age-gate policy, JWT verification, and stateless signed verification cookies. |
| 9 | + |
| 10 | +## What this package is for |
| 11 | + |
| 12 | +- enforce gate policy server-side |
| 13 | +- verify AgeCheck credentials (`did:web:agecheck.me` and optional demo issuer) |
| 14 | +- issue and validate signed HttpOnly verification cookies |
| 15 | +- keep provider integration pluggable behind one assertion boundary |
| 16 | + |
| 17 | +## Install |
| 18 | + |
| 19 | +```bash |
| 20 | +pnpm add @agecheck/node |
| 21 | +``` |
| 22 | + |
| 23 | +## Minimal integration path (existing sites) |
| 24 | + |
| 25 | +This is the shortest production path for hostmasters. |
| 26 | + |
| 27 | +1. Build one SDK instance at server startup. |
| 28 | +2. Protect routes with `requireVerifiedOrRedirect(...)`. |
| 29 | +3. Handle `POST /verify` by verifying JWT + session and setting signed cookie. |
| 30 | +4. Trust cookie validation on every protected request. |
| 31 | + |
| 32 | +```ts |
| 33 | +import { AgeCheckSdk } from "@agecheck/node"; |
| 34 | + |
| 35 | +const sdk = new AgeCheckSdk({ |
| 36 | + deploymentMode: "production", // "demo" for demo deployments |
| 37 | + verify: { |
| 38 | + requiredAge: 18, |
| 39 | + allowCustomIssuer: false, |
| 40 | + }, |
| 41 | + gate: { |
| 42 | + headerName: "X-Age-Gate", |
| 43 | + requiredValue: "true", |
| 44 | + }, |
| 45 | + cookie: { |
| 46 | + secret: process.env.AGECHECK_COOKIE_SECRET!, |
| 47 | + cookieName: "agecheck_verified", |
| 48 | + ttlSeconds: 86400, |
| 49 | + }, |
| 50 | +}); |
| 51 | + |
| 52 | +export async function enforce(request: Request): Promise<Response | null> { |
| 53 | + return sdk.requireVerifiedOrRedirect(request, { gatePath: "/ageverify" }); |
| 54 | +} |
| 55 | + |
| 56 | +export async function verifyEndpoint(request: Request): Promise<Response> { |
| 57 | + const body = (await request.json()) as { |
| 58 | + jwt?: string; |
| 59 | + payload?: { agegateway_session?: string }; |
| 60 | + redirect?: string; |
| 61 | + }; |
| 62 | + |
| 63 | + const result = await sdk.verifyToken(body.jwt ?? "", body.payload?.agegateway_session ?? ""); |
| 64 | + if (!result.ok) { |
| 65 | + return Response.json( |
| 66 | + { verified: false, error: "Age validation failed.", code: result.code }, |
| 67 | + { status: 401 }, |
| 68 | + ); |
| 69 | + } |
| 70 | + |
| 71 | + const setCookie = await sdk.buildSetCookieFromAssertion({ |
| 72 | + provider: "agecheck", |
| 73 | + verified: true, |
| 74 | + level: result.ageTier, |
| 75 | + verifiedAtUnix: Math.floor(Date.now() / 1000), |
| 76 | + assurance: "passkey", |
| 77 | + }); |
| 78 | + |
| 79 | + const headers = new Headers({ "content-type": "application/json" }); |
| 80 | + headers.append("set-cookie", setCookie); |
| 81 | + |
| 82 | + return new Response( |
| 83 | + JSON.stringify({ verified: true, redirect: body.redirect ?? "/" }), |
| 84 | + { status: 200, headers }, |
| 85 | + ); |
| 86 | +} |
| 87 | +``` |
| 88 | + |
| 89 | +## Deployment modes |
| 90 | + |
| 91 | +- `production` |
| 92 | + - accepts production issuer credentials |
| 93 | + - gate is raised only when policy header requires it |
| 94 | +- `demo` |
| 95 | + - accepts demo + production issuer credentials |
| 96 | + - gate is always raised |
| 97 | + |
| 98 | +## Provider boundary |
| 99 | + |
| 100 | +AgeCheck is the default provider, but you can normalize other provider results into the same assertion model and keep one cookie pipeline. |
| 101 | + |
| 102 | +```ts |
| 103 | +const cookie = await sdk.buildSetCookieFromAssertion({ |
| 104 | + provider: "my-provider", |
| 105 | + verified: true, |
| 106 | + level: "21+", |
| 107 | + verifiedAtUnix: Math.floor(Date.now() / 1000), |
| 108 | +}); |
| 109 | +``` |
| 110 | + |
| 111 | +## Framework adapters |
| 112 | + |
| 113 | +`@agecheck/node` includes framework adapter helpers: |
| 114 | + |
| 115 | +- `createExpressGateMiddleware`, `createExpressVerifyHandler` |
| 116 | +- `createFastifyGateHook`, `createFastifyVerifyHandler` |
| 117 | +- `createHonoGateMiddleware`, `createHonoVerifyHandler` |
| 118 | +- `createNuxtGateMiddleware`, `createNuxtVerifyHandler` |
| 119 | + |
| 120 | +See `/docs/ADAPTERS.md` for adapter mapping and behavior notes. |
| 121 | + |
| 122 | +## Worker reference |
| 123 | + |
| 124 | +`worker-demo/` is a reference backend implementation (verify endpoint + gate page + cookie endpoints). It is useful for validation and demos, but production adopters should wire the SDK into their own server routes/middleware. |
| 125 | + |
| 126 | +## Existing sites |
| 127 | + |
| 128 | +See `/docs/EXISTING_SITES.md` for the migration pattern that keeps your existing templates/content and moves enforcement into middleware. |
| 129 | + |
| 130 | +## Full hostmaster guide |
| 131 | + |
| 132 | +See `/docs/HOSTMASTER_E2E.md` for the full request lifecycle and production enforcement model. |
| 133 | + |
| 134 | +## Framework compatibility |
| 135 | + |
| 136 | +See `/docs/COMPATIBILITY.md` for latest-framework compatibility coverage and CI validation scope. |
| 137 | + |
| 138 | +## Versioning and releases |
| 139 | + |
| 140 | +See `/docs/VERSIONING.md`. |
| 141 | + |
| 142 | +## Quality gates |
| 143 | + |
| 144 | +```bash |
| 145 | +pnpm typecheck |
| 146 | +pnpm test |
| 147 | +pnpm build |
| 148 | +``` |
| 149 | + |
| 150 | +## License |
| 151 | + |
| 152 | +Apache-2.0 |
0 commit comments