@onesub/server 0.25.0 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/__tests__/google-webhook-open-warning.test.d.ts +15 -7
- package/dist/__tests__/google-webhook-open-warning.test.d.ts.map +1 -1
- package/dist/__tests__/google-webhook-unauthenticated.test.d.ts +30 -0
- package/dist/__tests__/google-webhook-unauthenticated.test.d.ts.map +1 -0
- package/dist/__tests__/route-log-fields.test.d.ts +20 -0
- package/dist/__tests__/route-log-fields.test.d.ts.map +1 -0
- package/dist/apps.d.ts.map +1 -1
- package/dist/index.cjs +155 -78
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +155 -78
- package/dist/index.js.map +1 -1
- package/dist/log-format.d.ts +2 -0
- package/dist/log-format.d.ts.map +1 -1
- package/dist/routes/admin.d.ts.map +1 -1
- package/dist/routes/purchase.d.ts.map +1 -1
- package/dist/routes/validate.d.ts.map +1 -1
- package/dist/routes/webhook-apple.d.ts.map +1 -1
- package/dist/routes/webhook-google.d.ts +0 -17
- package/dist/routes/webhook-google.d.ts.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -323,7 +323,9 @@ Canonical Postgres DDL shipped at [`sql/schema.sql`](./sql/schema.sql). Apply wi
|
|
|
323
323
|
## Security
|
|
324
324
|
|
|
325
325
|
- Apple JWS signature verified end-to-end against **Apple Root CA G3** (as of `0.6.0`)
|
|
326
|
-
- Google RTDN: `Authorization: Bearer` JWT verified against Google JWKS when `pushAudience` is
|
|
326
|
+
- Google RTDN: `Authorization: Bearer` JWT verified against Google JWKS when `pushAudience` is
|
|
327
|
+
configured. Without it the request cannot be attributed to Google and is refused with 401 under
|
|
328
|
+
`NODE_ENV=production`, unless `google.allowUnauthenticatedWebhook` opts in
|
|
327
329
|
- `transactionId` ownership enforced — same receipt can't be reused across users (`0.5.0+`)
|
|
328
330
|
- zod input validation + 50 KB body cap
|
|
329
331
|
- Full write-up: [`docs/SECURITY.md`](../../docs/SECURITY.md)
|
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Startup
|
|
2
|
+
* Startup warnings for the Google RTDN route's two loose conditions.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* The authentication half changed in 0.27.0: without a `pushAudience` the route used
|
|
5
|
+
* to accept the request, and now refuses it with 401 in production unless
|
|
6
|
+
* `google.allowUnauthenticatedWebhook` says otherwise. So there are two warnings to
|
|
7
|
+
* tell apart, and getting them backwards would be worse than having none — an
|
|
8
|
+
* operator reading "insecure" when the endpoint is actually refusing traffic goes
|
|
9
|
+
* hunting for a breach instead of a missing config value.
|
|
10
|
+
*
|
|
11
|
+
* Open mode (no `packageName`) is unchanged: it still warns rather than refusing.
|
|
12
|
+
*
|
|
13
|
+
* The assertions here were rewritten, not adjusted. Three of them were negative
|
|
14
|
+
* matches against the old warning's wording — text this file no longer produces at
|
|
15
|
+
* all, so they would have passed however loudly the new warning fired. A negative
|
|
16
|
+
* assertion against a string that cannot occur is not coverage, so each one now
|
|
17
|
+
* names both current warnings explicitly.
|
|
10
18
|
*/
|
|
11
19
|
export {};
|
|
12
20
|
//# sourceMappingURL=google-webhook-open-warning.test.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"google-webhook-open-warning.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/google-webhook-open-warning.test.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"google-webhook-open-warning.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/google-webhook-open-warning.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `POST /onesub/webhook/google` refuses requests it cannot attribute to Google.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this replaced masking the purchase token
|
|
5
|
+
*
|
|
6
|
+
* The RTDN paths are only dangerous because a caller who knows a `purchaseToken` or
|
|
7
|
+
* an `orderId` could reach them unauthenticated: the voided-purchase branch acts on
|
|
8
|
+
* the payload alone, cancelling a subscription by token or deleting a one-time
|
|
9
|
+
* purchase row by order id.
|
|
10
|
+
*
|
|
11
|
+
* Treating those ids as secrets does not work. For a Google subscription the purchase
|
|
12
|
+
* token **is** the record's `originalTransactionId` (`providers/google.ts` keys on it
|
|
13
|
+
* deliberately — RTDNs and `linkedPurchaseToken` chains carry nothing else), so it
|
|
14
|
+
* lives in the database, in every notification payload, and in the webhook lines
|
|
15
|
+
* where it is the subject of the investigation. Redacting it from logs would have left
|
|
16
|
+
* the capability intact while suggesting otherwise.
|
|
17
|
+
*
|
|
18
|
+
* Refusing unattributable requests removes the capability instead.
|
|
19
|
+
*
|
|
20
|
+
* ## Production-gated, and why that is not a loophole
|
|
21
|
+
*
|
|
22
|
+
* Locally and in CI no real RTDN arrives, and requiring Pub/Sub credentials there
|
|
23
|
+
* would break every webhook test in this repo and the `onesub dev` server for no
|
|
24
|
+
* security gain. This matches how the mockMode hard guard and the sandbox-receipt
|
|
25
|
+
* rejection are already gated. The residual risk — a production deployment that
|
|
26
|
+
* forgets `NODE_ENV=production` — is the same one those two already carry, so this
|
|
27
|
+
* adds no new footgun, and the boot warning fires on the same condition.
|
|
28
|
+
*/
|
|
29
|
+
export {};
|
|
30
|
+
//# sourceMappingURL=google-webhook-unauthenticated.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"google-webhook-unauthenticated.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/google-webhook-unauthenticated.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the routes, stores and webhook handlers put on a log line.
|
|
3
|
+
*
|
|
4
|
+
* This is the half of the migration that delivers the point of it. `userId` does not
|
|
5
|
+
* exist in the provider layer — it arrives in a request body — so "show me every
|
|
6
|
+
* rejection for this user" only became answerable when these call sites moved.
|
|
7
|
+
* `provider-log-fields.test.ts` covers Apple and Google; this covers the other 54.
|
|
8
|
+
*
|
|
9
|
+
* Driven through HTTP with supertest rather than by calling handlers, because two of
|
|
10
|
+
* the values under test (`userId`, `bundleId`) are attacker-supplied through the
|
|
11
|
+
* request and the escaping only matters on that path. `bundleId` in particular is
|
|
12
|
+
* read out of an **unverified** JWS by `peekAppleBundleId`, which makes
|
|
13
|
+
* `apps.ts`'s "No app configured" line the most attacker-reachable log site in the
|
|
14
|
+
* package.
|
|
15
|
+
*
|
|
16
|
+
* Exact-line assertions again, for the reason given in `provider-log-fields.test.ts`:
|
|
17
|
+
* a substring match cannot tell a field from a sentence that mentions the value.
|
|
18
|
+
*/
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=route-log-fields.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-log-fields.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/route-log-fields.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG"}
|
package/dist/apps.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apps.d.ts","sourceRoot":"","sources":["../src/apps.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAG1E;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,eAAe,EAAE,CAAC;IACjC,QAAQ,CAAC,UAAU,EAAE,eAAe,GAAG,SAAS,CAAC;IACjD;;;;OAIG;IACH,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,kBAAkB,CAAC;CAC9C;AAED,MAAM,WAAW,OAAO;IACtB,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAClC,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;CACtC;AAQD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,kBAAkB,GAAG,WAAW,CAMtE;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,kBAAkB,GAAG,WAAW,
|
|
1
|
+
{"version":3,"file":"apps.d.ts","sourceRoot":"","sources":["../src/apps.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAG1E;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,eAAe,EAAE,CAAC;IACjC,QAAQ,CAAC,UAAU,EAAE,eAAe,GAAG,SAAS,CAAC;IACjD;;;;OAIG;IACH,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,kBAAkB,CAAC;CAC9C;AAED,MAAM,WAAW,OAAO;IACtB,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAClC,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;CACtC;AAQD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,kBAAkB,GAAG,WAAW,CAMtE;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,kBAAkB,GAAG,WAAW,CAiExE;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAUjE"}
|