@atproto/pds 0.5.12 → 0.5.15

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.
Files changed (326) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/dist/api/app/bsky/notification/index.d.ts.map +1 -1
  3. package/dist/api/app/bsky/notification/index.js +2 -0
  4. package/dist/api/app/bsky/notification/index.js.map +1 -1
  5. package/dist/api/app/bsky/notification/unregisterPush.d.ts +4 -0
  6. package/dist/api/app/bsky/notification/unregisterPush.d.ts.map +1 -0
  7. package/dist/api/app/bsky/notification/unregisterPush.js +48 -0
  8. package/dist/api/app/bsky/notification/unregisterPush.js.map +1 -0
  9. package/dist/lexicons/app/bsky/actor/defs.defs.d.ts +6 -0
  10. package/dist/lexicons/app/bsky/actor/defs.defs.d.ts.map +1 -1
  11. package/dist/lexicons/app/bsky/actor/defs.defs.js +1 -0
  12. package/dist/lexicons/app/bsky/actor/defs.defs.js.map +1 -1
  13. package/package.json +42 -38
  14. package/build.templates.cjs +0 -22
  15. package/example.env +0 -42
  16. package/jest.config.cjs +0 -27
  17. package/src/account-manager/account-manager.ts +0 -916
  18. package/src/account-manager/db/index.ts +0 -21
  19. package/src/account-manager/db/migrations/001-init.ts +0 -115
  20. package/src/account-manager/db/migrations/002-account-deactivation.ts +0 -17
  21. package/src/account-manager/db/migrations/003-privileged-app-passwords.ts +0 -12
  22. package/src/account-manager/db/migrations/004-oauth.ts +0 -122
  23. package/src/account-manager/db/migrations/005-oauth-account-management.ts +0 -136
  24. package/src/account-manager/db/migrations/006-oauth-permission-sets.ts +0 -20
  25. package/src/account-manager/db/migrations/007-lexicon-failures-index.ts +0 -14
  26. package/src/account-manager/db/migrations/index.ts +0 -17
  27. package/src/account-manager/db/schema/account-device.ts +0 -14
  28. package/src/account-manager/db/schema/account.ts +0 -16
  29. package/src/account-manager/db/schema/actor.ts +0 -17
  30. package/src/account-manager/db/schema/app-password.ts +0 -13
  31. package/src/account-manager/db/schema/authorization-request.ts +0 -30
  32. package/src/account-manager/db/schema/authorized-client.ts +0 -23
  33. package/src/account-manager/db/schema/device.ts +0 -18
  34. package/src/account-manager/db/schema/email-token.ts +0 -19
  35. package/src/account-manager/db/schema/index.ts +0 -43
  36. package/src/account-manager/db/schema/invite-code.ts +0 -24
  37. package/src/account-manager/db/schema/lexicon.ts +0 -15
  38. package/src/account-manager/db/schema/refresh-token.ts +0 -11
  39. package/src/account-manager/db/schema/repo-root.ts +0 -12
  40. package/src/account-manager/db/schema/token.ts +0 -38
  41. package/src/account-manager/db/schema/used-refresh-token.ts +0 -13
  42. package/src/account-manager/helpers/account-device.ts +0 -63
  43. package/src/account-manager/helpers/account.ts +0 -355
  44. package/src/account-manager/helpers/auth.ts +0 -222
  45. package/src/account-manager/helpers/authorization-request.ts +0 -90
  46. package/src/account-manager/helpers/authorized-client.ts +0 -75
  47. package/src/account-manager/helpers/device.ts +0 -47
  48. package/src/account-manager/helpers/email-token.ts +0 -87
  49. package/src/account-manager/helpers/invite.ts +0 -263
  50. package/src/account-manager/helpers/lexicon.ts +0 -67
  51. package/src/account-manager/helpers/password.ts +0 -136
  52. package/src/account-manager/helpers/repo.ts +0 -24
  53. package/src/account-manager/helpers/scrypt.ts +0 -53
  54. package/src/account-manager/helpers/token.ts +0 -158
  55. package/src/account-manager/helpers/used-refresh-token.ts +0 -30
  56. package/src/account-manager/oauth-store.ts +0 -880
  57. package/src/account-manager/scope-reference-getter.ts +0 -106
  58. package/src/actor-store/actor-store-reader.ts +0 -45
  59. package/src/actor-store/actor-store-resources.ts +0 -8
  60. package/src/actor-store/actor-store-transactor.ts +0 -31
  61. package/src/actor-store/actor-store-writer.ts +0 -17
  62. package/src/actor-store/actor-store.ts +0 -210
  63. package/src/actor-store/blob/reader.ts +0 -160
  64. package/src/actor-store/blob/transactor.ts +0 -383
  65. package/src/actor-store/db/index.ts +0 -20
  66. package/src/actor-store/db/migrations/001-init.ts +0 -105
  67. package/src/actor-store/db/migrations/index.ts +0 -5
  68. package/src/actor-store/db/schema/account-pref.ts +0 -11
  69. package/src/actor-store/db/schema/backlink.ts +0 -9
  70. package/src/actor-store/db/schema/blob.ts +0 -14
  71. package/src/actor-store/db/schema/index.ts +0 -23
  72. package/src/actor-store/db/schema/record-blob.ts +0 -8
  73. package/src/actor-store/db/schema/record.ts +0 -14
  74. package/src/actor-store/db/schema/repo-block.ts +0 -10
  75. package/src/actor-store/db/schema/repo-root.ts +0 -10
  76. package/src/actor-store/migrate.ts +0 -39
  77. package/src/actor-store/preference/reader.ts +0 -48
  78. package/src/actor-store/preference/transactor.ts +0 -55
  79. package/src/actor-store/preference/util.ts +0 -39
  80. package/src/actor-store/record/reader.ts +0 -374
  81. package/src/actor-store/record/transactor.ts +0 -111
  82. package/src/actor-store/repo/reader.ts +0 -31
  83. package/src/actor-store/repo/sql-repo-reader.ts +0 -150
  84. package/src/actor-store/repo/sql-repo-transactor.ts +0 -105
  85. package/src/actor-store/repo/transactor.ts +0 -227
  86. package/src/api/app/bsky/actor/getPreferences.ts +0 -57
  87. package/src/api/app/bsky/actor/getProfile.ts +0 -41
  88. package/src/api/app/bsky/actor/getProfiles.ts +0 -51
  89. package/src/api/app/bsky/actor/index.ts +0 -13
  90. package/src/api/app/bsky/actor/putPreferences.ts +0 -62
  91. package/src/api/app/bsky/feed/getActorLikes.ts +0 -63
  92. package/src/api/app/bsky/feed/getAuthorFeed.ts +0 -84
  93. package/src/api/app/bsky/feed/getFeed.ts +0 -47
  94. package/src/api/app/bsky/feed/getPostThread.ts +0 -251
  95. package/src/api/app/bsky/feed/getTimeline.ts +0 -50
  96. package/src/api/app/bsky/feed/index.ts +0 -15
  97. package/src/api/app/bsky/index.ts +0 -11
  98. package/src/api/app/bsky/notification/index.ts +0 -7
  99. package/src/api/app/bsky/notification/registerPush.ts +0 -71
  100. package/src/api/app/bsky/util/resolver.ts +0 -20
  101. package/src/api/com/atproto/admin/deleteAccount.ts +0 -22
  102. package/src/api/com/atproto/admin/disableAccountInvites.ts +0 -18
  103. package/src/api/com/atproto/admin/disableInviteCodes.ts +0 -21
  104. package/src/api/com/atproto/admin/enableAccountInvites.ts +0 -18
  105. package/src/api/com/atproto/admin/getAccountInfo.ts +0 -32
  106. package/src/api/com/atproto/admin/getAccountInfos.ts +0 -34
  107. package/src/api/com/atproto/admin/getInviteCodes.ts +0 -126
  108. package/src/api/com/atproto/admin/getSubjectStatus.ts +0 -76
  109. package/src/api/com/atproto/admin/index.ts +0 -31
  110. package/src/api/com/atproto/admin/sendEmail.ts +0 -47
  111. package/src/api/com/atproto/admin/updateAccountEmail.ts +0 -32
  112. package/src/api/com/atproto/admin/updateAccountHandle.ts +0 -47
  113. package/src/api/com/atproto/admin/updateAccountPassword.ts +0 -34
  114. package/src/api/com/atproto/admin/updateSubjectStatus.ts +0 -68
  115. package/src/api/com/atproto/admin/util.ts +0 -66
  116. package/src/api/com/atproto/identity/getRecommendedDidCredentials.ts +0 -50
  117. package/src/api/com/atproto/identity/index.ts +0 -17
  118. package/src/api/com/atproto/identity/requestPlcOperationSignature.ts +0 -59
  119. package/src/api/com/atproto/identity/resolveHandle.ts +0 -50
  120. package/src/api/com/atproto/identity/signPlcOperation.ts +0 -85
  121. package/src/api/com/atproto/identity/submitPlcOperation.ts +0 -65
  122. package/src/api/com/atproto/identity/updateHandle.ts +0 -66
  123. package/src/api/com/atproto/index.ts +0 -19
  124. package/src/api/com/atproto/moderation/createReport.ts +0 -41
  125. package/src/api/com/atproto/moderation/index.ts +0 -7
  126. package/src/api/com/atproto/repo/applyWrites.ts +0 -226
  127. package/src/api/com/atproto/repo/createRecord.ts +0 -143
  128. package/src/api/com/atproto/repo/deleteRecord.ts +0 -122
  129. package/src/api/com/atproto/repo/describeRepo.ts +0 -43
  130. package/src/api/com/atproto/repo/getRecord.ts +0 -40
  131. package/src/api/com/atproto/repo/importRepo.ts +0 -101
  132. package/src/api/com/atproto/repo/index.ts +0 -25
  133. package/src/api/com/atproto/repo/listMissingBlobs.ts +0 -29
  134. package/src/api/com/atproto/repo/listRecords.ts +0 -36
  135. package/src/api/com/atproto/repo/putRecord.ts +0 -211
  136. package/src/api/com/atproto/repo/uploadBlob.ts +0 -60
  137. package/src/api/com/atproto/server/activateAccount.ts +0 -37
  138. package/src/api/com/atproto/server/checkAccountStatus.ts +0 -51
  139. package/src/api/com/atproto/server/confirmEmail.ts +0 -39
  140. package/src/api/com/atproto/server/createAccount.ts +0 -327
  141. package/src/api/com/atproto/server/createAppPassword.ts +0 -53
  142. package/src/api/com/atproto/server/createInviteCode.ts +0 -38
  143. package/src/api/com/atproto/server/createInviteCodes.ts +0 -42
  144. package/src/api/com/atproto/server/createSession.ts +0 -97
  145. package/src/api/com/atproto/server/deactivateAccount.ts +0 -41
  146. package/src/api/com/atproto/server/deleteAccount.ts +0 -85
  147. package/src/api/com/atproto/server/deleteSession.ts +0 -23
  148. package/src/api/com/atproto/server/describeServer.ts +0 -29
  149. package/src/api/com/atproto/server/getAccountInviteCodes.ts +0 -142
  150. package/src/api/com/atproto/server/getServiceAuth.ts +0 -111
  151. package/src/api/com/atproto/server/getSession.ts +0 -80
  152. package/src/api/com/atproto/server/index.ts +0 -55
  153. package/src/api/com/atproto/server/listAppPasswords.ts +0 -45
  154. package/src/api/com/atproto/server/refreshSession.ts +0 -72
  155. package/src/api/com/atproto/server/requestAccountDelete.ts +0 -66
  156. package/src/api/com/atproto/server/requestEmailConfirmation.ts +0 -68
  157. package/src/api/com/atproto/server/requestEmailUpdate.ts +0 -86
  158. package/src/api/com/atproto/server/requestPasswordReset.ts +0 -52
  159. package/src/api/com/atproto/server/reserveSigningKey.ts +0 -15
  160. package/src/api/com/atproto/server/resetPassword.ts +0 -50
  161. package/src/api/com/atproto/server/revokeAppPassword.ts +0 -42
  162. package/src/api/com/atproto/server/updateEmail.ts +0 -52
  163. package/src/api/com/atproto/server/util.ts +0 -136
  164. package/src/api/com/atproto/sync/deprecated/getCheckout.ts +0 -27
  165. package/src/api/com/atproto/sync/deprecated/getHead.ts +0 -33
  166. package/src/api/com/atproto/sync/getBlob.ts +0 -61
  167. package/src/api/com/atproto/sync/getBlocks.ts +0 -37
  168. package/src/api/com/atproto/sync/getLatestCommit.ts +0 -33
  169. package/src/api/com/atproto/sync/getRecord.ts +0 -49
  170. package/src/api/com/atproto/sync/getRepo.ts +0 -75
  171. package/src/api/com/atproto/sync/getRepoStatus.ts +0 -34
  172. package/src/api/com/atproto/sync/index.ts +0 -27
  173. package/src/api/com/atproto/sync/listBlobs.ts +0 -33
  174. package/src/api/com/atproto/sync/listRepos.ts +0 -74
  175. package/src/api/com/atproto/sync/subscribeRepos.ts +0 -73
  176. package/src/api/com/atproto/sync/util.ts +0 -37
  177. package/src/api/com/atproto/temp/checkSignupQueue.ts +0 -34
  178. package/src/api/com/atproto/temp/index.ts +0 -7
  179. package/src/api/index.ts +0 -10
  180. package/src/api/proxy.ts +0 -44
  181. package/src/app-view.ts +0 -24
  182. package/src/auth-output.ts +0 -52
  183. package/src/auth-routes.ts +0 -64
  184. package/src/auth-scope.ts +0 -40
  185. package/src/auth-verifier.ts +0 -668
  186. package/src/background.ts +0 -65
  187. package/src/basic-routes.ts +0 -52
  188. package/src/bsky-app-view.ts +0 -33
  189. package/src/config/config.ts +0 -553
  190. package/src/config/env.ts +0 -167
  191. package/src/config/index.ts +0 -3
  192. package/src/config/secrets.ts +0 -54
  193. package/src/context.ts +0 -523
  194. package/src/crawlers.ts +0 -46
  195. package/src/db/cast.ts +0 -52
  196. package/src/db/db.ts +0 -140
  197. package/src/db/index.ts +0 -4
  198. package/src/db/migrator.ts +0 -40
  199. package/src/db/pagination.ts +0 -132
  200. package/src/db/tables/moderation.ts +0 -80
  201. package/src/db/util.ts +0 -108
  202. package/src/did-cache/db/index.ts +0 -21
  203. package/src/did-cache/db/migrations.ts +0 -17
  204. package/src/did-cache/db/schema.ts +0 -9
  205. package/src/did-cache/index.ts +0 -131
  206. package/src/disk-blobstore.ts +0 -172
  207. package/src/error.ts +0 -19
  208. package/src/handle/explicit-slurs.ts +0 -22
  209. package/src/handle/index.ts +0 -49
  210. package/src/handle/reserved.ts +0 -1059
  211. package/src/image/image-url-builder.ts +0 -16
  212. package/src/index.ts +0 -189
  213. package/src/lexicons.ts +0 -4
  214. package/src/logger.ts +0 -35
  215. package/src/mailer/index.ts +0 -111
  216. package/src/mailer/moderation.ts +0 -33
  217. package/src/mailer/templates/confirm-email.d.ts +0 -4
  218. package/src/mailer/templates/confirm-email.hbs +0 -158
  219. package/src/mailer/templates/delete-account.d.ts +0 -4
  220. package/src/mailer/templates/delete-account.hbs +0 -156
  221. package/src/mailer/templates/plc-operation.d.ts +0 -4
  222. package/src/mailer/templates/plc-operation.hbs +0 -153
  223. package/src/mailer/templates/reset-password.d.ts +0 -4
  224. package/src/mailer/templates/reset-password.hbs +0 -154
  225. package/src/mailer/templates/update-email.d.ts +0 -4
  226. package/src/mailer/templates/update-email.hbs +0 -153
  227. package/src/mailer/templates.ts +0 -17
  228. package/src/pipethrough.ts +0 -716
  229. package/src/rate-limits.ts +0 -59
  230. package/src/read-after-write/index.ts +0 -3
  231. package/src/read-after-write/types.ts +0 -35
  232. package/src/read-after-write/util.ts +0 -101
  233. package/src/read-after-write/viewer.ts +0 -290
  234. package/src/redis.ts +0 -25
  235. package/src/repo/index.ts +0 -2
  236. package/src/repo/prepare.ts +0 -266
  237. package/src/repo/types.ts +0 -65
  238. package/src/scripts/README.md +0 -40
  239. package/src/scripts/index.ts +0 -20
  240. package/src/scripts/publish-identity.ts +0 -60
  241. package/src/scripts/rebuild-repo.ts +0 -119
  242. package/src/scripts/rotate-keys.ts +0 -146
  243. package/src/scripts/sequencer-recovery/index.ts +0 -23
  244. package/src/scripts/sequencer-recovery/recoverer.ts +0 -297
  245. package/src/scripts/sequencer-recovery/recovery-db.ts +0 -65
  246. package/src/scripts/sequencer-recovery/repair-repos.ts +0 -49
  247. package/src/scripts/sequencer-recovery/user-queues.ts +0 -44
  248. package/src/scripts/util.ts +0 -7
  249. package/src/sequencer/db/index.ts +0 -21
  250. package/src/sequencer/db/migrations/001-init.ts +0 -35
  251. package/src/sequencer/db/migrations/index.ts +0 -5
  252. package/src/sequencer/db/schema.ts +0 -19
  253. package/src/sequencer/events.ts +0 -199
  254. package/src/sequencer/index.ts +0 -3
  255. package/src/sequencer/outbox.ts +0 -123
  256. package/src/sequencer/sequencer.ts +0 -290
  257. package/src/util/compression.ts +0 -16
  258. package/src/util/debug.ts +0 -10
  259. package/src/util/http.ts +0 -31
  260. package/src/util/types.ts +0 -7
  261. package/src/well-known.ts +0 -30
  262. package/test.env +0 -2
  263. package/tests/__snapshots__/takedown-appeal.test.ts.snap +0 -43
  264. package/tests/_oauth_client_assets_middleware.ts +0 -23
  265. package/tests/_puppeteer.ts +0 -147
  266. package/tests/_types.d.ts +0 -16
  267. package/tests/_util.ts +0 -191
  268. package/tests/account-deactivation.test.ts +0 -204
  269. package/tests/account-deletion.test.ts +0 -300
  270. package/tests/account-manager.test.ts +0 -462
  271. package/tests/account-migration.test.ts +0 -221
  272. package/tests/account-status.test.ts +0 -206
  273. package/tests/account.test.ts +0 -595
  274. package/tests/app-passwords.test.ts +0 -262
  275. package/tests/auth.test.ts +0 -405
  276. package/tests/blob-deletes.test.ts +0 -204
  277. package/tests/create-post.test.ts +0 -79
  278. package/tests/crud.test.ts +0 -1595
  279. package/tests/db.test.ts +0 -170
  280. package/tests/email-confirmation.test.ts +0 -227
  281. package/tests/entryway-mock.ts +0 -323
  282. package/tests/entryway.test.ts +0 -145
  283. package/tests/file-uploads.test.ts +0 -301
  284. package/tests/get-service-auth.test.ts +0 -81
  285. package/tests/handle-validation.test.ts +0 -56
  286. package/tests/handles.test.ts +0 -274
  287. package/tests/invite-codes.test.ts +0 -367
  288. package/tests/invites-admin.test.ts +0 -252
  289. package/tests/moderation.test.ts +0 -280
  290. package/tests/moderator-auth.test.ts +0 -177
  291. package/tests/oauth.test.ts +0 -334
  292. package/tests/plc-operations.test.ts +0 -239
  293. package/tests/preferences.test.ts +0 -352
  294. package/tests/proxied/__snapshots__/admin.test.ts.snap +0 -516
  295. package/tests/proxied/__snapshots__/feedgen.test.ts.snap +0 -215
  296. package/tests/proxied/__snapshots__/views.test.ts.snap +0 -4456
  297. package/tests/proxied/admin.test.ts +0 -340
  298. package/tests/proxied/feedgen.test.ts +0 -66
  299. package/tests/proxied/notif.test.ts +0 -85
  300. package/tests/proxied/procedures.test.ts +0 -175
  301. package/tests/proxied/proxy-catchall.test.ts +0 -256
  302. package/tests/proxied/proxy-header.test.ts +0 -239
  303. package/tests/proxied/proxy-oauth-aud.test.ts +0 -182
  304. package/tests/proxied/read-after-write.test.ts +0 -382
  305. package/tests/proxied/views.test.ts +0 -608
  306. package/tests/races.test.ts +0 -89
  307. package/tests/rate-limits.test.ts +0 -70
  308. package/tests/recovery.test.ts +0 -180
  309. package/tests/seeds/basic.ts +0 -165
  310. package/tests/seeds/follows.ts +0 -57
  311. package/tests/seeds/likes.ts +0 -14
  312. package/tests/seeds/reposts.ts +0 -18
  313. package/tests/seeds/thread.ts +0 -40
  314. package/tests/seeds/users-bulk.ts +0 -246
  315. package/tests/seeds/users.ts +0 -67
  316. package/tests/sequencer.test.ts +0 -251
  317. package/tests/server.test.ts +0 -151
  318. package/tests/sync/invertible-ops.test.ts +0 -99
  319. package/tests/sync/list.test.ts +0 -43
  320. package/tests/sync/subscribe-repos.test.ts +0 -672
  321. package/tests/sync/sync.test.ts +0 -316
  322. package/tests/takedown-appeal.test.ts +0 -166
  323. package/tsconfig.build.json +0 -9
  324. package/tsconfig.build.tsbuildinfo +0 -1
  325. package/tsconfig.json +0 -7
  326. package/tsconfig.tests.json +0 -8
