@atproto/bsky 0.0.198 → 0.0.199
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/CHANGELOG.md +14 -0
- package/dist/api/age-assurance/const.d.ts +11 -0
- package/dist/api/age-assurance/const.d.ts.map +1 -0
- package/dist/api/age-assurance/const.js +142 -0
- package/dist/api/age-assurance/const.js.map +1 -0
- package/dist/api/age-assurance/index.d.ts +4 -0
- package/dist/api/age-assurance/index.d.ts.map +1 -0
- package/dist/api/age-assurance/index.js +24 -0
- package/dist/api/age-assurance/index.js.map +1 -0
- package/dist/api/age-assurance/kws/age-verified.d.ts +109 -0
- package/dist/api/age-assurance/kws/age-verified.d.ts.map +1 -0
- package/dist/api/age-assurance/kws/age-verified.js +63 -0
- package/dist/api/age-assurance/kws/age-verified.js.map +1 -0
- package/dist/api/age-assurance/kws/const.d.ts +13 -0
- package/dist/api/age-assurance/kws/const.d.ts.map +1 -0
- package/dist/api/age-assurance/kws/const.js +36 -0
- package/dist/api/age-assurance/kws/const.js.map +1 -0
- package/dist/api/age-assurance/kws/external-payload.d.ts +75 -0
- package/dist/api/age-assurance/kws/external-payload.d.ts.map +1 -0
- package/dist/api/age-assurance/kws/external-payload.js +124 -0
- package/dist/api/age-assurance/kws/external-payload.js.map +1 -0
- package/dist/api/age-assurance/kws/external-payload.test.d.ts +2 -0
- package/dist/api/age-assurance/kws/external-payload.test.d.ts.map +1 -0
- package/dist/api/age-assurance/kws/external-payload.test.js +65 -0
- package/dist/api/age-assurance/kws/external-payload.test.js.map +1 -0
- package/dist/api/age-assurance/redirects/kws-age-verified.d.ts +4 -0
- package/dist/api/age-assurance/redirects/kws-age-verified.d.ts.map +1 -0
- package/dist/api/age-assurance/redirects/kws-age-verified.js +76 -0
- package/dist/api/age-assurance/redirects/kws-age-verified.js.map +1 -0
- package/dist/api/age-assurance/stash.d.ts +4 -0
- package/dist/api/age-assurance/stash.d.ts.map +1 -0
- package/dist/api/age-assurance/stash.js +19 -0
- package/dist/api/age-assurance/stash.js.map +1 -0
- package/dist/api/age-assurance/types.d.ts +10 -0
- package/dist/api/age-assurance/types.d.ts.map +1 -0
- package/dist/api/age-assurance/types.js +3 -0
- package/dist/api/age-assurance/types.js.map +1 -0
- package/dist/api/age-assurance/util.d.ts +15 -0
- package/dist/api/age-assurance/util.d.ts.map +1 -0
- package/dist/api/age-assurance/util.js +54 -0
- package/dist/api/age-assurance/util.js.map +1 -0
- package/dist/api/age-assurance/webhooks/kws-age-verified.d.ts +4 -0
- package/dist/api/age-assurance/webhooks/kws-age-verified.d.ts.map +1 -0
- package/dist/api/age-assurance/webhooks/kws-age-verified.js +63 -0
- package/dist/api/age-assurance/webhooks/kws-age-verified.js.map +1 -0
- package/dist/api/app/bsky/ageassurance/begin.d.ts +4 -0
- package/dist/api/app/bsky/ageassurance/begin.d.ts.map +1 -0
- package/dist/api/app/bsky/ageassurance/begin.js +131 -0
- package/dist/api/app/bsky/ageassurance/begin.js.map +1 -0
- package/dist/api/app/bsky/ageassurance/getConfig.d.ts +4 -0
- package/dist/api/app/bsky/ageassurance/getConfig.d.ts.map +1 -0
- package/dist/api/app/bsky/ageassurance/getConfig.js +16 -0
- package/dist/api/app/bsky/ageassurance/getConfig.js.map +1 -0
- package/dist/api/app/bsky/ageassurance/getState.d.ts +4 -0
- package/dist/api/app/bsky/ageassurance/getState.d.ts.map +1 -0
- package/dist/api/app/bsky/ageassurance/getState.js +42 -0
- package/dist/api/app/bsky/ageassurance/getState.js.map +1 -0
- package/dist/api/external.d.ts.map +1 -1
- package/dist/api/external.js +2 -0
- package/dist/api/external.js.map +1 -1
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +8 -2
- package/dist/api/index.js.map +1 -1
- package/dist/api/kws/api.d.ts.map +1 -1
- package/dist/api/kws/api.js +44 -26
- package/dist/api/kws/api.js.map +1 -1
- package/dist/api/kws/index.d.ts.map +1 -1
- package/dist/api/kws/index.js +3 -1
- package/dist/api/kws/index.js.map +1 -1
- package/dist/api/kws/webhook.d.ts +3 -1
- package/dist/api/kws/webhook.d.ts.map +1 -1
- package/dist/api/kws/webhook.js +48 -20
- package/dist/api/kws/webhook.js.map +1 -1
- package/dist/config.d.ts +14 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +10 -2
- package/dist/config.js.map +1 -1
- package/dist/data-plane/bsync/index.d.ts.map +1 -1
- package/dist/data-plane/bsync/index.js +22 -0
- package/dist/data-plane/bsync/index.js.map +1 -1
- package/dist/data-plane/server/db/migrations/20251120T004738098Z-update-actor-age-assurance-v2.d.ts +4 -0
- package/dist/data-plane/server/db/migrations/20251120T004738098Z-update-actor-age-assurance-v2.d.ts.map +1 -0
- package/dist/data-plane/server/db/migrations/20251120T004738098Z-update-actor-age-assurance-v2.js +30 -0
- package/dist/data-plane/server/db/migrations/20251120T004738098Z-update-actor-age-assurance-v2.js.map +1 -0
- package/dist/data-plane/server/db/migrations/index.d.ts +1 -0
- package/dist/data-plane/server/db/migrations/index.d.ts.map +1 -1
- package/dist/data-plane/server/db/migrations/index.js +2 -1
- package/dist/data-plane/server/db/migrations/index.js.map +1 -1
- package/dist/data-plane/server/db/pagination.d.ts +3 -3
- package/dist/data-plane/server/db/tables/actor.d.ts +3 -0
- package/dist/data-plane/server/db/tables/actor.d.ts.map +1 -1
- package/dist/data-plane/server/db/tables/actor.js.map +1 -1
- package/dist/data-plane/server/routes/profile.d.ts.map +1 -1
- package/dist/data-plane/server/routes/profile.js +13 -1
- package/dist/data-plane/server/routes/profile.js.map +1 -1
- package/dist/hydration/hydrator.js +1 -1
- package/dist/hydration/hydrator.js.map +1 -1
- package/dist/kws.d.ts +35 -0
- package/dist/kws.d.ts.map +1 -1
- package/dist/kws.js +54 -0
- package/dist/kws.js.map +1 -1
- package/dist/logger.d.ts +1 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +2 -1
- package/dist/logger.js.map +1 -1
- package/dist/proto/bsky_pb.d.ts +8 -0
- package/dist/proto/bsky_pb.d.ts.map +1 -1
- package/dist/proto/bsky_pb.js +20 -0
- package/dist/proto/bsky_pb.js.map +1 -1
- package/dist/stash.d.ts +1 -0
- package/dist/stash.d.ts.map +1 -1
- package/dist/stash.js +1 -0
- package/dist/stash.js.map +1 -1
- package/dist/util/uris.d.ts +2 -2
- package/dist/util/uris.d.ts.map +1 -1
- package/package.json +10 -9
- package/proto/bsky.proto +1 -0
- package/src/api/age-assurance/const.ts +142 -0
- package/src/api/age-assurance/index.ts +34 -0
- package/src/api/age-assurance/kws/age-verified.ts +75 -0
- package/src/api/age-assurance/kws/const.ts +33 -0
- package/src/api/age-assurance/kws/external-payload.test.ts +72 -0
- package/src/api/age-assurance/kws/external-payload.ts +149 -0
- package/src/api/age-assurance/redirects/kws-age-verified.ts +107 -0
- package/src/api/age-assurance/stash.ts +22 -0
- package/src/api/age-assurance/types.ts +10 -0
- package/src/api/age-assurance/util.ts +66 -0
- package/src/api/age-assurance/webhooks/kws-age-verified.ts +75 -0
- package/src/api/app/bsky/ageassurance/begin.ts +167 -0
- package/src/api/app/bsky/ageassurance/getConfig.ts +15 -0
- package/src/api/app/bsky/ageassurance/getState.ts +53 -0
- package/src/api/external.ts +2 -0
- package/src/api/index.ts +6 -0
- package/src/api/kws/api.ts +55 -34
- package/src/api/kws/index.ts +7 -1
- package/src/api/kws/webhook.ts +57 -34
- package/src/config.ts +26 -2
- package/src/data-plane/bsync/index.ts +31 -0
- package/src/data-plane/server/db/migrations/20251120T004738098Z-update-actor-age-assurance-v2.ts +28 -0
- package/src/data-plane/server/db/migrations/index.ts +1 -0
- package/src/data-plane/server/db/tables/actor.ts +3 -0
- package/src/data-plane/server/routes/profile.ts +12 -1
- package/src/hydration/hydrator.ts +1 -1
- package/src/kws.ts +81 -0
- package/src/logger.ts +2 -0
- package/src/proto/bsky_pb.ts +12 -0
- package/src/stash.ts +3 -0
- package/tests/views/age-assurance-v2.test.ts +745 -0
- package/tests/views/age-assurance.test.ts +2 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/tsconfig.tests.tsbuildinfo +1 -1
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KWSExternalPayloadV2Schema = exports.KWSExternalPayloadV1Schema = exports.KWSExternalPayloadVersion = exports.KWSExternalPayloadTooLargeError = exports.KWS_EXTERNAL_PAYLOAD_CHAR_LIMIT = void 0;
|
|
4
|
+
exports.parseKWSExternalPayloadVersion = parseKWSExternalPayloadVersion;
|
|
5
|
+
exports.parseKWSExternalPayloadV1 = parseKWSExternalPayloadV1;
|
|
6
|
+
exports.serializeKWSExternalPayloadV1 = serializeKWSExternalPayloadV1;
|
|
7
|
+
exports.parseKWSExternalPayloadV1WithV2Compat = parseKWSExternalPayloadV1WithV2Compat;
|
|
8
|
+
exports.serializeKWSExternalPayloadV2 = serializeKWSExternalPayloadV2;
|
|
9
|
+
exports.parseKWSExternalPayloadV2 = parseKWSExternalPayloadV2;
|
|
10
|
+
const zod_1 = require("zod");
|
|
11
|
+
exports.KWS_EXTERNAL_PAYLOAD_CHAR_LIMIT = 250;
|
|
12
|
+
/**
|
|
13
|
+
* Thrown when the provided external payload exceeds KWS's character limit.
|
|
14
|
+
*
|
|
15
|
+
* This is most commonly caused by DIDs that are too long, such as for
|
|
16
|
+
* `did:web` DIDs. But it's very rare, and the client has special handling for
|
|
17
|
+
* this case.
|
|
18
|
+
*/
|
|
19
|
+
class KWSExternalPayloadTooLargeError extends Error {
|
|
20
|
+
}
|
|
21
|
+
exports.KWSExternalPayloadTooLargeError = KWSExternalPayloadTooLargeError;
|
|
22
|
+
var KWSExternalPayloadVersion;
|
|
23
|
+
(function (KWSExternalPayloadVersion) {
|
|
24
|
+
KWSExternalPayloadVersion["V1"] = "1";
|
|
25
|
+
KWSExternalPayloadVersion["V2"] = "2";
|
|
26
|
+
})(KWSExternalPayloadVersion || (exports.KWSExternalPayloadVersion = KWSExternalPayloadVersion = {}));
|
|
27
|
+
function parseKWSExternalPayloadVersion(raw) {
|
|
28
|
+
switch (raw) {
|
|
29
|
+
case KWSExternalPayloadVersion.V2:
|
|
30
|
+
return KWSExternalPayloadVersion.V2;
|
|
31
|
+
default:
|
|
32
|
+
return KWSExternalPayloadVersion.V1;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
exports.KWSExternalPayloadV1Schema = zod_1.z.object({
|
|
36
|
+
actorDid: zod_1.z.string(),
|
|
37
|
+
attemptId: zod_1.z.string(),
|
|
38
|
+
});
|
|
39
|
+
function parseKWSExternalPayloadV1(raw) {
|
|
40
|
+
try {
|
|
41
|
+
const value = JSON.parse(raw);
|
|
42
|
+
return exports.KWSExternalPayloadV1Schema.parse(value);
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
throw new Error(`Failed to parse KWSExternalPayloadV1`, {
|
|
46
|
+
cause: err,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function serializeKWSExternalPayloadV1(payload) {
|
|
51
|
+
try {
|
|
52
|
+
return JSON.stringify(exports.KWSExternalPayloadV1Schema.parse(payload));
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
throw new Error('Failed to serialize KWSExternalPayloadV1', { cause: err });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* During our migration from v1 to v2 of the KWS external payload, we'll be
|
|
60
|
+
* sending v2 payloads on the v1 flow (the `adult-verified` email flow). We use
|
|
61
|
+
* this utility to parse either v1 or v2 payloads in that flow.
|
|
62
|
+
*
|
|
63
|
+
* Check for the `version` field on the output of this method to discriminate
|
|
64
|
+
* between the two types and handle them differently.
|
|
65
|
+
*/
|
|
66
|
+
function parseKWSExternalPayloadV1WithV2Compat(raw) {
|
|
67
|
+
const deserialized = JSON.parse(raw);
|
|
68
|
+
const v2 = deserialized.v === KWSExternalPayloadVersion.V2;
|
|
69
|
+
if (v2) {
|
|
70
|
+
return parseKWSExternalPayloadV2(raw);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
return {
|
|
74
|
+
...parseKWSExternalPayloadV1(raw),
|
|
75
|
+
version: KWSExternalPayloadVersion.V1,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
exports.KWSExternalPayloadV2Schema = zod_1.z.object({
|
|
80
|
+
v: zod_1.z.string(),
|
|
81
|
+
id: zod_1.z.string(),
|
|
82
|
+
did: zod_1.z.string(),
|
|
83
|
+
gc: zod_1.z.string().length(2),
|
|
84
|
+
gr: zod_1.z.string().optional(),
|
|
85
|
+
});
|
|
86
|
+
function serializeKWSExternalPayloadV2(payload) {
|
|
87
|
+
let compressed;
|
|
88
|
+
try {
|
|
89
|
+
compressed = exports.KWSExternalPayloadV2Schema.parse({
|
|
90
|
+
v: KWSExternalPayloadVersion.V2, // version
|
|
91
|
+
id: payload.attemptId,
|
|
92
|
+
did: payload.actorDid,
|
|
93
|
+
gc: payload.countryCode, // geolocation country
|
|
94
|
+
gr: payload.regionCode, // geolocation region
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
throw new Error('Failed to serialize KWSExternalPayloadV2', { cause: err });
|
|
99
|
+
}
|
|
100
|
+
const serialized = JSON.stringify(compressed);
|
|
101
|
+
if (serialized.length > exports.KWS_EXTERNAL_PAYLOAD_CHAR_LIMIT) {
|
|
102
|
+
throw new KWSExternalPayloadTooLargeError(`Serialized external payload size ${serialized.length} exceeds limit of ${exports.KWS_EXTERNAL_PAYLOAD_CHAR_LIMIT}`);
|
|
103
|
+
}
|
|
104
|
+
return serialized;
|
|
105
|
+
}
|
|
106
|
+
function parseKWSExternalPayloadV2(raw) {
|
|
107
|
+
try {
|
|
108
|
+
const deserialized = JSON.parse(raw);
|
|
109
|
+
const parsed = exports.KWSExternalPayloadV2Schema.parse(deserialized);
|
|
110
|
+
return {
|
|
111
|
+
version: KWSExternalPayloadVersion.V2,
|
|
112
|
+
attemptId: parsed.id,
|
|
113
|
+
actorDid: parsed.did,
|
|
114
|
+
countryCode: parsed.gc,
|
|
115
|
+
regionCode: parsed.gr,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
throw new Error(`Failed to parse KWSExternalPayloadV2`, {
|
|
120
|
+
cause: err,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=external-payload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"external-payload.js","sourceRoot":"","sources":["../../../../src/api/age-assurance/kws/external-payload.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod'\n\nexport const KWS_EXTERNAL_PAYLOAD_CHAR_LIMIT = 250\n\n/**\n * Thrown when the provided external payload exceeds KWS's character limit.\n *\n * This is most commonly caused by DIDs that are too long, such as for\n * `did:web` DIDs. But it's very rare, and the client has special handling for\n * this case.\n */\nexport class KWSExternalPayloadTooLargeError extends Error {}\n\nexport enum KWSExternalPayloadVersion {\n V1 = '1',\n V2 = '2',\n}\n\nexport function parseKWSExternalPayloadVersion(raw: string) {\n switch (raw) {\n case KWSExternalPayloadVersion.V2:\n return KWSExternalPayloadVersion.V2\n default:\n return KWSExternalPayloadVersion.V1\n }\n}\n\nexport type KWSExternalPayloadV1 = {\n actorDid: string\n attemptId: string\n}\n\nexport const KWSExternalPayloadV1Schema = z.object({\n actorDid: z.string(),\n attemptId: z.string(),\n})\n\nexport function parseKWSExternalPayloadV1(raw: string): KWSExternalPayloadV1 {\n try {\n const value: unknown = JSON.parse(raw)\n return KWSExternalPayloadV1Schema.parse(value)\n } catch (err) {\n throw new Error(`Failed to parse KWSExternalPayloadV1`, {\n cause: err,\n })\n }\n}\n\nexport function serializeKWSExternalPayloadV1(\n payload: KWSExternalPayloadV1,\n): string {\n try {\n return JSON.stringify(KWSExternalPayloadV1Schema.parse(payload))\n } catch (err) {\n throw new Error('Failed to serialize KWSExternalPayloadV1', { cause: err })\n }\n}\n\n/**\n * During our migration from v1 to v2 of the KWS external payload, we'll be\n * sending v2 payloads on the v1 flow (the `adult-verified` email flow). We use\n * this utility to parse either v1 or v2 payloads in that flow.\n *\n * Check for the `version` field on the output of this method to discriminate\n * between the two types and handle them differently.\n */\nexport function parseKWSExternalPayloadV1WithV2Compat(\n raw: string,\n):\n | (KWSExternalPayloadV1 & { version: KWSExternalPayloadVersion.V1 })\n | KWSExternalPayloadV2 {\n const deserialized = JSON.parse(raw)\n const v2 = deserialized.v === KWSExternalPayloadVersion.V2\n\n if (v2) {\n return parseKWSExternalPayloadV2(raw)\n } else {\n return {\n ...parseKWSExternalPayloadV1(raw),\n version: KWSExternalPayloadVersion.V1,\n }\n }\n}\n\n/***************************\n * KWS External Payload V2 *\n ***************************/\n\nexport type KWSExternalPayloadV2 = {\n version: KWSExternalPayloadVersion.V2\n attemptId: string\n actorDid: string\n countryCode: string\n regionCode?: string\n}\n\nexport const KWSExternalPayloadV2Schema = z.object({\n v: z.string(),\n id: z.string(),\n did: z.string(),\n gc: z.string().length(2),\n gr: z.string().optional(),\n})\n\nexport function serializeKWSExternalPayloadV2(\n payload: KWSExternalPayloadV2,\n): string {\n let compressed: z.infer<typeof KWSExternalPayloadV2Schema>\n try {\n compressed = KWSExternalPayloadV2Schema.parse({\n v: KWSExternalPayloadVersion.V2, // version\n id: payload.attemptId,\n did: payload.actorDid,\n gc: payload.countryCode, // geolocation country\n gr: payload.regionCode, // geolocation region\n })\n } catch (err) {\n throw new Error('Failed to serialize KWSExternalPayloadV2', { cause: err })\n }\n\n const serialized = JSON.stringify(compressed)\n\n if (serialized.length > KWS_EXTERNAL_PAYLOAD_CHAR_LIMIT) {\n throw new KWSExternalPayloadTooLargeError(\n `Serialized external payload size ${serialized.length} exceeds limit of ${KWS_EXTERNAL_PAYLOAD_CHAR_LIMIT}`,\n )\n }\n\n return serialized\n}\n\nexport function parseKWSExternalPayloadV2(raw: string): KWSExternalPayloadV2 {\n try {\n const deserialized = JSON.parse(raw)\n const parsed = KWSExternalPayloadV2Schema.parse(deserialized)\n\n return {\n version: KWSExternalPayloadVersion.V2,\n attemptId: parsed.id,\n actorDid: parsed.did,\n countryCode: parsed.gc,\n regionCode: parsed.gr,\n }\n } catch (err) {\n throw new Error(`Failed to parse KWSExternalPayloadV2`, {\n cause: err,\n })\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"external-payload.test.d.ts","sourceRoot":"","sources":["../../../../src/api/age-assurance/kws/external-payload.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const globals_1 = require("@jest/globals");
|
|
4
|
+
const external_payload_1 = require("./external-payload");
|
|
5
|
+
(0, globals_1.describe)('parseKWSExternalPayloadVersion', () => {
|
|
6
|
+
(0, globals_1.it)('should return V2 for "2"', () => {
|
|
7
|
+
const result = (0, external_payload_1.parseKWSExternalPayloadVersion)('2');
|
|
8
|
+
(0, globals_1.expect)(result).toBe('2');
|
|
9
|
+
});
|
|
10
|
+
(0, globals_1.it)('should return V1 for unknown versions', () => {
|
|
11
|
+
const result = (0, external_payload_1.parseKWSExternalPayloadVersion)('unknown');
|
|
12
|
+
(0, globals_1.expect)(result).toBe('1');
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
(0, globals_1.describe)('parseKWSExternalPayloadV1WithV2Compat', () => {
|
|
16
|
+
(0, globals_1.it)('should parse V1 payload correctly', () => {
|
|
17
|
+
const payload = {
|
|
18
|
+
attemptId: '123',
|
|
19
|
+
actorDid: 'did:plc:123',
|
|
20
|
+
};
|
|
21
|
+
const serialized = (0, external_payload_1.serializeKWSExternalPayloadV1)(payload);
|
|
22
|
+
const result = (0, external_payload_1.parseKWSExternalPayloadV1WithV2Compat)(serialized);
|
|
23
|
+
(0, globals_1.expect)(result).toEqual({
|
|
24
|
+
version: external_payload_1.KWSExternalPayloadVersion.V1,
|
|
25
|
+
...payload,
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
(0, globals_1.it)('should parse V2 payload correctly', () => {
|
|
29
|
+
const payload = {
|
|
30
|
+
version: external_payload_1.KWSExternalPayloadVersion.V2,
|
|
31
|
+
attemptId: '123',
|
|
32
|
+
actorDid: 'did:plc:123',
|
|
33
|
+
countryCode: 'US',
|
|
34
|
+
};
|
|
35
|
+
const serialized = (0, external_payload_1.serializeKWSExternalPayloadV2)(payload);
|
|
36
|
+
const result = (0, external_payload_1.parseKWSExternalPayloadV1WithV2Compat)(serialized);
|
|
37
|
+
(0, globals_1.expect)(result).toEqual(payload);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
(0, globals_1.describe)('serializeKWSExternalPayloadV2 & parseKWSExternalPayloadV2', () => {
|
|
41
|
+
const payload = {
|
|
42
|
+
version: external_payload_1.KWSExternalPayloadVersion.V2,
|
|
43
|
+
attemptId: '123',
|
|
44
|
+
actorDid: 'did:plc:123',
|
|
45
|
+
countryCode: 'US',
|
|
46
|
+
regionCode: 'CA',
|
|
47
|
+
};
|
|
48
|
+
(0, globals_1.it)('compresses when serializing', () => {
|
|
49
|
+
const serialized = (0, external_payload_1.serializeKWSExternalPayloadV2)(payload);
|
|
50
|
+
const comparison = JSON.stringify({
|
|
51
|
+
v: external_payload_1.KWSExternalPayloadVersion.V2,
|
|
52
|
+
id: payload.attemptId,
|
|
53
|
+
did: payload.actorDid,
|
|
54
|
+
gc: payload.countryCode,
|
|
55
|
+
gr: payload.regionCode,
|
|
56
|
+
});
|
|
57
|
+
(0, globals_1.expect)(serialized).toEqual(comparison);
|
|
58
|
+
});
|
|
59
|
+
(0, globals_1.it)('decompresses when parsing', () => {
|
|
60
|
+
const serialized = (0, external_payload_1.serializeKWSExternalPayloadV2)(payload);
|
|
61
|
+
const deserialized = (0, external_payload_1.parseKWSExternalPayloadV2)(serialized);
|
|
62
|
+
(0, globals_1.expect)(deserialized).toEqual(payload);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
//# sourceMappingURL=external-payload.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"external-payload.test.js","sourceRoot":"","sources":["../../../../src/api/age-assurance/kws/external-payload.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, expect, it } from '@jest/globals'\nimport {\n KWSExternalPayloadVersion,\n parseKWSExternalPayloadV1WithV2Compat,\n parseKWSExternalPayloadV2,\n parseKWSExternalPayloadVersion,\n serializeKWSExternalPayloadV1,\n serializeKWSExternalPayloadV2,\n} from './external-payload'\n\ndescribe('parseKWSExternalPayloadVersion', () => {\n it('should return V2 for \"2\"', () => {\n const result = parseKWSExternalPayloadVersion('2')\n expect(result).toBe('2')\n })\n it('should return V1 for unknown versions', () => {\n const result = parseKWSExternalPayloadVersion('unknown')\n expect(result).toBe('1')\n })\n})\n\ndescribe('parseKWSExternalPayloadV1WithV2Compat', () => {\n it('should parse V1 payload correctly', () => {\n const payload = {\n attemptId: '123',\n actorDid: 'did:plc:123',\n }\n const serialized = serializeKWSExternalPayloadV1(payload)\n const result = parseKWSExternalPayloadV1WithV2Compat(serialized)\n expect(result).toEqual({\n version: KWSExternalPayloadVersion.V1,\n ...payload,\n })\n })\n it('should parse V2 payload correctly', () => {\n const payload = {\n version: KWSExternalPayloadVersion.V2 as const,\n attemptId: '123',\n actorDid: 'did:plc:123',\n countryCode: 'US',\n }\n const serialized = serializeKWSExternalPayloadV2(payload)\n const result = parseKWSExternalPayloadV1WithV2Compat(serialized)\n expect(result).toEqual(payload)\n })\n})\n\ndescribe('serializeKWSExternalPayloadV2 & parseKWSExternalPayloadV2', () => {\n const payload = {\n version: KWSExternalPayloadVersion.V2 as const,\n attemptId: '123',\n actorDid: 'did:plc:123',\n countryCode: 'US',\n regionCode: 'CA',\n }\n it('compresses when serializing', () => {\n const serialized = serializeKWSExternalPayloadV2(payload)\n const comparison = JSON.stringify({\n v: KWSExternalPayloadVersion.V2,\n id: payload.attemptId,\n did: payload.actorDid,\n gc: payload.countryCode,\n gr: payload.regionCode,\n })\n expect(serialized).toEqual(comparison)\n })\n it('decompresses when parsing', () => {\n const serialized = serializeKWSExternalPayloadV2(payload)\n const deserialized = parseKWSExternalPayloadV2(serialized)\n expect(deserialized).toEqual(payload)\n })\n})\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kws-age-verified.d.ts","sourceRoot":"","sources":["../../../../src/api/age-assurance/redirects/kws-age-verified.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAUjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AA8B3C,eAAO,MAAM,OAAO,GACjB,KAAK,gBAAgB,KAAG,cAiExB,CAAA"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handler = void 0;
|
|
4
|
+
const logger_1 = require("../../../logger");
|
|
5
|
+
const util_1 = require("../../kws/util");
|
|
6
|
+
const const_1 = require("../const");
|
|
7
|
+
const age_verified_1 = require("../kws/age-verified");
|
|
8
|
+
const external_payload_1 = require("../kws/external-payload");
|
|
9
|
+
const stash_1 = require("../stash");
|
|
10
|
+
const util_2 = require("../util");
|
|
11
|
+
function parseQueryParams(ctx, req) {
|
|
12
|
+
try {
|
|
13
|
+
const status = String(req.query.status);
|
|
14
|
+
const externalPayload = String(req.query.externalPayload);
|
|
15
|
+
const signature = String(req.query.signature);
|
|
16
|
+
(0, util_1.validateSignature)(ctx.cfg.kws.ageVerifiedRedirectSecret, `${status}:${externalPayload}`, signature);
|
|
17
|
+
return {
|
|
18
|
+
status,
|
|
19
|
+
externalPayload,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
throw new Error('Invalid KWS API request', { cause: err });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const handler = (ctx) => async (req, res) => {
|
|
27
|
+
let externalPayload;
|
|
28
|
+
try {
|
|
29
|
+
const query = parseQueryParams(ctx, req);
|
|
30
|
+
const { verified, verifiedMinimumAge } = (0, age_verified_1.parseKWSAgeVerifiedStatus)(query.status);
|
|
31
|
+
externalPayload = (0, external_payload_1.parseKWSExternalPayloadV2)(query.externalPayload);
|
|
32
|
+
const { actorDid, attemptId, countryCode, regionCode } = externalPayload;
|
|
33
|
+
/*
|
|
34
|
+
* KWS does not send unverified webhooks for age verification, so we
|
|
35
|
+
* expect all webhooks to be verified. This is just a sanity check.
|
|
36
|
+
*/
|
|
37
|
+
if (!verified) {
|
|
38
|
+
const message = 'Expected KWS verification redirect to have verified status';
|
|
39
|
+
logger_1.ageAssuranceLogger.error({}, message);
|
|
40
|
+
throw new Error(message);
|
|
41
|
+
}
|
|
42
|
+
const { access } = (0, util_2.computeAgeAssuranceAccessOrThrow)(const_1.AGE_ASSURANCE_CONFIG, {
|
|
43
|
+
countryCode,
|
|
44
|
+
regionCode,
|
|
45
|
+
verifiedMinimumAge,
|
|
46
|
+
});
|
|
47
|
+
await (0, stash_1.createEvent)(ctx, actorDid, {
|
|
48
|
+
attemptId,
|
|
49
|
+
// Assumes `app.set('trust proxy', ...)` configured with `true` or specific values.
|
|
50
|
+
completeIp: req.ip,
|
|
51
|
+
completeUa: (0, util_1.getClientUa)(req),
|
|
52
|
+
countryCode,
|
|
53
|
+
regionCode,
|
|
54
|
+
status: 'assured',
|
|
55
|
+
access,
|
|
56
|
+
});
|
|
57
|
+
const q = new URLSearchParams({ actorDid, result: 'success' });
|
|
58
|
+
return res
|
|
59
|
+
.status(302)
|
|
60
|
+
.setHeader('Location', `${ctx.cfg.kws.redirectUrl}?${q}`)
|
|
61
|
+
.end();
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
logger_1.ageAssuranceLogger.error({ err, ...externalPayload }, 'Failed to handle KWS verification redirect');
|
|
65
|
+
const q = new URLSearchParams({
|
|
66
|
+
...(externalPayload ? { actorDid: externalPayload.actorDid } : {}),
|
|
67
|
+
result: 'unknown',
|
|
68
|
+
});
|
|
69
|
+
return res
|
|
70
|
+
.status(302)
|
|
71
|
+
.setHeader('Location', `${ctx.cfg.kws.redirectUrl}?${q}`)
|
|
72
|
+
.end();
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
exports.handler = handler;
|
|
76
|
+
//# sourceMappingURL=kws-age-verified.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kws-age-verified.js","sourceRoot":"","sources":["../../../../src/api/age-assurance/redirects/kws-age-verified.ts"],"names":[],"mappings":"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","sourcesContent":["import express, { RequestHandler } from 'express'\nimport { ageAssuranceLogger as logger } from '../../../logger'\nimport { getClientUa, validateSignature } from '../../kws/util'\nimport { AGE_ASSURANCE_CONFIG } from '../const'\nimport { parseKWSAgeVerifiedStatus } from '../kws/age-verified'\nimport {\n type KWSExternalPayloadV2,\n parseKWSExternalPayloadV2,\n} from '../kws/external-payload'\nimport { createEvent } from '../stash'\nimport { AppContextWithAA } from '../types'\nimport { computeAgeAssuranceAccessOrThrow } from '../util'\n\nfunction parseQueryParams(\n ctx: AppContextWithAA,\n req: express.Request,\n): {\n status: string\n externalPayload: string\n} {\n try {\n const status = String(req.query.status)\n const externalPayload = String(req.query.externalPayload)\n const signature = String(req.query.signature)\n\n validateSignature(\n ctx.cfg.kws.ageVerifiedRedirectSecret,\n `${status}:${externalPayload}`,\n signature,\n )\n\n return {\n status,\n externalPayload,\n }\n } catch (err) {\n throw new Error('Invalid KWS API request', { cause: err })\n }\n}\n\nexport const handler =\n (ctx: AppContextWithAA): RequestHandler =>\n async (req: express.Request, res: express.Response) => {\n let externalPayload: KWSExternalPayloadV2 | undefined\n\n try {\n const query = parseQueryParams(ctx, req)\n const { verified, verifiedMinimumAge } = parseKWSAgeVerifiedStatus(\n query.status,\n )\n externalPayload = parseKWSExternalPayloadV2(query.externalPayload)\n const { actorDid, attemptId, countryCode, regionCode } = externalPayload\n\n /*\n * KWS does not send unverified webhooks for age verification, so we\n * expect all webhooks to be verified. This is just a sanity check.\n */\n if (!verified) {\n const message =\n 'Expected KWS verification redirect to have verified status'\n logger.error({}, message)\n throw new Error(message)\n }\n\n const { access } = computeAgeAssuranceAccessOrThrow(\n AGE_ASSURANCE_CONFIG,\n {\n countryCode,\n regionCode,\n verifiedMinimumAge,\n },\n )\n\n await createEvent(ctx, actorDid, {\n attemptId,\n // Assumes `app.set('trust proxy', ...)` configured with `true` or specific values.\n completeIp: req.ip,\n completeUa: getClientUa(req),\n countryCode,\n regionCode,\n status: 'assured',\n access,\n })\n\n const q = new URLSearchParams({ actorDid, result: 'success' })\n\n return res\n .status(302)\n .setHeader('Location', `${ctx.cfg.kws.redirectUrl}?${q}`)\n .end()\n } catch (err) {\n logger.error(\n { err, ...externalPayload },\n 'Failed to handle KWS verification redirect',\n )\n\n const q = new URLSearchParams({\n ...(externalPayload ? { actorDid: externalPayload.actorDid } : {}),\n result: 'unknown',\n })\n\n return res\n .status(302)\n .setHeader('Location', `${ctx.cfg.kws.redirectUrl}?${q}`)\n .end()\n }\n }\n"]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { AppContext } from '../../context';
|
|
2
|
+
import { Event as AgeAssuranceEvent } from '../../lexicon/types/app/bsky/ageassurance/defs';
|
|
3
|
+
export declare function createEvent(ctx: AppContext, actorDid: string, event: Omit<AgeAssuranceEvent, 'createdAt'>): Promise<AgeAssuranceEvent>;
|
|
4
|
+
//# sourceMappingURL=stash.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stash.d.ts","sourceRoot":"","sources":["../../../src/api/age-assurance/stash.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC1C,OAAO,EAAE,KAAK,IAAI,iBAAiB,EAAE,MAAM,gDAAgD,CAAA;AAG3F,wBAAsB,WAAW,CAC/B,GAAG,EAAE,UAAU,EACf,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,IAAI,CAAC,iBAAiB,EAAE,WAAW,CAAC,8BAa5C"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createEvent = createEvent;
|
|
4
|
+
const common_1 = require("@atproto/common");
|
|
5
|
+
const stash_1 = require("../../stash");
|
|
6
|
+
async function createEvent(ctx, actorDid, event) {
|
|
7
|
+
const payload = {
|
|
8
|
+
createdAt: new Date().toISOString(),
|
|
9
|
+
...event,
|
|
10
|
+
};
|
|
11
|
+
await ctx.stashClient.create({
|
|
12
|
+
actorDid: actorDid,
|
|
13
|
+
namespace: stash_1.Namespaces.AppBskyAgeassuranceDefsEvent,
|
|
14
|
+
key: common_1.TID.nextStr(),
|
|
15
|
+
payload,
|
|
16
|
+
});
|
|
17
|
+
return payload;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=stash.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stash.js","sourceRoot":"","sources":["../../../src/api/age-assurance/stash.ts"],"names":[],"mappings":";;AAKA,kCAgBC;AArBD,4CAAqC;AAGrC,uCAAwC;AAEjC,KAAK,UAAU,WAAW,CAC/B,GAAe,EACf,QAAgB,EAChB,KAA2C;IAE3C,MAAM,OAAO,GAAsB;QACjC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,GAAG,KAAK;KACT,CAAA;IACD,MAAM,GAAG,CAAC,WAAW,CAAC,MAAM,CAAC;QAC3B,QAAQ,EAAE,QAAQ;QAClB,SAAS,EAAE,kBAAU,CAAC,4BAA4B;QAClD,GAAG,EAAE,YAAG,CAAC,OAAO,EAAE;QAClB,OAAO;KACR,CAAC,CAAA;IACF,OAAO,OAAO,CAAA;AAChB,CAAC","sourcesContent":["import { TID } from '@atproto/common'\nimport { AppContext } from '../../context'\nimport { Event as AgeAssuranceEvent } from '../../lexicon/types/app/bsky/ageassurance/defs'\nimport { Namespaces } from '../../stash'\n\nexport async function createEvent(\n ctx: AppContext,\n actorDid: string,\n event: Omit<AgeAssuranceEvent, 'createdAt'>,\n) {\n const payload: AgeAssuranceEvent = {\n createdAt: new Date().toISOString(),\n ...event,\n }\n await ctx.stashClient.create({\n actorDid: actorDid,\n namespace: Namespaces.AppBskyAgeassuranceDefsEvent,\n key: TID.nextStr(),\n payload,\n })\n return payload\n}\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { KwsConfig, ServerConfig } from '../../config';
|
|
2
|
+
import { AppContext } from '../../context';
|
|
3
|
+
import { KwsClient } from '../../kws';
|
|
4
|
+
export type AppContextWithAA = AppContext & {
|
|
5
|
+
kwsClient: KwsClient;
|
|
6
|
+
cfg: ServerConfig & {
|
|
7
|
+
kws: KwsConfig;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/api/age-assurance/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAErC,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG;IAC1C,SAAS,EAAE,SAAS,CAAA;IACpB,GAAG,EAAE,YAAY,GAAG;QAClB,GAAG,EAAE,SAAS,CAAA;KACf,CAAA;CACF,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/api/age-assurance/types.ts"],"names":[],"mappings":"","sourcesContent":["import { KwsConfig, ServerConfig } from '../../config'\nimport { AppContext } from '../../context'\nimport { KwsClient } from '../../kws'\n\nexport type AppContextWithAA = AppContext & {\n kwsClient: KwsClient\n cfg: ServerConfig & {\n kws: KwsConfig\n }\n}\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type AppBskyAgeassuranceDefs } from '@atproto/api';
|
|
2
|
+
/**
|
|
3
|
+
* Compute age assurance access based on verified minimum age. Thrown errors
|
|
4
|
+
* are internal errors, so handle them accordingly.
|
|
5
|
+
*/
|
|
6
|
+
export declare function computeAgeAssuranceAccessOrThrow(config: AppBskyAgeassuranceDefs.Config, { countryCode, regionCode, verifiedMinimumAge, }: {
|
|
7
|
+
countryCode: string;
|
|
8
|
+
regionCode?: string;
|
|
9
|
+
verifiedMinimumAge: number;
|
|
10
|
+
}): {
|
|
11
|
+
access: AppBskyAgeassuranceDefs.Access;
|
|
12
|
+
reason: import("@atproto/api").AgeAssuranceRuleID;
|
|
13
|
+
};
|
|
14
|
+
export declare function createLocationString(countryCode: string, regionCode?: string): string;
|
|
15
|
+
//# sourceMappingURL=util.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../../src/api/age-assurance/util.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,uBAAuB,EAG7B,MAAM,cAAc,CAAA;AAErB;;;GAGG;AACH,wBAAgB,gCAAgC,CAC9C,MAAM,EAAE,uBAAuB,CAAC,MAAM,EACtC,EACE,WAAW,EACX,UAAU,EACV,kBAAkB,GACnB,EAAE;IACD,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,kBAAkB,EAAE,MAAM,CAAA;CAC3B;YAgCW,wBAAyB,MAAM;;EAO5C;AAED,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,UAI5E"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.computeAgeAssuranceAccessOrThrow = computeAgeAssuranceAccessOrThrow;
|
|
4
|
+
exports.createLocationString = createLocationString;
|
|
5
|
+
const api_1 = require("@atproto/api");
|
|
6
|
+
/**
|
|
7
|
+
* Compute age assurance access based on verified minimum age. Thrown errors
|
|
8
|
+
* are internal errors, so handle them accordingly.
|
|
9
|
+
*/
|
|
10
|
+
function computeAgeAssuranceAccessOrThrow(config, { countryCode, regionCode, verifiedMinimumAge, }) {
|
|
11
|
+
const region = (0, api_1.getAgeAssuranceRegionConfig)(config, {
|
|
12
|
+
countryCode,
|
|
13
|
+
regionCode,
|
|
14
|
+
});
|
|
15
|
+
if (region) {
|
|
16
|
+
const result = (0, api_1.computeAgeAssuranceRegionAccess)(region, {
|
|
17
|
+
assuredAge: verifiedMinimumAge,
|
|
18
|
+
/*
|
|
19
|
+
* We don't care about this here, this is a client-only rule. If we have
|
|
20
|
+
* verified data, we can use that, and the account creation date is
|
|
21
|
+
* irrelevant.
|
|
22
|
+
*/
|
|
23
|
+
accountCreatedAt: undefined,
|
|
24
|
+
});
|
|
25
|
+
if (result) {
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
/*
|
|
30
|
+
* If we don't get a result, it's because none of the rules matched,
|
|
31
|
+
* which is a configuration error: there should always be a default
|
|
32
|
+
* rule.
|
|
33
|
+
*/
|
|
34
|
+
throw new Error('Cound not compute age assurance region access');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
/**
|
|
39
|
+
* If we had geolocation data, but we don't have a region config for this
|
|
40
|
+
* geolocation, then it means a user outside of our configured regions
|
|
41
|
+
* has completed age verification. In this case, we can't determine their
|
|
42
|
+
* access level, so we throw an error.
|
|
43
|
+
*
|
|
44
|
+
* This case is also guarded in `app.bsky.ageassurance.begin`.
|
|
45
|
+
*/
|
|
46
|
+
throw new Error('Could not get config for region');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function createLocationString(countryCode, regionCode) {
|
|
50
|
+
return regionCode
|
|
51
|
+
? `${countryCode.toUpperCase()}-${regionCode.toUpperCase()}`
|
|
52
|
+
: countryCode.toUpperCase();
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=util.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../../../src/api/age-assurance/util.ts"],"names":[],"mappings":";;AAUA,4EAiDC;AAED,oDAIC;AAjED,sCAIqB;AAErB;;;GAGG;AACH,SAAgB,gCAAgC,CAC9C,MAAsC,EACtC,EACE,WAAW,EACX,UAAU,EACV,kBAAkB,GAKnB;IAED,MAAM,MAAM,GAAG,IAAA,iCAA2B,EAAC,MAAM,EAAE;QACjD,WAAW;QACX,UAAU;KACX,CAAC,CAAA;IAEF,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,MAAM,GAAG,IAAA,qCAA+B,EAAC,MAAM,EAAE;YACrD,UAAU,EAAE,kBAAkB;YAC9B;;;;eAIG;YACH,gBAAgB,EAAE,SAAS;SAC5B,CAAC,CAAA;QAEF,IAAI,MAAM,EAAE,CAAC;YACX,OAAO,MAAM,CAAA;QACf,CAAC;aAAM,CAAC;YACN;;;;eAIG;YACH,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAA;QAClE,CAAC;IACH,CAAC;SAAM,CAAC;QACN;;;;;;;WAOG;QACH,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAA;IACpD,CAAC;AACH,CAAC;AAED,SAAgB,oBAAoB,CAAC,WAAmB,EAAE,UAAmB;IAC3E,OAAO,UAAU;QACf,CAAC,CAAC,GAAG,WAAW,CAAC,WAAW,EAAE,IAAI,UAAU,CAAC,WAAW,EAAE,EAAE;QAC5D,CAAC,CAAC,WAAW,CAAC,WAAW,EAAE,CAAA;AAC/B,CAAC","sourcesContent":["import {\n type AppBskyAgeassuranceDefs,\n computeAgeAssuranceRegionAccess,\n getAgeAssuranceRegionConfig,\n} from '@atproto/api'\n\n/**\n * Compute age assurance access based on verified minimum age. Thrown errors\n * are internal errors, so handle them accordingly.\n */\nexport function computeAgeAssuranceAccessOrThrow(\n config: AppBskyAgeassuranceDefs.Config,\n {\n countryCode,\n regionCode,\n verifiedMinimumAge,\n }: {\n countryCode: string\n regionCode?: string\n verifiedMinimumAge: number\n },\n) {\n const region = getAgeAssuranceRegionConfig(config, {\n countryCode,\n regionCode,\n })\n\n if (region) {\n const result = computeAgeAssuranceRegionAccess(region, {\n assuredAge: verifiedMinimumAge,\n /*\n * We don't care about this here, this is a client-only rule. If we have\n * verified data, we can use that, and the account creation date is\n * irrelevant.\n */\n accountCreatedAt: undefined,\n })\n\n if (result) {\n return result\n } else {\n /*\n * If we don't get a result, it's because none of the rules matched,\n * which is a configuration error: there should always be a default\n * rule.\n */\n throw new Error('Cound not compute age assurance region access')\n }\n } else {\n /**\n * If we had geolocation data, but we don't have a region config for this\n * geolocation, then it means a user outside of our configured regions\n * has completed age verification. In this case, we can't determine their\n * access level, so we throw an error.\n *\n * This case is also guarded in `app.bsky.ageassurance.begin`.\n */\n throw new Error('Could not get config for region')\n }\n}\n\nexport function createLocationString(countryCode: string, regionCode?: string) {\n return regionCode\n ? `${countryCode.toUpperCase()}-${regionCode.toUpperCase()}`\n : countryCode.toUpperCase()\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kws-age-verified.d.ts","sourceRoot":"","sources":["../../../../src/api/age-assurance/webhooks/kws-age-verified.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AASjD,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAGhD,eAAO,MAAM,OAAO,GACjB,KAAK,gBAAgB,KAAG,cA6DxB,CAAA"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handler = void 0;
|
|
4
|
+
const logger_1 = require("../../../logger");
|
|
5
|
+
const const_1 = require("../const");
|
|
6
|
+
const age_verified_1 = require("../kws/age-verified");
|
|
7
|
+
const external_payload_1 = require("../kws/external-payload");
|
|
8
|
+
const stash_1 = require("../stash");
|
|
9
|
+
const util_1 = require("../util");
|
|
10
|
+
const handler = (ctx) => async (req, res) => {
|
|
11
|
+
let body;
|
|
12
|
+
try {
|
|
13
|
+
body = (0, age_verified_1.parseKWSAgeVerifiedWebhook)(req.body);
|
|
14
|
+
}
|
|
15
|
+
catch (err) {
|
|
16
|
+
const message = 'Failed to parse KWS webhook body';
|
|
17
|
+
logger_1.ageAssuranceLogger.error({ err }, message);
|
|
18
|
+
return res.status(400).json({ error: message });
|
|
19
|
+
}
|
|
20
|
+
const { status, externalPayload } = body.payload;
|
|
21
|
+
const { verified, verifiedMinimumAge } = status;
|
|
22
|
+
const { actorDid, countryCode, regionCode, attemptId } = (0, external_payload_1.parseKWSExternalPayloadV2)(externalPayload);
|
|
23
|
+
/*
|
|
24
|
+
* KWS does not send unverified webhooks for age verification, so we
|
|
25
|
+
* expect all webhooks to be verified. This is just a sanity check.
|
|
26
|
+
*/
|
|
27
|
+
if (!verified) {
|
|
28
|
+
const message = 'Expected KWS webhook to have verified status';
|
|
29
|
+
logger_1.ageAssuranceLogger.error({}, message);
|
|
30
|
+
return res.status(400).json({ error: message });
|
|
31
|
+
}
|
|
32
|
+
let result;
|
|
33
|
+
try {
|
|
34
|
+
result = (0, util_1.computeAgeAssuranceAccessOrThrow)(const_1.AGE_ASSURANCE_CONFIG, {
|
|
35
|
+
countryCode,
|
|
36
|
+
regionCode,
|
|
37
|
+
verifiedMinimumAge,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
// internal errors
|
|
42
|
+
logger_1.ageAssuranceLogger.error({ err, attemptId, actorDid, countryCode, regionCode }, 'Failed to compute age assurance access');
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
if (result) {
|
|
46
|
+
await (0, stash_1.createEvent)(ctx, actorDid, {
|
|
47
|
+
attemptId,
|
|
48
|
+
countryCode,
|
|
49
|
+
regionCode,
|
|
50
|
+
status: 'assured',
|
|
51
|
+
access: result.access,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return res.status(200).end();
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
const message = 'Failed to handle KWS webhook';
|
|
58
|
+
logger_1.ageAssuranceLogger.error({ err, attemptId, actorDid, countryCode, regionCode }, message);
|
|
59
|
+
return res.status(500).json({ error: message });
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
exports.handler = handler;
|
|
63
|
+
//# sourceMappingURL=kws-age-verified.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kws-age-verified.js","sourceRoot":"","sources":["../../../../src/api/age-assurance/webhooks/kws-age-verified.ts"],"names":[],"mappings":";;;AACA,4CAA8D;AAC9D,oCAA+C;AAC/C,sDAG4B;AAC5B,8DAAmE;AACnE,oCAAsC;AAEtC,kCAA0D;AAEnD,MAAM,OAAO,GAClB,CAAC,GAAqB,EAAkB,EAAE,CAC1C,KAAK,EAAE,GAAoB,EAAE,GAAqB,EAAE,EAAE;IACpD,IAAI,IAA2B,CAAA;IAC/B,IAAI,CAAC;QACH,IAAI,GAAG,IAAA,yCAA0B,EAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IAC7C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,kCAAkC,CAAA;QAClD,2BAAM,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,OAAO,CAAC,CAAA;QAC9B,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAA;IACjD,CAAC;IAED,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,GAAG,IAAI,CAAC,OAAO,CAAA;IAChD,MAAM,EAAE,QAAQ,EAAE,kBAAkB,EAAE,GAAG,MAAM,CAAA;IAC/C,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,GACpD,IAAA,4CAAyB,EAAC,eAAe,CAAC,CAAA;IAE5C;;;OAGG;IACH,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,OAAO,GAAG,8CAA8C,CAAA;QAC9D,2BAAM,CAAC,KAAK,CAAC,EAAE,EAAE,OAAO,CAAC,CAAA;QACzB,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAA;IACjD,CAAC;IAED,IAAI,MAAuE,CAAA;IAC3E,IAAI,CAAC;QACH,MAAM,GAAG,IAAA,uCAAgC,EAAC,4BAAoB,EAAE;YAC9D,WAAW;YACX,UAAU;YACV,kBAAkB;SACnB,CAAC,CAAA;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,kBAAkB;QAClB,2BAAM,CAAC,KAAK,CACV,EAAE,GAAG,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,EACrD,wCAAwC,CACzC,CAAA;IACH,CAAC;IAED,IAAI,CAAC;QACH,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,IAAA,mBAAW,EAAC,GAAG,EAAE,QAAQ,EAAE;gBAC/B,SAAS;gBACT,WAAW;gBACX,UAAU;gBACV,MAAM,EAAE,SAAS;gBACjB,MAAM,EAAE,MAAM,CAAC,MAAM;aACtB,CAAC,CAAA;QACJ,CAAC;QAED,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAA;IAC9B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,8BAA8B,CAAA;QAC9C,2BAAM,CAAC,KAAK,CACV,EAAE,GAAG,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,EACrD,OAAO,CACR,CAAA;QACD,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAA;IACjD,CAAC;AACH,CAAC,CAAA;AA9DU,QAAA,OAAO,WA8DjB","sourcesContent":["import express, { RequestHandler } from 'express'\nimport { ageAssuranceLogger as logger } from '../../../logger'\nimport { AGE_ASSURANCE_CONFIG } from '../const'\nimport {\n type KWSWebhookAgeVerified,\n parseKWSAgeVerifiedWebhook,\n} from '../kws/age-verified'\nimport { parseKWSExternalPayloadV2 } from '../kws/external-payload'\nimport { createEvent } from '../stash'\nimport { type AppContextWithAA } from '../types'\nimport { computeAgeAssuranceAccessOrThrow } from '../util'\n\nexport const handler =\n (ctx: AppContextWithAA): RequestHandler =>\n async (req: express.Request, res: express.Response) => {\n let body: KWSWebhookAgeVerified\n try {\n body = parseKWSAgeVerifiedWebhook(req.body)\n } catch (err) {\n const message = 'Failed to parse KWS webhook body'\n logger.error({ err }, message)\n return res.status(400).json({ error: message })\n }\n\n const { status, externalPayload } = body.payload\n const { verified, verifiedMinimumAge } = status\n const { actorDid, countryCode, regionCode, attemptId } =\n parseKWSExternalPayloadV2(externalPayload)\n\n /*\n * KWS does not send unverified webhooks for age verification, so we\n * expect all webhooks to be verified. This is just a sanity check.\n */\n if (!verified) {\n const message = 'Expected KWS webhook to have verified status'\n logger.error({}, message)\n return res.status(400).json({ error: message })\n }\n\n let result: ReturnType<typeof computeAgeAssuranceAccessOrThrow> | undefined\n try {\n result = computeAgeAssuranceAccessOrThrow(AGE_ASSURANCE_CONFIG, {\n countryCode,\n regionCode,\n verifiedMinimumAge,\n })\n } catch (err) {\n // internal errors\n logger.error(\n { err, attemptId, actorDid, countryCode, regionCode },\n 'Failed to compute age assurance access',\n )\n }\n\n try {\n if (result) {\n await createEvent(ctx, actorDid, {\n attemptId,\n countryCode,\n regionCode,\n status: 'assured',\n access: result.access,\n })\n }\n\n return res.status(200).end()\n } catch (err) {\n const message = 'Failed to handle KWS webhook'\n logger.error(\n { err, attemptId, actorDid, countryCode, regionCode },\n message,\n )\n return res.status(500).json({ error: message })\n }\n }\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"begin.d.ts","sourceRoot":"","sources":["../../../../../src/api/app/bsky/ageassurance/begin.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAkB5C,MAAM,CAAC,OAAO,WAAW,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,QA2GvD"}
|