Hi, I noticed a possible paid-cookie scope issue in the proxy.
The JWT payload in src/jwt.ts only records that a payment happened and when it expires:
6: export interface JWTPayload {
7: paid: boolean; // indicates payment was verified
8: iat: number; // issued at (seconds since epoch)
9: exp: number; // expires at (seconds since epoch)
10: }
When a JWT is generated, it contains paid: true but no route, amount, recipient, resource, network, or payment method scope:
89: // JWT Payload
90: const payload: JWTPayload = {
91: paid: true,
92: iat: now,
93: exp: now + expiresInSeconds,
94: };
115: return `${dataToSign}.${encodedSignature}`;
In src/auth.ts, a valid cookie skips payment middleware entirely:
21: export function requirePaymentOrCookie(paymentMw: MiddlewareHandler) {
23: // Check for valid cookie
24: const token = getCookie(c, "auth_token");
40: const payload = await verifyJWT(token, jwtSecret);
42: // If token is valid, skip payment and go directly to handler
43: if (payload) {
44: c.set("auth", payload);
45: await next(); // Call the handler
46: return;
47: }
50: // No valid cookie - apply payment middleware
51: return await paymentMw(c, next);
Each protected route can have its own amount/description:
81: export function createProtectedRoute(config: ProtectedRouteConfig) {
100: const paymentMw = payment(mppx.charge, {
101: amount: config.amount,
102: description: config.description,
103: });
106: return await requirePaymentOrCookie(paymentMw)(c, next);
In src/index.ts, the token is generated after the protected middleware accepts a new payment and then set as a path-wide cookie:
166: const result = await protectedMiddleware(c, async () => {
168: const hasExistingAuth = c.get("auth");
170: if (!hasExistingAuth) {
171: // This is a new payment - generate JWT cookie
174: jwtToken = await generateJWT(c.env.JWT_SECRET, 3600);
175: }
...
219: if (jwtToken) {
220: setCookie(c, "auth_token", jwtToken, {
224: maxAge: 3600,
225: path: "/",
226: });
The resulting authorization path appears to be:
first paid request -> auth_token(paid=true) -> later protected route -> skip payment middleware
I did not see the cookie scoped to the route pattern, amount, recipient, resource, or network that produced it.
This may matter when a deployment has more than one protected route or price. A cookie minted for one protected path could be accepted for another protected path until expiry.
A safer design would include route/payment context claims in the JWT and compare them against the current protected route before skipping payment middleware. I am reporting this as a potential issue rather than a confirmed exploit, since a single-route deployment with one price would reduce the risk.
Hi, I noticed a possible paid-cookie scope issue in the proxy.
The JWT payload in
src/jwt.tsonly records that a payment happened and when it expires:When a JWT is generated, it contains
paid: truebut no route, amount, recipient, resource, network, or payment method scope:In
src/auth.ts, a valid cookie skips payment middleware entirely:Each protected route can have its own amount/description:
In
src/index.ts, the token is generated after the protected middleware accepts a new payment and then set as a path-wide cookie:The resulting authorization path appears to be:
I did not see the cookie scoped to the route pattern, amount, recipient, resource, or network that produced it.
This may matter when a deployment has more than one protected route or price. A cookie minted for one protected path could be accepted for another protected path until expiry.
A safer design would include route/payment context claims in the JWT and compare them against the current protected route before skipping payment middleware. I am reporting this as a potential issue rather than a confirmed exploit, since a single-route deployment with one price would reduce the risk.