@atproto/bsky 0.0.198 → 0.0.200
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 +26 -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/app/bsky/contact/dismissMatch.d.ts +4 -0
- package/dist/api/app/bsky/contact/dismissMatch.d.ts.map +1 -0
- package/dist/api/app/bsky/contact/dismissMatch.js +23 -0
- package/dist/api/app/bsky/contact/dismissMatch.js.map +1 -0
- package/dist/api/app/bsky/contact/getMatches.d.ts +4 -0
- package/dist/api/app/bsky/contact/getMatches.d.ts.map +1 -0
- package/dist/api/app/bsky/contact/getMatches.js +59 -0
- package/dist/api/app/bsky/contact/getMatches.js.map +1 -0
- package/dist/api/app/bsky/contact/getSyncStatus.d.ts +4 -0
- package/dist/api/app/bsky/contact/getSyncStatus.d.ts.map +1 -0
- package/dist/api/app/bsky/contact/getSyncStatus.js +32 -0
- package/dist/api/app/bsky/contact/getSyncStatus.js.map +1 -0
- package/dist/api/app/bsky/contact/importContacts.d.ts +4 -0
- package/dist/api/app/bsky/contact/importContacts.d.ts.map +1 -0
- package/dist/api/app/bsky/contact/importContacts.js +62 -0
- package/dist/api/app/bsky/contact/importContacts.js.map +1 -0
- package/dist/api/app/bsky/contact/removeData.d.ts +4 -0
- package/dist/api/app/bsky/contact/removeData.d.ts.map +1 -0
- package/dist/api/app/bsky/contact/removeData.js +22 -0
- package/dist/api/app/bsky/contact/removeData.js.map +1 -0
- package/dist/api/app/bsky/contact/startPhoneVerification.d.ts +4 -0
- package/dist/api/app/bsky/contact/startPhoneVerification.d.ts.map +1 -0
- package/dist/api/app/bsky/contact/startPhoneVerification.js +23 -0
- package/dist/api/app/bsky/contact/startPhoneVerification.js.map +1 -0
- package/dist/api/app/bsky/contact/util.d.ts +6 -0
- package/dist/api/app/bsky/contact/util.d.ts.map +1 -0
- package/dist/api/app/bsky/contact/util.js +10 -0
- package/dist/api/app/bsky/contact/util.js.map +1 -0
- package/dist/api/app/bsky/contact/verifyPhone.d.ts +4 -0
- package/dist/api/app/bsky/contact/verifyPhone.d.ts.map +1 -0
- package/dist/api/app/bsky/contact/verifyPhone.js +26 -0
- package/dist/api/app/bsky/contact/verifyPhone.js.map +1 -0
- package/dist/api/app/bsky/graph/getRelationships.d.ts.map +1 -1
- package/dist/api/app/bsky/graph/getRelationships.js +4 -0
- package/dist/api/app/bsky/graph/getRelationships.js.map +1 -1
- 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 +22 -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 +22 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +31 -2
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +3 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +3 -0
- package/dist/context.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/actor.js +1 -1
- package/dist/hydration/actor.js.map +1 -1
- package/dist/hydration/hydrator.js +1 -1
- package/dist/hydration/hydrator.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -0
- package/dist/index.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/lexicon/index.d.ts +19 -0
- package/dist/lexicon/index.d.ts.map +1 -1
- package/dist/lexicon/index.js +48 -1
- package/dist/lexicon/index.js.map +1 -1
- package/dist/lexicon/lexicons.d.ts +664 -0
- package/dist/lexicon/lexicons.d.ts.map +1 -1
- package/dist/lexicon/lexicons.js +354 -0
- package/dist/lexicon/lexicons.js.map +1 -1
- package/dist/lexicon/types/app/bsky/contact/defs.d.ts +24 -0
- package/dist/lexicon/types/app/bsky/contact/defs.d.ts.map +1 -0
- package/dist/lexicon/types/app/bsky/contact/defs.js +25 -0
- package/dist/lexicon/types/app/bsky/contact/defs.js.map +1 -0
- package/dist/lexicon/types/app/bsky/contact/dismissMatch.d.ts +25 -0
- package/dist/lexicon/types/app/bsky/contact/dismissMatch.d.ts.map +1 -0
- package/dist/lexicon/types/app/bsky/contact/dismissMatch.js +7 -0
- package/dist/lexicon/types/app/bsky/contact/dismissMatch.js.map +1 -0
- package/dist/lexicon/types/app/bsky/contact/getMatches.d.ts +25 -0
- package/dist/lexicon/types/app/bsky/contact/getMatches.d.ts.map +1 -0
- package/dist/lexicon/types/app/bsky/contact/getMatches.js +7 -0
- package/dist/lexicon/types/app/bsky/contact/getMatches.js.map +1 -0
- package/dist/lexicon/types/app/bsky/contact/getSyncStatus.d.ts +21 -0
- package/dist/lexicon/types/app/bsky/contact/getSyncStatus.d.ts.map +1 -0
- package/dist/lexicon/types/app/bsky/contact/getSyncStatus.js +7 -0
- package/dist/lexicon/types/app/bsky/contact/getSyncStatus.js.map +1 -0
- package/dist/lexicon/types/app/bsky/contact/importContacts.d.ts +30 -0
- package/dist/lexicon/types/app/bsky/contact/importContacts.d.ts.map +1 -0
- package/dist/lexicon/types/app/bsky/contact/importContacts.js +7 -0
- package/dist/lexicon/types/app/bsky/contact/importContacts.js.map +1 -0
- package/dist/lexicon/types/app/bsky/contact/removeData.d.ts +23 -0
- package/dist/lexicon/types/app/bsky/contact/removeData.d.ts.map +1 -0
- package/dist/lexicon/types/app/bsky/contact/removeData.js +7 -0
- package/dist/lexicon/types/app/bsky/contact/removeData.js.map +1 -0
- package/dist/lexicon/types/app/bsky/contact/startPhoneVerification.d.ts +25 -0
- package/dist/lexicon/types/app/bsky/contact/startPhoneVerification.d.ts.map +1 -0
- package/dist/lexicon/types/app/bsky/contact/startPhoneVerification.js +7 -0
- package/dist/lexicon/types/app/bsky/contact/startPhoneVerification.js.map +1 -0
- package/dist/lexicon/types/app/bsky/contact/verifyPhone.d.ts +29 -0
- package/dist/lexicon/types/app/bsky/contact/verifyPhone.d.ts.map +1 -0
- package/dist/lexicon/types/app/bsky/contact/verifyPhone.js +7 -0
- package/dist/lexicon/types/app/bsky/contact/verifyPhone.js.map +1 -0
- package/dist/lexicon/types/app/bsky/graph/defs.d.ts +8 -0
- package/dist/lexicon/types/app/bsky/graph/defs.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/graph/defs.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 +4 -0
- package/dist/proto/bsky_pb.d.ts.map +1 -1
- package/dist/proto/bsky_pb.js +10 -0
- package/dist/proto/bsky_pb.js.map +1 -1
- package/dist/proto/rolodex_connect.d.ts +83 -0
- package/dist/proto/rolodex_connect.d.ts.map +1 -0
- package/dist/proto/rolodex_connect.js +90 -0
- package/dist/proto/rolodex_connect.js.map +1 -0
- package/dist/proto/rolodex_pb.d.ts +363 -0
- package/dist/proto/rolodex_pb.d.ts.map +1 -0
- package/dist/proto/rolodex_pb.js +1032 -0
- package/dist/proto/rolodex_pb.js.map +1 -0
- package/dist/rolodex.d.ts +9 -0
- package/dist/rolodex.d.ts.map +1 -0
- package/dist/rolodex.js +25 -0
- package/dist/rolodex.js.map +1 -0
- 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 +16 -15
- package/proto/bsky.proto +1 -0
- package/proto/rolodex.proto +116 -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/app/bsky/contact/dismissMatch.ts +24 -0
- package/src/api/app/bsky/contact/getMatches.ts +111 -0
- package/src/api/app/bsky/contact/getSyncStatus.ts +35 -0
- package/src/api/app/bsky/contact/importContacts.ts +118 -0
- package/src/api/app/bsky/contact/removeData.ts +23 -0
- package/src/api/app/bsky/contact/startPhoneVerification.ts +24 -0
- package/src/api/app/bsky/contact/util.ts +13 -0
- package/src/api/app/bsky/contact/verifyPhone.ts +27 -0
- package/src/api/app/bsky/graph/getRelationships.ts +4 -0
- package/src/api/external.ts +2 -0
- package/src/api/index.ts +20 -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 +53 -2
- package/src/context.ts +6 -0
- 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/actor.ts +1 -1
- package/src/hydration/hydrator.ts +1 -1
- package/src/index.ts +13 -0
- package/src/kws.ts +81 -0
- package/src/lexicon/index.ts +101 -0
- package/src/lexicon/lexicons.ts +375 -0
- package/src/lexicon/types/app/bsky/contact/defs.ts +52 -0
- package/src/lexicon/types/app/bsky/contact/dismissMatch.ts +43 -0
- package/src/lexicon/types/app/bsky/contact/getMatches.ts +43 -0
- package/src/lexicon/types/app/bsky/contact/getSyncStatus.ts +39 -0
- package/src/lexicon/types/app/bsky/contact/importContacts.ts +49 -0
- package/src/lexicon/types/app/bsky/contact/removeData.ts +40 -0
- package/src/lexicon/types/app/bsky/contact/startPhoneVerification.ts +43 -0
- package/src/lexicon/types/app/bsky/contact/verifyPhone.ts +48 -0
- package/src/lexicon/types/app/bsky/graph/defs.ts +8 -0
- package/src/logger.ts +2 -0
- package/src/proto/bsky_pb.ts +6 -0
- package/src/proto/rolodex_connect.ts +89 -0
- package/src/proto/rolodex_pb.ts +746 -0
- package/src/rolodex.ts +42 -0
- package/src/stash.ts +3 -0
- package/tests/views/__snapshots__/profile.test.ts.snap +103 -0
- package/tests/views/age-assurance-v2.test.ts +745 -0
- package/tests/views/age-assurance.test.ts +2 -0
- package/tests/views/profile.test.ts +39 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/tsconfig.tests.tsbuildinfo +1 -1
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Schema for KWS the `status` object on `age-verified` payloads.
|
|
5
|
+
*/
|
|
6
|
+
export const KWSAgeVerifiedStatusSchema = z.object({
|
|
7
|
+
verified: z.boolean(),
|
|
8
|
+
verifiedMinimumAge: z.number(),
|
|
9
|
+
transactionId: z.string().optional(),
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The KWS `status` object on `age-verified` payloads.
|
|
14
|
+
*/
|
|
15
|
+
export type KWSAgeVerifiedStatus = z.infer<typeof KWSAgeVerifiedStatusSchema>
|
|
16
|
+
|
|
17
|
+
export function serializeKWSAgeVerifiedStatus(
|
|
18
|
+
status: KWSAgeVerifiedStatus,
|
|
19
|
+
): string {
|
|
20
|
+
return JSON.stringify(KWSAgeVerifiedStatusSchema.parse(status))
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Parse KWS `age-verified` status object.
|
|
25
|
+
*/
|
|
26
|
+
export const parseKWSAgeVerifiedStatus = (
|
|
27
|
+
raw: string,
|
|
28
|
+
): KWSAgeVerifiedStatus => {
|
|
29
|
+
try {
|
|
30
|
+
const value = JSON.parse(raw)
|
|
31
|
+
return KWSAgeVerifiedStatusSchema.parse(value)
|
|
32
|
+
} catch (err) {
|
|
33
|
+
throw new Error(`Invalid KWS age-verified status: ${raw}`, {
|
|
34
|
+
cause: err,
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Schema for KWS `age-verified` webhooks.
|
|
41
|
+
*
|
|
42
|
+
* Note: we don't use `.strict()` here so that we avoid breaking if KWS adds
|
|
43
|
+
* fields, and some fields below are not strictly typed since we're not using
|
|
44
|
+
* them.
|
|
45
|
+
*/
|
|
46
|
+
export const KWSAgeVerifiedWebhookSchema = z.object({
|
|
47
|
+
name: z.string(),
|
|
48
|
+
time: z.string(), // ISO8601 timestamp, but don't validate here
|
|
49
|
+
orgId: z.string().uuid().optional(),
|
|
50
|
+
productId: z.string().uuid().optional(),
|
|
51
|
+
payload: z.object({
|
|
52
|
+
email: z.string(), // no need to validate here
|
|
53
|
+
externalPayload: z.string(),
|
|
54
|
+
status: KWSAgeVerifiedStatusSchema,
|
|
55
|
+
}),
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The raw KWS `age-verified` webhook body
|
|
60
|
+
*/
|
|
61
|
+
export type KWSWebhookAgeVerified = z.infer<typeof KWSAgeVerifiedWebhookSchema>
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Parse KWS `age-verified` webhook body and its external payload.
|
|
65
|
+
*/
|
|
66
|
+
export const parseKWSAgeVerifiedWebhook = (
|
|
67
|
+
raw: string,
|
|
68
|
+
): KWSWebhookAgeVerified => {
|
|
69
|
+
try {
|
|
70
|
+
const value: unknown = JSON.parse(raw)
|
|
71
|
+
return KWSAgeVerifiedWebhookSchema.parse(value)
|
|
72
|
+
} catch (err) {
|
|
73
|
+
throw new Error(`Invalid webhook body: ${raw}`, { cause: err })
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supported languages for KWS Adult Verification. This list comes from KWS's
|
|
3
|
+
* Age Verification Developer Guide PDF doc.
|
|
4
|
+
*/
|
|
5
|
+
export const KWS_SUPPORTED_LANGUAGES = new Set([
|
|
6
|
+
'en',
|
|
7
|
+
'ar',
|
|
8
|
+
'zh-Hans',
|
|
9
|
+
'nl',
|
|
10
|
+
'tl',
|
|
11
|
+
'fr',
|
|
12
|
+
'de',
|
|
13
|
+
'id',
|
|
14
|
+
'it',
|
|
15
|
+
'ja',
|
|
16
|
+
'ko',
|
|
17
|
+
'pl',
|
|
18
|
+
'pt-BR',
|
|
19
|
+
'pt',
|
|
20
|
+
'ru',
|
|
21
|
+
'es',
|
|
22
|
+
'th',
|
|
23
|
+
'tr',
|
|
24
|
+
'vi',
|
|
25
|
+
])
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Regions where our "version 2" using the `age-verified` KWS flow is
|
|
29
|
+
* available. In these regions, we'll use a different KWS flow from the
|
|
30
|
+
* existing `adult-verified` flow, pass along a different external payload, and
|
|
31
|
+
* handle webhooks/redirects differently in the appview.
|
|
32
|
+
*/
|
|
33
|
+
export const KWS_V2_COUNTRIES = new Set(['AU'])
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { describe, expect, it } from '@jest/globals'
|
|
2
|
+
import {
|
|
3
|
+
KWSExternalPayloadVersion,
|
|
4
|
+
parseKWSExternalPayloadV1WithV2Compat,
|
|
5
|
+
parseKWSExternalPayloadV2,
|
|
6
|
+
parseKWSExternalPayloadVersion,
|
|
7
|
+
serializeKWSExternalPayloadV1,
|
|
8
|
+
serializeKWSExternalPayloadV2,
|
|
9
|
+
} from './external-payload'
|
|
10
|
+
|
|
11
|
+
describe('parseKWSExternalPayloadVersion', () => {
|
|
12
|
+
it('should return V2 for "2"', () => {
|
|
13
|
+
const result = parseKWSExternalPayloadVersion('2')
|
|
14
|
+
expect(result).toBe('2')
|
|
15
|
+
})
|
|
16
|
+
it('should return V1 for unknown versions', () => {
|
|
17
|
+
const result = parseKWSExternalPayloadVersion('unknown')
|
|
18
|
+
expect(result).toBe('1')
|
|
19
|
+
})
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
describe('parseKWSExternalPayloadV1WithV2Compat', () => {
|
|
23
|
+
it('should parse V1 payload correctly', () => {
|
|
24
|
+
const payload = {
|
|
25
|
+
attemptId: '123',
|
|
26
|
+
actorDid: 'did:plc:123',
|
|
27
|
+
}
|
|
28
|
+
const serialized = serializeKWSExternalPayloadV1(payload)
|
|
29
|
+
const result = parseKWSExternalPayloadV1WithV2Compat(serialized)
|
|
30
|
+
expect(result).toEqual({
|
|
31
|
+
version: KWSExternalPayloadVersion.V1,
|
|
32
|
+
...payload,
|
|
33
|
+
})
|
|
34
|
+
})
|
|
35
|
+
it('should parse V2 payload correctly', () => {
|
|
36
|
+
const payload = {
|
|
37
|
+
version: KWSExternalPayloadVersion.V2 as const,
|
|
38
|
+
attemptId: '123',
|
|
39
|
+
actorDid: 'did:plc:123',
|
|
40
|
+
countryCode: 'US',
|
|
41
|
+
}
|
|
42
|
+
const serialized = serializeKWSExternalPayloadV2(payload)
|
|
43
|
+
const result = parseKWSExternalPayloadV1WithV2Compat(serialized)
|
|
44
|
+
expect(result).toEqual(payload)
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
describe('serializeKWSExternalPayloadV2 & parseKWSExternalPayloadV2', () => {
|
|
49
|
+
const payload = {
|
|
50
|
+
version: KWSExternalPayloadVersion.V2 as const,
|
|
51
|
+
attemptId: '123',
|
|
52
|
+
actorDid: 'did:plc:123',
|
|
53
|
+
countryCode: 'US',
|
|
54
|
+
regionCode: 'CA',
|
|
55
|
+
}
|
|
56
|
+
it('compresses when serializing', () => {
|
|
57
|
+
const serialized = serializeKWSExternalPayloadV2(payload)
|
|
58
|
+
const comparison = JSON.stringify({
|
|
59
|
+
v: KWSExternalPayloadVersion.V2,
|
|
60
|
+
id: payload.attemptId,
|
|
61
|
+
did: payload.actorDid,
|
|
62
|
+
gc: payload.countryCode,
|
|
63
|
+
gr: payload.regionCode,
|
|
64
|
+
})
|
|
65
|
+
expect(serialized).toEqual(comparison)
|
|
66
|
+
})
|
|
67
|
+
it('decompresses when parsing', () => {
|
|
68
|
+
const serialized = serializeKWSExternalPayloadV2(payload)
|
|
69
|
+
const deserialized = parseKWSExternalPayloadV2(serialized)
|
|
70
|
+
expect(deserialized).toEqual(payload)
|
|
71
|
+
})
|
|
72
|
+
})
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
export const KWS_EXTERNAL_PAYLOAD_CHAR_LIMIT = 250
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Thrown when the provided external payload exceeds KWS's character limit.
|
|
7
|
+
*
|
|
8
|
+
* This is most commonly caused by DIDs that are too long, such as for
|
|
9
|
+
* `did:web` DIDs. But it's very rare, and the client has special handling for
|
|
10
|
+
* this case.
|
|
11
|
+
*/
|
|
12
|
+
export class KWSExternalPayloadTooLargeError extends Error {}
|
|
13
|
+
|
|
14
|
+
export enum KWSExternalPayloadVersion {
|
|
15
|
+
V1 = '1',
|
|
16
|
+
V2 = '2',
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function parseKWSExternalPayloadVersion(raw: string) {
|
|
20
|
+
switch (raw) {
|
|
21
|
+
case KWSExternalPayloadVersion.V2:
|
|
22
|
+
return KWSExternalPayloadVersion.V2
|
|
23
|
+
default:
|
|
24
|
+
return KWSExternalPayloadVersion.V1
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type KWSExternalPayloadV1 = {
|
|
29
|
+
actorDid: string
|
|
30
|
+
attemptId: string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const KWSExternalPayloadV1Schema = z.object({
|
|
34
|
+
actorDid: z.string(),
|
|
35
|
+
attemptId: z.string(),
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
export function parseKWSExternalPayloadV1(raw: string): KWSExternalPayloadV1 {
|
|
39
|
+
try {
|
|
40
|
+
const value: unknown = JSON.parse(raw)
|
|
41
|
+
return KWSExternalPayloadV1Schema.parse(value)
|
|
42
|
+
} catch (err) {
|
|
43
|
+
throw new Error(`Failed to parse KWSExternalPayloadV1`, {
|
|
44
|
+
cause: err,
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function serializeKWSExternalPayloadV1(
|
|
50
|
+
payload: KWSExternalPayloadV1,
|
|
51
|
+
): string {
|
|
52
|
+
try {
|
|
53
|
+
return JSON.stringify(KWSExternalPayloadV1Schema.parse(payload))
|
|
54
|
+
} catch (err) {
|
|
55
|
+
throw new Error('Failed to serialize KWSExternalPayloadV1', { cause: err })
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* During our migration from v1 to v2 of the KWS external payload, we'll be
|
|
61
|
+
* sending v2 payloads on the v1 flow (the `adult-verified` email flow). We use
|
|
62
|
+
* this utility to parse either v1 or v2 payloads in that flow.
|
|
63
|
+
*
|
|
64
|
+
* Check for the `version` field on the output of this method to discriminate
|
|
65
|
+
* between the two types and handle them differently.
|
|
66
|
+
*/
|
|
67
|
+
export function parseKWSExternalPayloadV1WithV2Compat(
|
|
68
|
+
raw: string,
|
|
69
|
+
):
|
|
70
|
+
| (KWSExternalPayloadV1 & { version: KWSExternalPayloadVersion.V1 })
|
|
71
|
+
| KWSExternalPayloadV2 {
|
|
72
|
+
const deserialized = JSON.parse(raw)
|
|
73
|
+
const v2 = deserialized.v === KWSExternalPayloadVersion.V2
|
|
74
|
+
|
|
75
|
+
if (v2) {
|
|
76
|
+
return parseKWSExternalPayloadV2(raw)
|
|
77
|
+
} else {
|
|
78
|
+
return {
|
|
79
|
+
...parseKWSExternalPayloadV1(raw),
|
|
80
|
+
version: KWSExternalPayloadVersion.V1,
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/***************************
|
|
86
|
+
* KWS External Payload V2 *
|
|
87
|
+
***************************/
|
|
88
|
+
|
|
89
|
+
export type KWSExternalPayloadV2 = {
|
|
90
|
+
version: KWSExternalPayloadVersion.V2
|
|
91
|
+
attemptId: string
|
|
92
|
+
actorDid: string
|
|
93
|
+
countryCode: string
|
|
94
|
+
regionCode?: string
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export const KWSExternalPayloadV2Schema = z.object({
|
|
98
|
+
v: z.string(),
|
|
99
|
+
id: z.string(),
|
|
100
|
+
did: z.string(),
|
|
101
|
+
gc: z.string().length(2),
|
|
102
|
+
gr: z.string().optional(),
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
export function serializeKWSExternalPayloadV2(
|
|
106
|
+
payload: KWSExternalPayloadV2,
|
|
107
|
+
): string {
|
|
108
|
+
let compressed: z.infer<typeof KWSExternalPayloadV2Schema>
|
|
109
|
+
try {
|
|
110
|
+
compressed = KWSExternalPayloadV2Schema.parse({
|
|
111
|
+
v: KWSExternalPayloadVersion.V2, // version
|
|
112
|
+
id: payload.attemptId,
|
|
113
|
+
did: payload.actorDid,
|
|
114
|
+
gc: payload.countryCode, // geolocation country
|
|
115
|
+
gr: payload.regionCode, // geolocation region
|
|
116
|
+
})
|
|
117
|
+
} catch (err) {
|
|
118
|
+
throw new Error('Failed to serialize KWSExternalPayloadV2', { cause: err })
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const serialized = JSON.stringify(compressed)
|
|
122
|
+
|
|
123
|
+
if (serialized.length > KWS_EXTERNAL_PAYLOAD_CHAR_LIMIT) {
|
|
124
|
+
throw new KWSExternalPayloadTooLargeError(
|
|
125
|
+
`Serialized external payload size ${serialized.length} exceeds limit of ${KWS_EXTERNAL_PAYLOAD_CHAR_LIMIT}`,
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return serialized
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function parseKWSExternalPayloadV2(raw: string): KWSExternalPayloadV2 {
|
|
133
|
+
try {
|
|
134
|
+
const deserialized = JSON.parse(raw)
|
|
135
|
+
const parsed = KWSExternalPayloadV2Schema.parse(deserialized)
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
version: KWSExternalPayloadVersion.V2,
|
|
139
|
+
attemptId: parsed.id,
|
|
140
|
+
actorDid: parsed.did,
|
|
141
|
+
countryCode: parsed.gc,
|
|
142
|
+
regionCode: parsed.gr,
|
|
143
|
+
}
|
|
144
|
+
} catch (err) {
|
|
145
|
+
throw new Error(`Failed to parse KWSExternalPayloadV2`, {
|
|
146
|
+
cause: err,
|
|
147
|
+
})
|
|
148
|
+
}
|
|
149
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import express, { RequestHandler } from 'express'
|
|
2
|
+
import { ageAssuranceLogger as logger } from '../../../logger'
|
|
3
|
+
import { getClientUa, validateSignature } from '../../kws/util'
|
|
4
|
+
import { AGE_ASSURANCE_CONFIG } from '../const'
|
|
5
|
+
import { parseKWSAgeVerifiedStatus } from '../kws/age-verified'
|
|
6
|
+
import {
|
|
7
|
+
type KWSExternalPayloadV2,
|
|
8
|
+
parseKWSExternalPayloadV2,
|
|
9
|
+
} from '../kws/external-payload'
|
|
10
|
+
import { createEvent } from '../stash'
|
|
11
|
+
import { AppContextWithAA } from '../types'
|
|
12
|
+
import { computeAgeAssuranceAccessOrThrow } from '../util'
|
|
13
|
+
|
|
14
|
+
function parseQueryParams(
|
|
15
|
+
ctx: AppContextWithAA,
|
|
16
|
+
req: express.Request,
|
|
17
|
+
): {
|
|
18
|
+
status: string
|
|
19
|
+
externalPayload: string
|
|
20
|
+
} {
|
|
21
|
+
try {
|
|
22
|
+
const status = String(req.query.status)
|
|
23
|
+
const externalPayload = String(req.query.externalPayload)
|
|
24
|
+
const signature = String(req.query.signature)
|
|
25
|
+
|
|
26
|
+
validateSignature(
|
|
27
|
+
ctx.cfg.kws.ageVerifiedRedirectSecret,
|
|
28
|
+
`${status}:${externalPayload}`,
|
|
29
|
+
signature,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
status,
|
|
34
|
+
externalPayload,
|
|
35
|
+
}
|
|
36
|
+
} catch (err) {
|
|
37
|
+
throw new Error('Invalid KWS API request', { cause: err })
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const handler =
|
|
42
|
+
(ctx: AppContextWithAA): RequestHandler =>
|
|
43
|
+
async (req: express.Request, res: express.Response) => {
|
|
44
|
+
let externalPayload: KWSExternalPayloadV2 | undefined
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
const query = parseQueryParams(ctx, req)
|
|
48
|
+
const { verified, verifiedMinimumAge } = parseKWSAgeVerifiedStatus(
|
|
49
|
+
query.status,
|
|
50
|
+
)
|
|
51
|
+
externalPayload = parseKWSExternalPayloadV2(query.externalPayload)
|
|
52
|
+
const { actorDid, attemptId, countryCode, regionCode } = externalPayload
|
|
53
|
+
|
|
54
|
+
/*
|
|
55
|
+
* KWS does not send unverified webhooks for age verification, so we
|
|
56
|
+
* expect all webhooks to be verified. This is just a sanity check.
|
|
57
|
+
*/
|
|
58
|
+
if (!verified) {
|
|
59
|
+
const message =
|
|
60
|
+
'Expected KWS verification redirect to have verified status'
|
|
61
|
+
logger.error({}, message)
|
|
62
|
+
throw new Error(message)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const { access } = computeAgeAssuranceAccessOrThrow(
|
|
66
|
+
AGE_ASSURANCE_CONFIG,
|
|
67
|
+
{
|
|
68
|
+
countryCode,
|
|
69
|
+
regionCode,
|
|
70
|
+
verifiedMinimumAge,
|
|
71
|
+
},
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
await createEvent(ctx, actorDid, {
|
|
75
|
+
attemptId,
|
|
76
|
+
// Assumes `app.set('trust proxy', ...)` configured with `true` or specific values.
|
|
77
|
+
completeIp: req.ip,
|
|
78
|
+
completeUa: getClientUa(req),
|
|
79
|
+
countryCode,
|
|
80
|
+
regionCode,
|
|
81
|
+
status: 'assured',
|
|
82
|
+
access,
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
const q = new URLSearchParams({ actorDid, result: 'success' })
|
|
86
|
+
|
|
87
|
+
return res
|
|
88
|
+
.status(302)
|
|
89
|
+
.setHeader('Location', `${ctx.cfg.kws.redirectUrl}?${q}`)
|
|
90
|
+
.end()
|
|
91
|
+
} catch (err) {
|
|
92
|
+
logger.error(
|
|
93
|
+
{ err, ...externalPayload },
|
|
94
|
+
'Failed to handle KWS verification redirect',
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
const q = new URLSearchParams({
|
|
98
|
+
...(externalPayload ? { actorDid: externalPayload.actorDid } : {}),
|
|
99
|
+
result: 'unknown',
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
return res
|
|
103
|
+
.status(302)
|
|
104
|
+
.setHeader('Location', `${ctx.cfg.kws.redirectUrl}?${q}`)
|
|
105
|
+
.end()
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { TID } from '@atproto/common'
|
|
2
|
+
import { AppContext } from '../../context'
|
|
3
|
+
import { Event as AgeAssuranceEvent } from '../../lexicon/types/app/bsky/ageassurance/defs'
|
|
4
|
+
import { Namespaces } from '../../stash'
|
|
5
|
+
|
|
6
|
+
export async function createEvent(
|
|
7
|
+
ctx: AppContext,
|
|
8
|
+
actorDid: string,
|
|
9
|
+
event: Omit<AgeAssuranceEvent, 'createdAt'>,
|
|
10
|
+
) {
|
|
11
|
+
const payload: AgeAssuranceEvent = {
|
|
12
|
+
createdAt: new Date().toISOString(),
|
|
13
|
+
...event,
|
|
14
|
+
}
|
|
15
|
+
await ctx.stashClient.create({
|
|
16
|
+
actorDid: actorDid,
|
|
17
|
+
namespace: Namespaces.AppBskyAgeassuranceDefsEvent,
|
|
18
|
+
key: TID.nextStr(),
|
|
19
|
+
payload,
|
|
20
|
+
})
|
|
21
|
+
return payload
|
|
22
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { KwsConfig, ServerConfig } from '../../config'
|
|
2
|
+
import { AppContext } from '../../context'
|
|
3
|
+
import { KwsClient } from '../../kws'
|
|
4
|
+
|
|
5
|
+
export type AppContextWithAA = AppContext & {
|
|
6
|
+
kwsClient: KwsClient
|
|
7
|
+
cfg: ServerConfig & {
|
|
8
|
+
kws: KwsConfig
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type AppBskyAgeassuranceDefs,
|
|
3
|
+
computeAgeAssuranceRegionAccess,
|
|
4
|
+
getAgeAssuranceRegionConfig,
|
|
5
|
+
} from '@atproto/api'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Compute age assurance access based on verified minimum age. Thrown errors
|
|
9
|
+
* are internal errors, so handle them accordingly.
|
|
10
|
+
*/
|
|
11
|
+
export function computeAgeAssuranceAccessOrThrow(
|
|
12
|
+
config: AppBskyAgeassuranceDefs.Config,
|
|
13
|
+
{
|
|
14
|
+
countryCode,
|
|
15
|
+
regionCode,
|
|
16
|
+
verifiedMinimumAge,
|
|
17
|
+
}: {
|
|
18
|
+
countryCode: string
|
|
19
|
+
regionCode?: string
|
|
20
|
+
verifiedMinimumAge: number
|
|
21
|
+
},
|
|
22
|
+
) {
|
|
23
|
+
const region = getAgeAssuranceRegionConfig(config, {
|
|
24
|
+
countryCode,
|
|
25
|
+
regionCode,
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
if (region) {
|
|
29
|
+
const result = computeAgeAssuranceRegionAccess(region, {
|
|
30
|
+
assuredAge: verifiedMinimumAge,
|
|
31
|
+
/*
|
|
32
|
+
* We don't care about this here, this is a client-only rule. If we have
|
|
33
|
+
* verified data, we can use that, and the account creation date is
|
|
34
|
+
* irrelevant.
|
|
35
|
+
*/
|
|
36
|
+
accountCreatedAt: undefined,
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
if (result) {
|
|
40
|
+
return result
|
|
41
|
+
} else {
|
|
42
|
+
/*
|
|
43
|
+
* If we don't get a result, it's because none of the rules matched,
|
|
44
|
+
* which is a configuration error: there should always be a default
|
|
45
|
+
* rule.
|
|
46
|
+
*/
|
|
47
|
+
throw new Error('Cound not compute age assurance region access')
|
|
48
|
+
}
|
|
49
|
+
} else {
|
|
50
|
+
/**
|
|
51
|
+
* If we had geolocation data, but we don't have a region config for this
|
|
52
|
+
* geolocation, then it means a user outside of our configured regions
|
|
53
|
+
* has completed age verification. In this case, we can't determine their
|
|
54
|
+
* access level, so we throw an error.
|
|
55
|
+
*
|
|
56
|
+
* This case is also guarded in `app.bsky.ageassurance.begin`.
|
|
57
|
+
*/
|
|
58
|
+
throw new Error('Could not get config for region')
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function createLocationString(countryCode: string, regionCode?: string) {
|
|
63
|
+
return regionCode
|
|
64
|
+
? `${countryCode.toUpperCase()}-${regionCode.toUpperCase()}`
|
|
65
|
+
: countryCode.toUpperCase()
|
|
66
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import express, { RequestHandler } from 'express'
|
|
2
|
+
import { ageAssuranceLogger as logger } from '../../../logger'
|
|
3
|
+
import { AGE_ASSURANCE_CONFIG } from '../const'
|
|
4
|
+
import {
|
|
5
|
+
type KWSWebhookAgeVerified,
|
|
6
|
+
parseKWSAgeVerifiedWebhook,
|
|
7
|
+
} from '../kws/age-verified'
|
|
8
|
+
import { parseKWSExternalPayloadV2 } from '../kws/external-payload'
|
|
9
|
+
import { createEvent } from '../stash'
|
|
10
|
+
import { type AppContextWithAA } from '../types'
|
|
11
|
+
import { computeAgeAssuranceAccessOrThrow } from '../util'
|
|
12
|
+
|
|
13
|
+
export const handler =
|
|
14
|
+
(ctx: AppContextWithAA): RequestHandler =>
|
|
15
|
+
async (req: express.Request, res: express.Response) => {
|
|
16
|
+
let body: KWSWebhookAgeVerified
|
|
17
|
+
try {
|
|
18
|
+
body = parseKWSAgeVerifiedWebhook(req.body)
|
|
19
|
+
} catch (err) {
|
|
20
|
+
const message = 'Failed to parse KWS webhook body'
|
|
21
|
+
logger.error({ err }, message)
|
|
22
|
+
return res.status(400).json({ error: message })
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const { status, externalPayload } = body.payload
|
|
26
|
+
const { verified, verifiedMinimumAge } = status
|
|
27
|
+
const { actorDid, countryCode, regionCode, attemptId } =
|
|
28
|
+
parseKWSExternalPayloadV2(externalPayload)
|
|
29
|
+
|
|
30
|
+
/*
|
|
31
|
+
* KWS does not send unverified webhooks for age verification, so we
|
|
32
|
+
* expect all webhooks to be verified. This is just a sanity check.
|
|
33
|
+
*/
|
|
34
|
+
if (!verified) {
|
|
35
|
+
const message = 'Expected KWS webhook to have verified status'
|
|
36
|
+
logger.error({}, message)
|
|
37
|
+
return res.status(400).json({ error: message })
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let result: ReturnType<typeof computeAgeAssuranceAccessOrThrow> | undefined
|
|
41
|
+
try {
|
|
42
|
+
result = computeAgeAssuranceAccessOrThrow(AGE_ASSURANCE_CONFIG, {
|
|
43
|
+
countryCode,
|
|
44
|
+
regionCode,
|
|
45
|
+
verifiedMinimumAge,
|
|
46
|
+
})
|
|
47
|
+
} catch (err) {
|
|
48
|
+
// internal errors
|
|
49
|
+
logger.error(
|
|
50
|
+
{ err, attemptId, actorDid, countryCode, regionCode },
|
|
51
|
+
'Failed to compute age assurance access',
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
if (result) {
|
|
57
|
+
await createEvent(ctx, actorDid, {
|
|
58
|
+
attemptId,
|
|
59
|
+
countryCode,
|
|
60
|
+
regionCode,
|
|
61
|
+
status: 'assured',
|
|
62
|
+
access: result.access,
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return res.status(200).end()
|
|
67
|
+
} catch (err) {
|
|
68
|
+
const message = 'Failed to handle KWS webhook'
|
|
69
|
+
logger.error(
|
|
70
|
+
{ err, attemptId, actorDid, countryCode, regionCode },
|
|
71
|
+
message,
|
|
72
|
+
)
|
|
73
|
+
return res.status(500).json({ error: message })
|
|
74
|
+
}
|
|
75
|
+
}
|