Skip to content

Commit c632af8

Browse files
committed
improve integration docs (Express/env/troubleshooting), set Node 20+ expectation, and align @agecheck/core devDependency to ^0.2.0
add 5-minute Express setup + required envs; clarify Node 20+ and cookie-issuance troubleshooting; align core dev dep to ^0.2.0
1 parent 84d87de commit c632af8

4 files changed

Lines changed: 66 additions & 47 deletions

File tree

README.md

Lines changed: 48 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,20 @@ TypeScript server SDK for age verification, designed to help websites implement
1919
pnpm add @agecheck/node
2020
```
2121

22+
## Runtime requirement
23+
24+
- Node.js `>=20`
25+
26+
## Required environment variables
27+
28+
```bash
29+
AGECHECK_COOKIE_SECRET=replace_with_32_plus_bytes_random
30+
AGECHECK_DEPLOYMENT_MODE=production
31+
AGECHECK_REQUIRED_AGE=18
32+
AGECHECK_GATE_HEADER_NAME=X-Age-Gate
33+
AGECHECK_GATE_HEADER_REQUIRED_VALUE=true
34+
```
35+
2236
## Supported integration modes
2337

2438
1. Managed gate mode: use AgeCheck gate route + verify route + signed cookie.
@@ -27,65 +41,39 @@ pnpm add @agecheck/node
2741

2842
All modes converge into one normalized provider assertion and one signed cookie pipeline.
2943

30-
## Minimal managed-gate integration
44+
## 5-minute Express setup (recommended)
3145

3246
```ts
33-
import { AgeCheckSdk } from "@agecheck/node";
47+
import express from "express";
48+
import { AgeCheckSdk, createExpressGateMiddleware, createExpressVerifyHandler } from "@agecheck/node";
49+
50+
const app = express();
51+
app.use(express.json());
3452

3553
const sdk = new AgeCheckSdk({
36-
deploymentMode: "production", // "demo" for demo deployments
54+
deploymentMode: (process.env.AGECHECK_DEPLOYMENT_MODE === "demo" ? "demo" : "production"),
3755
verify: {
38-
requiredAge: 18,
56+
requiredAge: Number(process.env.AGECHECK_REQUIRED_AGE ?? "18"),
3957
allowCustomIssuer: false,
4058
},
4159
gate: {
42-
headerName: "X-Age-Gate",
43-
requiredValue: "true",
60+
headerName: process.env.AGECHECK_GATE_HEADER_NAME ?? "X-Age-Gate",
61+
requiredValue: process.env.AGECHECK_GATE_HEADER_REQUIRED_VALUE ?? "true",
4462
},
4563
cookie: {
4664
secret: process.env.AGECHECK_COOKIE_SECRET!,
4765
cookieName: "agecheck_verified",
48-
ttlSeconds: 86400, // hostmaster-controlled (e.g. 31536000 for 1 year)
66+
ttlSeconds: 86400,
4967
},
5068
});
5169

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-
}
70+
app.post("/verify", createExpressVerifyHandler(sdk));
71+
app.use("/restricted", createExpressGateMiddleware(sdk, { gatePath: "/ageverify" }));
8772
```
8873

74+
This route pair handles token verification and signed verification cookie issuance.
75+
Most hostmasters should not call `buildSetCookieFromAssertion(...)` directly.
76+
8977
## Existing Gate Integration (Provider Mode)
9078

9179
Use these helpers when a hostmaster already has a gate flow and wants to add AgeCheck as a provider:
@@ -185,6 +173,24 @@ This keeps provider internals isolated while preserving one site-level cookie an
185173

186174
See `/docs/ADAPTERS.md` for adapter mapping and behavior notes.
187175

176+
## Troubleshooting
177+
178+
If you see:
179+
180+
```json
181+
{
182+
"verified": false,
183+
"code": "verify_failed",
184+
"error": "Failed to issue verification cookie"
185+
}
186+
```
187+
188+
check:
189+
- Node runtime is `>=20`
190+
- `AGECHECK_COOKIE_SECRET` is present in the running process and at least 32 bytes
191+
- `payload.agegateway_session` is a UUID
192+
- your verify route is using adapter helpers (`createExpressVerifyHandler` or equivalent)
193+
188194
## Worker reference
189195

190196
`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.

docs/frameworks/express.md

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,12 @@ const app = express();
1212
app.use(express.json());
1313

1414
const sdk = new AgeCheckSdk({
15-
deploymentMode: "production",
16-
verify: { requiredAge: 18 },
17-
gate: { headerName: "X-Age-Gate", requiredValue: "true" },
15+
deploymentMode: process.env.AGECHECK_DEPLOYMENT_MODE === "demo" ? "demo" : "production",
16+
verify: { requiredAge: Number(process.env.AGECHECK_REQUIRED_AGE ?? "18") },
17+
gate: {
18+
headerName: process.env.AGECHECK_GATE_HEADER_NAME ?? "X-Age-Gate",
19+
requiredValue: process.env.AGECHECK_GATE_HEADER_REQUIRED_VALUE ?? "true",
20+
},
1821
cookie: { secret: process.env.AGECHECK_COOKIE_SECRET!, cookieName: "agecheck_verified", ttlSeconds: 86400 },
1922
});
2023

@@ -25,3 +28,13 @@ app.get("/restricted", (_req, res) => {
2528
res.status(200).send("Restricted content");
2629
});
2730
```
31+
32+
Required environment variables:
33+
34+
```bash
35+
AGECHECK_COOKIE_SECRET=replace_with_32_plus_bytes_random
36+
AGECHECK_DEPLOYMENT_MODE=production
37+
AGECHECK_REQUIRED_AGE=18
38+
AGECHECK_GATE_HEADER_NAME=X-Age-Gate
39+
AGECHECK_GATE_HEADER_REQUIRED_VALUE=true
40+
```

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
"@agecheck/core": "^0.2.0"
4040
},
4141
"devDependencies": {
42-
"@agecheck/core": "^0.1.1",
42+
"@agecheck/core": "^0.2.0",
4343
"@types/node": "^24.5.2",
4444
"tsup": "^8.5.0",
4545
"typescript": "^5.9.2",

worker-demo/wrangler.jsonc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"AGECHECK_DEPLOYMENT_MODE": "demo",
77
"AGECHECK_REQUIRED_AGE": "18",
88
"AGECHECK_EASY_AGEGATE": "false",
9-
"AGECHECK_CORS_ALLOWED_ORIGINS": "https://demo.agecheck.me"
9+
"AGECHECK_CORS_ALLOWED_ORIGINS": "https://demo.agecheck.me, https://tools.agecheck.me"
1010
},
1111
"r2_buckets": [
1212
{

0 commit comments

Comments
 (0)