@@ -1,880 +0,0 @@
1
- import assert from 'node:assert'
2
- import { Client, createOp as createPlcOp } from '@did-plc/lib'
3
- import { Selectable } from 'kysely'
4
- import { Keypair, Secp256k1Keypair } from '@atproto/crypto'
5
- import { DidString, HandleString, getBlobCidString } from '@atproto/lex'
6
- import {
7
- Account,
8
- AccountStore,
9
- AuthenticateAccountData,
10
- AuthorizedClientData,
11
- AuthorizedClients,
12
- ClientId,
13
- Code,
14
- DeactivateAccountData,
15
- DeleteAccountConfirmInput,
16
- DeleteAccountRequestInput,
17
- DeviceAccount,
18
- DeviceData,
19
- DeviceId,
20
- DeviceStore,
21
- Did,
22
- FoundRequestResult,
23
- HandleUnavailableError,
24
- HandleUnavailableReason,
25
- InvalidCredentialsError,
26
- InvalidInviteCodeError,
27
- InvalidRequestError,
28
- LexiconData,
29
- LexiconStore,
30
- NewTokenData,
31
- ReactivateAccountData,
32
- RefreshToken,
33
- RequestData,
34
- RequestId,
35
- RequestStore,
36
- ResetPasswordConfirmInput,
37
- ResetPasswordRequestInput,
38
- SignUpData,
39
- TokenData,
40
- TokenId,
41
- TokenInfo,
42
- TokenStore,
43
- UpdateEmailConfirmInput,
44
- UpdateEmailRequestInput,
45
- UpdateEmailRequestOutput,
46
- UpdateHandleData,
47
- UpdateRequestData,
48
- VerifyEmailConfirmInput,
49
- VerifyEmailRequestInput,
50
- } from '@atproto/oauth-provider'
51
- import {
52
- AuthRequiredError as XrpcAuthRequiredError,
53
- InvalidRequestError as XrpcInvalidRequestError,
54
- } from '@atproto/xrpc-server'
55
- import { ActorStore } from '../actor-store/actor-store.js'
56
- import { BackgroundQueue } from '../background.js'
57
- import { fromDateISO } from '../db/index.js'
58
- import { ImageUrlBuilder } from '../image/image-url-builder.js'
59
- import { dbLogger } from '../logger.js'
60
- import { ServerMailer } from '../mailer/index.js'
61
- import { Sequencer } from '../sequencer/index.js'
62
- import { AccountManager, InvalidPasswordError } from './account-manager.js'
63
- import * as schemas from './db/schema/index.js'
64
- import * as accountDeviceHelper from './helpers/account-device.js'
65
- import { ActorAccount, UserAlreadyExistsError } from './helpers/account.js'
66
- import * as authRequestHelper from './helpers/authorization-request.js'
67
- import * as authorizedClientHelper from './helpers/authorized-client.js'
68
- import * as deviceHelper from './helpers/device.js'
69
- import * as lexiconHelper from './helpers/lexicon.js'
70
- import * as tokenHelper from './helpers/token.js'
71
- import * as usedRefreshTokenHelper from './helpers/used-refresh-token.js'
72
-
73
- /**
74
- * This class' purpose is to implement the interface needed by the OAuthProvider
75
- * to interact with the account database (through the {@link AccountManager}).
76
- *
77
- * @note The use of this class assumes that there is no entryway.
78
- */
79
- export class OAuthStore
80
- implements AccountStore, RequestStore, DeviceStore, LexiconStore, TokenStore
81
- {
82
- constructor(
83
- private readonly accountManager: AccountManager,
84
- private readonly actorStore: ActorStore,
85
- private readonly imageUrlBuilder: ImageUrlBuilder,
86
- private readonly backgroundQueue: BackgroundQueue,
87
- private readonly mailer: ServerMailer,
88
- private readonly sequencer: Sequencer,
89
- private readonly plcClient: Client,
90
- private readonly plcRotationKey: Keypair,
91
- private readonly publicUrl: string,
92
- private readonly recoveryDidKey: string | null,
93
- ) {}
94
-
95
- private get db() {
96
- const { db } = this.accountManager
97
- if (db.destroyed) throw new Error('Database connection is closed')
98
- return db
99
- }
100
-
101
- private get serviceDid() {
102
- return this.accountManager.serviceDid
103
- }
104
-
105
- private async verifyEmailAvailability(email: string): Promise<void> {
106
- // @NOTE Email validity & disposability check performed by the OAuthProvider
107
-
108
- const account = await this.accountManager.getAccountByEmail(email, {
109
- includeDeactivated: true,
110
- includeTakenDown: true,
111
- })
112
-
113
- if (account) {
114
- throw new InvalidRequestError(`Email already taken`)
115
- }
116
- }
117
-
118
- private async verifyInviteCode(code: string) {
119
- try {
120
- await this.accountManager.ensureInviteIsAvailable(code)
121
- } catch (err) {
122
- const message =
123
- err instanceof XrpcInvalidRequestError ? err.message : undefined
124
- throw new InvalidInviteCodeError(message, err)
125
- }
126
- }
127
-
128
- // AccountStore
129
-
130
- async createAccount({
131
- locale: _locale,
132
- inviteCode,
133
- handle,
134
- email,
135
- password,
136
- }: SignUpData): Promise<Account> {
137
- // @TODO Send an account creation confirmation email (+verification link) to the user (in their locale)
138
- // @NOTE Password strength & length already enforced by the OAuthProvider
139
-
140
- await Promise.all([
141
- this.verifyEmailAvailability(email),
142
- this.verifyHandleAvailability(handle),
143
- !inviteCode || this.verifyInviteCode(inviteCode),
144
- ])
145
-
146
- // @TODO The code bellow should probably be refactored to be common with the
147
- // code of the `com.atproto.server.createAccount` XRPC endpoint.
148
-
149
- const signingKey = await Secp256k1Keypair.create({ exportable: true })
150
- const signingKeyDid = signingKey.did()
151
-
152
- const canTombstone =
153
- // @NOTE IMPORTANT We don't support "bring your own DID" here (yet?). If
154
- // we ever do, make sure to update the computation of canTombstone so that
155
- // the user's did don't get tombstoned.
156
- true
157
-
158
- const plc = await createPlcOp({
159
- signingKey: signingKeyDid,
160
- rotationKeys: this.recoveryDidKey
161
- ? [this.recoveryDidKey, this.plcRotationKey.did()]
162
- : [this.plcRotationKey.did()],
163
- handle,
164
- pds: this.publicUrl,
165
- signer: this.plcRotationKey,
166
- })
167
-
168
- const did = plc.did as DidString
169
-
170
- try {
171
- await this.actorStore.create(did, signingKey)
172
-
173
- try {
174
- const commit = await this.actorStore.transact(did, (actorTxn) => {
175
- return actorTxn.repo.createRepo([])
176
- })
177
-
178
- await this.plcClient.sendOperation(did, plc.op)
179
-
180
- try {
181
- await this.accountManager.createAccount({
182
- did,
183
- handle,
184
- email,
185
- password,
186
- inviteCode,
187
- repoCid: commit.cid,
188
- repoRev: commit.rev,
189
- })
190
-
191
- try {
192
- await this.sequencer.sequenceAccountCreation(did, handle, commit)
193
-
194
- try {
195
- await this.actorStore
196
- .clearReservedKeypair(signingKeyDid, did)
197
- .catch((err) => {
198
- // @NOTE This is a cleanup operation so we won't fail the
199
- // whole flow if it fails, but we log it just in case
200
- dbLogger.error(
201
- { did, signingKeyDid, err },
202
- 'Failed to clear reserved keypair',
203
- )
204
- })
205
-
206
- const account = await this.accountManager.getAccount(did)
207
- assert(account, 'Account not found after creation')
208
-
209
- return await this.buildAccount(account)
210
- } catch (err) {
211
- await this.sequencer.sequenceAccountDeletion(did)
212
- throw err
213
- }
214
- } catch (err) {
215
- await this.accountManager.deleteAccount(did)
216
- throw err
217
- }
218
- } catch (err) {
219
- if (canTombstone) {
220
- await this.plcClient.tombstone(did, this.plcRotationKey)
221
- }
222
- throw err
223
- }
224
- } catch (err) {
225
- await this.actorStore.destroy(did)
226
- throw err
227
- }
228
- } catch (err) {
229
- // XrpcError => OAuthError
230
- if (err instanceof XrpcInvalidRequestError) {
231
- throw new InvalidRequestError(err.message, err)
232
- }
233
- throw err
234
- }
235
- }
236
-
237
- async authenticateAccount({
238
- locale: _locale,
239
- username: identifier,
240
- password,
241
- // Not supported by the PDS (yet?)
242
- emailOtp = undefined,
243
- }: AuthenticateAccountData): Promise<Account> {
244
- // @TODO (?) Send an email to the user to notify them of the login attempt
245
- try {
246
- // Should never happen
247
- if (emailOtp != null) {
248
- throw new Error('Email OTP is not supported')
249
- }
250
-
251
- const { user, appPassword, isSoftDeleted } =
252
- await this.accountManager.login({ identifier, password })
253
-
254
- if (isSoftDeleted) {
255
- throw new InvalidRequestError('Account was taken down')
256
- }
257
-
258
- if (appPassword) {
259
- throw new InvalidRequestError('App passwords are not allowed')
260
- }
261
-
262
- return this.buildAccount(user)
263
- } catch (err) {
264
- // `InvalidPasswordError` is a subclass of `XrpcAuthRequiredError`,
265
- // so it must be checked first. Surfacing the matched `did` as the
266
- // `sub` lets the oauth-provider's `onSignInFailed` hook distinguish
267
- // "identifier known, credentials wrong" from "identifier unknown".
268
- if (err instanceof InvalidPasswordError) {
269
- throw new InvalidCredentialsError(err.message, err.did, err)
270
- }
271
- if (err instanceof XrpcAuthRequiredError) {
272
- throw new InvalidCredentialsError(err.message, undefined, err)
273
- }
274
- throw err
275
- }
276
- }
277
-
278
- async setAuthorizedClient(
279
- did: Did,
280
- clientId: ClientId,
281
- data: AuthorizedClientData,
282
- ): Promise<void> {
283
- await authorizedClientHelper.upsert(this.db, did, clientId, data)
284
- }
285
-
286
- async getAccount(did: Did): Promise<{
287
- account: Account
288
- authorizedClients: AuthorizedClients
289
- }> {
290
- const accountRow = await this.accountManager.getAccount(did, {
291
- includeDeactivated: true,
292
- includeTakenDown: false,
293
- })
294
-
295
- assert(accountRow, 'Account not found')
296
-
297
- const account = await this.buildAccount(accountRow)
298
- const authorizedClients = await authorizedClientHelper.getAuthorizedClients(
299
- this.db,
300
- did,
301
- )
302
-
303
- return { account, authorizedClients }
304
- }
305
-
306
- async upsertDeviceAccount(deviceId: DeviceId, sub: string): Promise<void> {
307
- await this.db.executeWithRetry(
308
- accountDeviceHelper.upsertQB(this.db, deviceId, sub),
309
- )
310
- }
311
-
312
- async getDeviceAccount(
313
- deviceId: DeviceId,
314
- did: Did,
315
- ): Promise<DeviceAccount | null> {
316
- const row = await accountDeviceHelper
317
- .selectQB(this.db, { deviceId, did })
318
- .executeTakeFirst()
319
-
320
- if (!row) return null
321
-
322
- return {
323
- deviceId,
324
- deviceData: deviceHelper.rowToDeviceData(row),
325
- account: await this.buildAccount(row),
326
- authorizedClients: await authorizedClientHelper.getAuthorizedClients(
327
- this.db,
328
- did,
329
- ),
330
- createdAt: fromDateISO(row.adCreatedAt),
331
- updatedAt: fromDateISO(row.adUpdatedAt),
332
- }
333
- }
334
-
335
- async removeDeviceAccount(deviceId: DeviceId, did: Did): Promise<void> {
336
- await this.db.executeWithRetry(
337
- accountDeviceHelper.removeQB(this.db, deviceId, did),
338
- )
339
- }
340
-
341
- async listDeviceAccounts(
342
- filter: { did: Did } | { deviceId: DeviceId },
343
- ): Promise<DeviceAccount[]> {
344
- const rows = await accountDeviceHelper.selectQB(this.db, filter).execute()
345
-
346
- const uniqueDids = [...new Set(rows.map((row) => row.did))]
347
-
348
- // Enrich all distinct account with their profile data
349
- const accounts = new Map(
350
- await Promise.all(
351
- Array.from(uniqueDids, async (did): Promise<[Did, Account]> => {
352
- const row = rows.find((r) => r.did === did)!
353
- return [did, await this.buildAccount(row)]
354
- }),
355
- ),
356
- )
357
-
358
- const authorizedClientsMap =
359
- await authorizedClientHelper.getAuthorizedClientsMulti(
360
- this.db,
361
- uniqueDids,
362
- )
363
-
364
- return rows.map((row) => ({
365
- deviceId: row.deviceId,
366
- deviceData: deviceHelper.rowToDeviceData(row),
367
- account: accounts.get(row.did)!,
368
- authorizedClients: authorizedClientsMap.get(row.did)!,
369
- createdAt: fromDateISO(row.adCreatedAt),
370
- updatedAt: fromDateISO(row.adUpdatedAt),
371
- }))
372
- }
373
-
374
- async resetPasswordRequest({
375
- email,
376
- locale,
377
- }: ResetPasswordRequestInput): Promise<Account | null> {
378
- const account = await this.accountManager.getAccountByEmail(email, {
379
- includeDeactivated: true,
380
- includeTakenDown: false,
381
- })
382
-
383
- if (!account?.email || !account?.handle) return null
384
-
385
- const { handle } = account
386
- const token = await this.accountManager.createEmailToken(
387
- account.did,
388
- 'reset_password',
389
- )
390
-
391
- await this.mailer.sendResetPassword(
392
- { handle, token, locale },
393
- { to: account.email },
394
- )
395
-
396
- return this.buildAccount(account)
397
- }
398
-
399
- async resetPasswordConfirm(
400
- data: ResetPasswordConfirmInput,
401
- ): Promise<Account | null> {
402
- try {
403
- const did = await this.accountManager.resetPassword(data)
404
- const account = await this.accountManager.getAccount(did, {
405
- includeDeactivated: true,
406
- includeTakenDown: false,
407
- })
408
-
409
- return account ? this.buildAccount(account) : null
410
- } catch (err) {
411
- if (err instanceof XrpcInvalidRequestError) {
412
- return null
413
- }
414
-
415
- throw err
416
- }
417
- }
418
-
419
- async verifyHandleAvailability(handle: HandleString): Promise<void> {
420
- // @NOTE Handle validity & normalization already enforced by the OAuthProvider
421
- try {
422
- const normalized =
423
- await this.accountManager.normalizeAndValidateHandle(handle)
424
-
425
- // Should never happen (OAuthProvider should have already validated the
426
- // handle) This check is just a safeguard against future normalization
427
- // changes.
428
- if (normalized !== handle) {
429
- throw new HandleUnavailableError('syntax', 'Invalid handle')
430
- }
431
-
432
- const account = await this.accountManager.getAccount(normalized, {
433
- includeDeactivated: true,
434
- includeTakenDown: true,
435
- })
436
-
437
- if (account) {
438
- throw new HandleUnavailableError('taken')
439
- }
440
- } catch (err) {
441
- throw toHandleUnavailableError(err)
442
- }
443
- }
444
-
445
- // RequestStore
446
-
447
- async createRequest(id: RequestId, data: RequestData): Promise<void> {
448
- await this.db.executeWithRetry(
449
- authRequestHelper.createQB(this.db, id, data),
450
- )
451
- }
452
-
453
- async readRequest(id: RequestId): Promise<RequestData | null> {
454
- try {
455
- const row = await authRequestHelper.readQB(this.db, id).executeTakeFirst()
456
- if (!row) return null
457
- return authRequestHelper.rowToRequestData(row)
458
- } finally {
459
- // Take the opportunity to clean up expired requests. Do this after we got
460
- // the current (potentially expired) request data to allow the provider to
461
- // handle expired requests.
462
- this.backgroundQueue.add(async () => {
463
- await this.db.executeWithRetry(
464
- authRequestHelper.removeOldExpiredQB(this.db),
465
- )
466
- })
467
- }
468
- }
469
-
470
- async updateRequest(id: RequestId, data: UpdateRequestData): Promise<void> {
471
- await this.db.executeWithRetry(
472
- authRequestHelper.updateQB(this.db, id, data),
473
- )
474
- }
475
-
476
- async deleteRequest(id: RequestId): Promise<void> {
477
- await this.db.executeWithRetry(authRequestHelper.removeByIdQB(this.db, id))
478
- }
479
-
480
- async consumeRequestCode(code: Code): Promise<FoundRequestResult | null> {
481
- const row = await authRequestHelper
482
- .consumeByCodeQB(this.db, code)
483
- .executeTakeFirst()
484
- return row ? authRequestHelper.rowToFoundRequestResult(row) : null
485
- }
486
-
487
- // DeviceStore
488
-
489
- async createDevice(deviceId: DeviceId, data: DeviceData): Promise<void> {
490
- await this.db.executeWithRetry(
491
- deviceHelper.createQB(this.db, deviceId, data),
492
- )
493
- }
494
-
495
- async readDevice(deviceId: DeviceId): Promise<null | DeviceData> {
496
- const row = await deviceHelper.readQB(this.db, deviceId).executeTakeFirst()
497
- return row ? deviceHelper.rowToDeviceData(row) : null
498
- }
499
-
500
- async updateDevice(
501
- deviceId: DeviceId,
502
- data: Partial<DeviceData>,
503
- ): Promise<void> {
504
- await this.db.executeWithRetry(
505
- deviceHelper.updateQB(this.db, deviceId, data),
506
- )
507
- }
508
-
509
- async deleteDevice(deviceId: DeviceId): Promise<void> {
510
- // Will cascade to device_account (device_account_device_id_fk)
511
- await this.db.executeWithRetry(deviceHelper.removeQB(this.db, deviceId))
512
- }
513
-
514
- // LexiconStore
515
-
516
- async findLexicon(nsid: string): Promise<LexiconData | null> {
517
- return lexiconHelper.find(this.db, nsid)
518
- }
519
-
520
- async storeLexicon(nsid: string, data: LexiconData): Promise<void> {
521
- return lexiconHelper.upsert(this.db, nsid, data)
522
- }
523
-
524
- async deleteLexicon(nsid: string): Promise<void> {
525
- return lexiconHelper.remove(this.db, nsid)
526
- }
527
-
528
- // TokenStore
529
-
530
- async createToken(
531
- id: TokenId,
532
- data: TokenData,
533
- refreshToken?: RefreshToken,
534
- ): Promise<void> {
535
- await this.db.transaction(async (dbTxn) => {
536
- if (refreshToken) {
537
- const { count } = await usedRefreshTokenHelper
538
- .countQB(dbTxn, refreshToken)
539
- .executeTakeFirstOrThrow()
540
-
541
- if (count > 0) {
542
- throw new Error('Refresh token already in use')
543
- }
544
- }
545
-
546
- return tokenHelper.createQB(dbTxn, id, data, refreshToken).execute()
547
- })
548
- }
549
-
550
- async listAccountTokens(did: Did): Promise<TokenInfo[]> {
551
- const rows = await tokenHelper.findByQB(this.db, { did }).execute()
552
- return Promise.all(rows.map((row) => this.toTokenInfo(row)))
553
- }
554
-
555
- async readToken(tokenId: TokenId): Promise<TokenInfo | null> {
556
- const row = await tokenHelper
557
- .findByQB(this.db, { tokenId })
558
- .executeTakeFirst()
559
- return row ? this.toTokenInfo(row) : null
560
- }
561
-
562
- async deleteToken(tokenId: TokenId): Promise<void> {
563
- // Will cascade to used_refresh_token (used_refresh_token_fk)
564
- await this.db.executeWithRetry(tokenHelper.removeQB(this.db, tokenId))
565
- }
566
-
567
- async rotateToken(
568
- tokenId: TokenId,
569
- newTokenId: TokenId,
570
- newRefreshToken: RefreshToken,
571
- newData: NewTokenData,
572
- ): Promise<void> {
573
- const err = await this.db.transaction(async (dbTxn) => {
574
- const { id, currentRefreshToken } = await tokenHelper
575
- .forRotateQB(dbTxn, tokenId)
576
- .executeTakeFirstOrThrow()
577
-
578
- if (currentRefreshToken) {
579
- await usedRefreshTokenHelper
580
- .insertQB(dbTxn, id, currentRefreshToken)
581
- .execute()
582
- }
583
-
584
- const { count } = await usedRefreshTokenHelper
585
- .countQB(dbTxn, newRefreshToken)
586
- .executeTakeFirstOrThrow()
587
-
588
- if (count > 0) {
589
- // Do NOT throw (we don't want the transaction to be rolled back)
590
- return new Error('New refresh token already in use')
591
- }
592
-
593
- await tokenHelper
594
- .rotateQB(dbTxn, id, newTokenId, newRefreshToken, newData)
595
- .execute()
596
- })
597
-
598
- if (err) throw err
599
- }
600
-
601
- async findTokenByRefreshToken(
602
- refreshToken: RefreshToken,
603
- ): Promise<TokenInfo | null> {
604
- const used = await usedRefreshTokenHelper
605
- .findByTokenQB(this.db, refreshToken)
606
- .executeTakeFirst()
607
-
608
- const search = used
609
- ? { id: used.tokenId }
610
- : { currentRefreshToken: refreshToken }
611
-
612
- const row = await tokenHelper.findByQB(this.db, search).executeTakeFirst()
613
- return row ? this.toTokenInfo(row) : null
614
- }
615
-
616
- async findTokenByCode(code: Code): Promise<TokenInfo | null> {
617
- const row = await tokenHelper.findByQB(this.db, { code }).executeTakeFirst()
618
- return row ? this.toTokenInfo(row) : null
619
- }
620
-
621
- async verifyEmailRequest({
622
- did,
623
- locale,
624
- }: VerifyEmailRequestInput): Promise<void> {
625
- try {
626
- await this.accountManager.requestEmailConfirmation(did, { locale })
627
- } catch (err) {
628
- if (err instanceof XrpcAuthRequiredError) {
629
- throw new InvalidRequestError(err.message, err)
630
- }
631
-
632
- throw err
633
- }
634
- }
635
-
636
- async verifyEmailConfirm({
637
- did,
638
- email,
639
- token,
640
- }: VerifyEmailConfirmInput): Promise<Account | null> {
641
- try {
642
- const account = await this.accountManager.confirmEmail(did, email, token)
643
-
644
- return this.buildAccount(account)
645
- } catch (err) {
646
- if (err instanceof XrpcInvalidRequestError) {
647
- return null
648
- }
649
-
650
- throw err
651
- }
652
- }
653
-
654
- async updateEmailRequest({
655
- did,
656
- locale,
657
- }: UpdateEmailRequestInput): Promise<UpdateEmailRequestOutput> {
658
- return this.accountManager.requestEmailUpdate(did, { locale })
659
- }
660
-
661
- async updateEmailConfirm({
662
- did,
663
- token,
664
- email,
665
- locale,
666
- }: UpdateEmailConfirmInput): Promise<Account | null> {
667
- try {
668
- const account = await this.accountManager.updateEmail(did, email, token, {
669
- sendConfirmationEmail: true,
670
- locale,
671
- })
672
-
673
- return this.buildAccount(account)
674
- } catch (cause) {
675
- if (cause instanceof UserAlreadyExistsError) {
676
- throw new InvalidRequestError(cause.message, cause)
677
- }
678
-
679
- throw cause
680
- }
681
- }
682
-
683
- async updateHandle({ did, handle }: UpdateHandleData): Promise<Account> {
684
- try {
685
- const account = await this.accountManager.updateHandle(did, handle)
686
-
687
- return this.buildAccount(account)
688
- } catch (err) {
689
- throw toHandleUnavailableError(err)
690
- }
691
- }
692
-
693
- async deactivateAccount({
694
- did,
695
- deleteAfter,
696
- }: DeactivateAccountData): Promise<Account> {
697
- const { account } = await this.accountManager.deactivateAccount(did, {
698
- deleteCredentials: true,
699
- deleteAfter,
700
- })
701
-
702
- return this.buildAccount(account)
703
- }
704
-
705
- async reactivateAccount({ did }: ReactivateAccountData): Promise<Account> {
706
- try {
707
- const { account } = await this.accountManager.activateAccount(did)
708
-
709
- return this.buildAccount(account)
710
- } catch (err) {
711
- if (err instanceof XrpcInvalidRequestError) {
712
- throw new InvalidRequestError(err.message, err)
713
- }
714
-
715
- throw err
716
- }
717
- }
718
-
719
- async deleteAccountRequest({
720
- did,
721
- locale,
722
- }: DeleteAccountRequestInput): Promise<void> {
723
- // Mirror the XRPC `com.atproto.server.requestAccountDelete` flow
724
- // (no-entryway path): generate an email confirmation token and dispatch
725
- // it to the account's email address.
726
- const account = await this.accountManager.getAccount(did, {
727
- includeDeactivated: true,
728
- includeTakenDown: true,
729
- })
730
- if (!account) {
731
- throw new InvalidRequestError('Account not found')
732
- }
733
- if (!account.email) {
734
- throw new InvalidRequestError('Account does not have an email address')
735
- }
736
-
737
- const token = await this.accountManager.createEmailToken(
738
- did,
739
- 'delete_account',
740
- )
741
- await this.mailer.sendAccountDelete(
742
- { token, locale },
743
- { to: account.email },
744
- )
745
- }
746
-
747
- async deleteAccountConfirm({
748
- did,
749
- token,
750
- password,
751
- }: DeleteAccountConfirmInput): Promise<void> {
752
- // Mirror the XRPC `com.atproto.server.deleteAccount` flow (no-entryway
753
- // path): verify the password, validate the email confirmation token,
754
- // destroy the actor store, delete the account row, and emit the
755
- // tombstone account event.
756
- const account = await this.accountManager.getAccount(did, {
757
- includeDeactivated: true,
758
- includeTakenDown: true,
759
- })
760
- if (!account) {
761
- throw new InvalidRequestError('Account not found')
762
- }
763
-
764
- const validPass = await this.accountManager.verifyAccountPassword(
765
- did,
766
- password,
767
- )
768
- if (!validPass) {
769
- throw new InvalidCredentialsError('Invalid did or password', did)
770
- }
771
-
772
- await this.accountManager.assertValidEmailToken(
773
- did,
774
- 'delete_account',
775
- token,
776
- )
777
-
778
- // @NOTE Order matters here: first "unlink" the account by removing it
779
- // from the account manager database ("source of truth"), then notify the
780
- // sequencer, and finally cleanup files from the file system.
781
- await this.accountManager.deleteAccount(did)
782
- try {
783
- await this.sequencer.sequenceAccountDeletion(did)
784
- } finally {
785
- await this.actorStore.destroy(did)
786
- }
787
- }
788
-
789
- private async toTokenInfo(
790
- row: ActorAccount & Selectable<schemas.Token>,
791
- ): Promise<TokenInfo> {
792
- return {
793
- id: row.tokenId,
794
- data: tokenHelper.toTokenData(row),
795
- account: await this.buildAccount(row),
796
- currentRefreshToken: row.currentRefreshToken,
797
- }
798
- }
799
-
800
- private async buildAccount(row: ActorAccount): Promise<Account> {
801
- const account: Account = {
802
- did: row.did,
803
- pds: this.serviceDid,
804
- email: row.email || undefined,
805
- emailVerified: row.email ? row.emailConfirmedAt != null : undefined,
806
- handle: row.handle || undefined,
807
- deactivated: row.deactivatedAt != null,
808
- }
809
-
810
- if (!account.name || !account.picture) {
811
- const { did } = account
812
-
813
- const profile = await this.actorStore
814
- .read(did, async (store) => {
815
- return store.record.getProfileRecord()
816
- })
817
- .catch((err) => {
818
- dbLogger.error({ err }, 'Failed to get profile record')
819
- return null // No need to propagate
820
- })
821
-
822
- if (profile) {
823
- const { avatar, displayName } = profile
824
-
825
- account.name ||= displayName
826
- account.picture ||= avatar
827
- ? this.imageUrlBuilder.build('avatar', did, getBlobCidString(avatar))
828
- : undefined
829
- }
830
- }
831
-
832
- return account
833
- }
834
- }
835
-
836
- function toHandleUnavailableError(err: unknown): unknown {
837
- if (err instanceof XrpcInvalidRequestError) {
838
- const reason = toHandleUnavailableReason(err)
839
- if (reason) throw new HandleUnavailableError(reason, err.message, err)
840
-
841
- return new InvalidRequestError(err.message, err)
842
- }
843
-
844
- return err
845
- }
846
-
847
- /**
848
- * This function maps specific `XrpcInvalidRequestError`, thrown by the
849
- * `AccountManager` when validating a handle, to a more specific
850
- * `HandleUnavailableError` with a reason. This allows the OAuthProvider to
851
- * provide properly localized and specific error messages to the user when a
852
- * handle is not available.
853
- */
854
- function toHandleUnavailableReason(
855
- err: XrpcInvalidRequestError,
856
- ): HandleUnavailableReason | undefined {
857
- switch (err.error) {
858
- case 'HandleNotAvailable': {
859
- if (err.message === 'Reserved handle') return 'reserved'
860
- return 'taken'
861
- }
862
-
863
- case 'UnsupportedDomain': {
864
- return 'unsupported'
865
- }
866
-
867
- case 'InvalidHandle': {
868
- if (err.message === 'Inappropriate language in handle') return 'slur'
869
- if (err.message === 'Handle TLD is invalid or disallowed') return 'domain'
870
- return 'syntax'
871
- }
872
-
873
- case 'InvalidRequest': {
874
- if (err.message === 'External handle did not resolve to DID') {
875
- return 'resolution'
876
- }
877
- return undefined
878
- }
879
- }
880
- }