@atproto/pds 0.5.11 → 0.5.14

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 (322) hide show
  1. package/CHANGELOG.md +46 -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/package.json +42 -38
  10. package/build.templates.cjs +0 -22
  11. package/example.env +0 -42
  12. package/jest.config.cjs +0 -27
  13. package/src/account-manager/account-manager.ts +0 -916
  14. package/src/account-manager/db/index.ts +0 -21
  15. package/src/account-manager/db/migrations/001-init.ts +0 -115
  16. package/src/account-manager/db/migrations/002-account-deactivation.ts +0 -17
  17. package/src/account-manager/db/migrations/003-privileged-app-passwords.ts +0 -12
  18. package/src/account-manager/db/migrations/004-oauth.ts +0 -122
  19. package/src/account-manager/db/migrations/005-oauth-account-management.ts +0 -136
  20. package/src/account-manager/db/migrations/006-oauth-permission-sets.ts +0 -20
  21. package/src/account-manager/db/migrations/007-lexicon-failures-index.ts +0 -14
  22. package/src/account-manager/db/migrations/index.ts +0 -17
  23. package/src/account-manager/db/schema/account-device.ts +0 -14
  24. package/src/account-manager/db/schema/account.ts +0 -16
  25. package/src/account-manager/db/schema/actor.ts +0 -17
  26. package/src/account-manager/db/schema/app-password.ts +0 -13
  27. package/src/account-manager/db/schema/authorization-request.ts +0 -30
  28. package/src/account-manager/db/schema/authorized-client.ts +0 -23
  29. package/src/account-manager/db/schema/device.ts +0 -18
  30. package/src/account-manager/db/schema/email-token.ts +0 -19
  31. package/src/account-manager/db/schema/index.ts +0 -43
  32. package/src/account-manager/db/schema/invite-code.ts +0 -24
  33. package/src/account-manager/db/schema/lexicon.ts +0 -15
  34. package/src/account-manager/db/schema/refresh-token.ts +0 -11
  35. package/src/account-manager/db/schema/repo-root.ts +0 -12
  36. package/src/account-manager/db/schema/token.ts +0 -38
  37. package/src/account-manager/db/schema/used-refresh-token.ts +0 -13
  38. package/src/account-manager/helpers/account-device.ts +0 -63
  39. package/src/account-manager/helpers/account.ts +0 -355
  40. package/src/account-manager/helpers/auth.ts +0 -222
  41. package/src/account-manager/helpers/authorization-request.ts +0 -90
  42. package/src/account-manager/helpers/authorized-client.ts +0 -75
  43. package/src/account-manager/helpers/device.ts +0 -47
  44. package/src/account-manager/helpers/email-token.ts +0 -87
  45. package/src/account-manager/helpers/invite.ts +0 -263
  46. package/src/account-manager/helpers/lexicon.ts +0 -67
  47. package/src/account-manager/helpers/password.ts +0 -136
  48. package/src/account-manager/helpers/repo.ts +0 -24
  49. package/src/account-manager/helpers/scrypt.ts +0 -53
  50. package/src/account-manager/helpers/token.ts +0 -158
  51. package/src/account-manager/helpers/used-refresh-token.ts +0 -30
  52. package/src/account-manager/oauth-store.ts +0 -880
  53. package/src/account-manager/scope-reference-getter.ts +0 -106
  54. package/src/actor-store/actor-store-reader.ts +0 -45
  55. package/src/actor-store/actor-store-resources.ts +0 -8
  56. package/src/actor-store/actor-store-transactor.ts +0 -31
  57. package/src/actor-store/actor-store-writer.ts +0 -17
  58. package/src/actor-store/actor-store.ts +0 -210
  59. package/src/actor-store/blob/reader.ts +0 -160
  60. package/src/actor-store/blob/transactor.ts +0 -383
  61. package/src/actor-store/db/index.ts +0 -20
  62. package/src/actor-store/db/migrations/001-init.ts +0 -105
  63. package/src/actor-store/db/migrations/index.ts +0 -5
  64. package/src/actor-store/db/schema/account-pref.ts +0 -11
  65. package/src/actor-store/db/schema/backlink.ts +0 -9
  66. package/src/actor-store/db/schema/blob.ts +0 -14
  67. package/src/actor-store/db/schema/index.ts +0 -23
  68. package/src/actor-store/db/schema/record-blob.ts +0 -8
  69. package/src/actor-store/db/schema/record.ts +0 -14
  70. package/src/actor-store/db/schema/repo-block.ts +0 -10
  71. package/src/actor-store/db/schema/repo-root.ts +0 -10
  72. package/src/actor-store/migrate.ts +0 -39
  73. package/src/actor-store/preference/reader.ts +0 -48
  74. package/src/actor-store/preference/transactor.ts +0 -55
  75. package/src/actor-store/preference/util.ts +0 -39
  76. package/src/actor-store/record/reader.ts +0 -374
  77. package/src/actor-store/record/transactor.ts +0 -111
  78. package/src/actor-store/repo/reader.ts +0 -31
  79. package/src/actor-store/repo/sql-repo-reader.ts +0 -150
  80. package/src/actor-store/repo/sql-repo-transactor.ts +0 -105
  81. package/src/actor-store/repo/transactor.ts +0 -227
  82. package/src/api/app/bsky/actor/getPreferences.ts +0 -57
  83. package/src/api/app/bsky/actor/getProfile.ts +0 -41
  84. package/src/api/app/bsky/actor/getProfiles.ts +0 -51
  85. package/src/api/app/bsky/actor/index.ts +0 -13
  86. package/src/api/app/bsky/actor/putPreferences.ts +0 -62
  87. package/src/api/app/bsky/feed/getActorLikes.ts +0 -63
  88. package/src/api/app/bsky/feed/getAuthorFeed.ts +0 -84
  89. package/src/api/app/bsky/feed/getFeed.ts +0 -47
  90. package/src/api/app/bsky/feed/getPostThread.ts +0 -251
  91. package/src/api/app/bsky/feed/getTimeline.ts +0 -50
  92. package/src/api/app/bsky/feed/index.ts +0 -15
  93. package/src/api/app/bsky/index.ts +0 -11
  94. package/src/api/app/bsky/notification/index.ts +0 -7
  95. package/src/api/app/bsky/notification/registerPush.ts +0 -71
  96. package/src/api/app/bsky/util/resolver.ts +0 -20
  97. package/src/api/com/atproto/admin/deleteAccount.ts +0 -22
  98. package/src/api/com/atproto/admin/disableAccountInvites.ts +0 -18
  99. package/src/api/com/atproto/admin/disableInviteCodes.ts +0 -21
  100. package/src/api/com/atproto/admin/enableAccountInvites.ts +0 -18
  101. package/src/api/com/atproto/admin/getAccountInfo.ts +0 -32
  102. package/src/api/com/atproto/admin/getAccountInfos.ts +0 -34
  103. package/src/api/com/atproto/admin/getInviteCodes.ts +0 -126
  104. package/src/api/com/atproto/admin/getSubjectStatus.ts +0 -76
  105. package/src/api/com/atproto/admin/index.ts +0 -31
  106. package/src/api/com/atproto/admin/sendEmail.ts +0 -47
  107. package/src/api/com/atproto/admin/updateAccountEmail.ts +0 -32
  108. package/src/api/com/atproto/admin/updateAccountHandle.ts +0 -47
  109. package/src/api/com/atproto/admin/updateAccountPassword.ts +0 -34
  110. package/src/api/com/atproto/admin/updateSubjectStatus.ts +0 -68
  111. package/src/api/com/atproto/admin/util.ts +0 -66
  112. package/src/api/com/atproto/identity/getRecommendedDidCredentials.ts +0 -50
  113. package/src/api/com/atproto/identity/index.ts +0 -17
  114. package/src/api/com/atproto/identity/requestPlcOperationSignature.ts +0 -59
  115. package/src/api/com/atproto/identity/resolveHandle.ts +0 -50
  116. package/src/api/com/atproto/identity/signPlcOperation.ts +0 -85
  117. package/src/api/com/atproto/identity/submitPlcOperation.ts +0 -65
  118. package/src/api/com/atproto/identity/updateHandle.ts +0 -66
  119. package/src/api/com/atproto/index.ts +0 -19
  120. package/src/api/com/atproto/moderation/createReport.ts +0 -41
  121. package/src/api/com/atproto/moderation/index.ts +0 -7
  122. package/src/api/com/atproto/repo/applyWrites.ts +0 -226
  123. package/src/api/com/atproto/repo/createRecord.ts +0 -143
  124. package/src/api/com/atproto/repo/deleteRecord.ts +0 -122
  125. package/src/api/com/atproto/repo/describeRepo.ts +0 -43
  126. package/src/api/com/atproto/repo/getRecord.ts +0 -40
  127. package/src/api/com/atproto/repo/importRepo.ts +0 -101
  128. package/src/api/com/atproto/repo/index.ts +0 -25
  129. package/src/api/com/atproto/repo/listMissingBlobs.ts +0 -29
  130. package/src/api/com/atproto/repo/listRecords.ts +0 -36
  131. package/src/api/com/atproto/repo/putRecord.ts +0 -211
  132. package/src/api/com/atproto/repo/uploadBlob.ts +0 -60
  133. package/src/api/com/atproto/server/activateAccount.ts +0 -37
  134. package/src/api/com/atproto/server/checkAccountStatus.ts +0 -51
  135. package/src/api/com/atproto/server/confirmEmail.ts +0 -39
  136. package/src/api/com/atproto/server/createAccount.ts +0 -327
  137. package/src/api/com/atproto/server/createAppPassword.ts +0 -53
  138. package/src/api/com/atproto/server/createInviteCode.ts +0 -38
  139. package/src/api/com/atproto/server/createInviteCodes.ts +0 -42
  140. package/src/api/com/atproto/server/createSession.ts +0 -97
  141. package/src/api/com/atproto/server/deactivateAccount.ts +0 -41
  142. package/src/api/com/atproto/server/deleteAccount.ts +0 -85
  143. package/src/api/com/atproto/server/deleteSession.ts +0 -23
  144. package/src/api/com/atproto/server/describeServer.ts +0 -29
  145. package/src/api/com/atproto/server/getAccountInviteCodes.ts +0 -142
  146. package/src/api/com/atproto/server/getServiceAuth.ts +0 -111
  147. package/src/api/com/atproto/server/getSession.ts +0 -80
  148. package/src/api/com/atproto/server/index.ts +0 -55
  149. package/src/api/com/atproto/server/listAppPasswords.ts +0 -45
  150. package/src/api/com/atproto/server/refreshSession.ts +0 -72
  151. package/src/api/com/atproto/server/requestAccountDelete.ts +0 -66
  152. package/src/api/com/atproto/server/requestEmailConfirmation.ts +0 -68
  153. package/src/api/com/atproto/server/requestEmailUpdate.ts +0 -86
  154. package/src/api/com/atproto/server/requestPasswordReset.ts +0 -52
  155. package/src/api/com/atproto/server/reserveSigningKey.ts +0 -15
  156. package/src/api/com/atproto/server/resetPassword.ts +0 -50
  157. package/src/api/com/atproto/server/revokeAppPassword.ts +0 -42
  158. package/src/api/com/atproto/server/updateEmail.ts +0 -52
  159. package/src/api/com/atproto/server/util.ts +0 -136
  160. package/src/api/com/atproto/sync/deprecated/getCheckout.ts +0 -27
  161. package/src/api/com/atproto/sync/deprecated/getHead.ts +0 -33
  162. package/src/api/com/atproto/sync/getBlob.ts +0 -61
  163. package/src/api/com/atproto/sync/getBlocks.ts +0 -37
  164. package/src/api/com/atproto/sync/getLatestCommit.ts +0 -33
  165. package/src/api/com/atproto/sync/getRecord.ts +0 -49
  166. package/src/api/com/atproto/sync/getRepo.ts +0 -75
  167. package/src/api/com/atproto/sync/getRepoStatus.ts +0 -34
  168. package/src/api/com/atproto/sync/index.ts +0 -27
  169. package/src/api/com/atproto/sync/listBlobs.ts +0 -33
  170. package/src/api/com/atproto/sync/listRepos.ts +0 -74
  171. package/src/api/com/atproto/sync/subscribeRepos.ts +0 -73
  172. package/src/api/com/atproto/sync/util.ts +0 -37
  173. package/src/api/com/atproto/temp/checkSignupQueue.ts +0 -34
  174. package/src/api/com/atproto/temp/index.ts +0 -7
  175. package/src/api/index.ts +0 -10
  176. package/src/api/proxy.ts +0 -44
  177. package/src/app-view.ts +0 -24
  178. package/src/auth-output.ts +0 -52
  179. package/src/auth-routes.ts +0 -64
  180. package/src/auth-scope.ts +0 -40
  181. package/src/auth-verifier.ts +0 -668
  182. package/src/background.ts +0 -65
  183. package/src/basic-routes.ts +0 -52
  184. package/src/bsky-app-view.ts +0 -33
  185. package/src/config/config.ts +0 -553
  186. package/src/config/env.ts +0 -167
  187. package/src/config/index.ts +0 -3
  188. package/src/config/secrets.ts +0 -54
  189. package/src/context.ts +0 -523
  190. package/src/crawlers.ts +0 -46
  191. package/src/db/cast.ts +0 -52
  192. package/src/db/db.ts +0 -140
  193. package/src/db/index.ts +0 -4
  194. package/src/db/migrator.ts +0 -40
  195. package/src/db/pagination.ts +0 -132
  196. package/src/db/tables/moderation.ts +0 -80
  197. package/src/db/util.ts +0 -108
  198. package/src/did-cache/db/index.ts +0 -21
  199. package/src/did-cache/db/migrations.ts +0 -17
  200. package/src/did-cache/db/schema.ts +0 -9
  201. package/src/did-cache/index.ts +0 -131
  202. package/src/disk-blobstore.ts +0 -172
  203. package/src/error.ts +0 -19
  204. package/src/handle/explicit-slurs.ts +0 -22
  205. package/src/handle/index.ts +0 -49
  206. package/src/handle/reserved.ts +0 -1059
  207. package/src/image/image-url-builder.ts +0 -16
  208. package/src/index.ts +0 -189
  209. package/src/lexicons.ts +0 -4
  210. package/src/logger.ts +0 -35
  211. package/src/mailer/index.ts +0 -111
  212. package/src/mailer/moderation.ts +0 -33
  213. package/src/mailer/templates/confirm-email.d.ts +0 -4
  214. package/src/mailer/templates/confirm-email.hbs +0 -158
  215. package/src/mailer/templates/delete-account.d.ts +0 -4
  216. package/src/mailer/templates/delete-account.hbs +0 -156
  217. package/src/mailer/templates/plc-operation.d.ts +0 -4
  218. package/src/mailer/templates/plc-operation.hbs +0 -153
  219. package/src/mailer/templates/reset-password.d.ts +0 -4
  220. package/src/mailer/templates/reset-password.hbs +0 -154
  221. package/src/mailer/templates/update-email.d.ts +0 -4
  222. package/src/mailer/templates/update-email.hbs +0 -153
  223. package/src/mailer/templates.ts +0 -17
  224. package/src/pipethrough.ts +0 -716
  225. package/src/rate-limits.ts +0 -59
  226. package/src/read-after-write/index.ts +0 -3
  227. package/src/read-after-write/types.ts +0 -35
  228. package/src/read-after-write/util.ts +0 -101
  229. package/src/read-after-write/viewer.ts +0 -290
  230. package/src/redis.ts +0 -25
  231. package/src/repo/index.ts +0 -2
  232. package/src/repo/prepare.ts +0 -266
  233. package/src/repo/types.ts +0 -65
  234. package/src/scripts/README.md +0 -40
  235. package/src/scripts/index.ts +0 -20
  236. package/src/scripts/publish-identity.ts +0 -60
  237. package/src/scripts/rebuild-repo.ts +0 -119
  238. package/src/scripts/rotate-keys.ts +0 -146
  239. package/src/scripts/sequencer-recovery/index.ts +0 -23
  240. package/src/scripts/sequencer-recovery/recoverer.ts +0 -297
  241. package/src/scripts/sequencer-recovery/recovery-db.ts +0 -65
  242. package/src/scripts/sequencer-recovery/repair-repos.ts +0 -49
  243. package/src/scripts/sequencer-recovery/user-queues.ts +0 -44
  244. package/src/scripts/util.ts +0 -7
  245. package/src/sequencer/db/index.ts +0 -21
  246. package/src/sequencer/db/migrations/001-init.ts +0 -35
  247. package/src/sequencer/db/migrations/index.ts +0 -5
  248. package/src/sequencer/db/schema.ts +0 -19
  249. package/src/sequencer/events.ts +0 -199
  250. package/src/sequencer/index.ts +0 -3
  251. package/src/sequencer/outbox.ts +0 -123
  252. package/src/sequencer/sequencer.ts +0 -290
  253. package/src/util/compression.ts +0 -16
  254. package/src/util/debug.ts +0 -10
  255. package/src/util/http.ts +0 -31
  256. package/src/util/types.ts +0 -7
  257. package/src/well-known.ts +0 -30
  258. package/test.env +0 -2
  259. package/tests/__snapshots__/takedown-appeal.test.ts.snap +0 -43
  260. package/tests/_oauth_client_assets_middleware.ts +0 -23
  261. package/tests/_puppeteer.ts +0 -147
  262. package/tests/_types.d.ts +0 -16
  263. package/tests/_util.ts +0 -191
  264. package/tests/account-deactivation.test.ts +0 -204
  265. package/tests/account-deletion.test.ts +0 -300
  266. package/tests/account-manager.test.ts +0 -462
  267. package/tests/account-migration.test.ts +0 -221
  268. package/tests/account-status.test.ts +0 -206
  269. package/tests/account.test.ts +0 -595
  270. package/tests/app-passwords.test.ts +0 -262
  271. package/tests/auth.test.ts +0 -405
  272. package/tests/blob-deletes.test.ts +0 -204
  273. package/tests/create-post.test.ts +0 -79
  274. package/tests/crud.test.ts +0 -1595
  275. package/tests/db.test.ts +0 -170
  276. package/tests/email-confirmation.test.ts +0 -227
  277. package/tests/entryway-mock.ts +0 -323
  278. package/tests/entryway.test.ts +0 -145
  279. package/tests/file-uploads.test.ts +0 -301
  280. package/tests/get-service-auth.test.ts +0 -81
  281. package/tests/handle-validation.test.ts +0 -56
  282. package/tests/handles.test.ts +0 -274
  283. package/tests/invite-codes.test.ts +0 -367
  284. package/tests/invites-admin.test.ts +0 -252
  285. package/tests/moderation.test.ts +0 -280
  286. package/tests/moderator-auth.test.ts +0 -177
  287. package/tests/oauth.test.ts +0 -334
  288. package/tests/plc-operations.test.ts +0 -239
  289. package/tests/preferences.test.ts +0 -352
  290. package/tests/proxied/__snapshots__/admin.test.ts.snap +0 -516
  291. package/tests/proxied/__snapshots__/feedgen.test.ts.snap +0 -215
  292. package/tests/proxied/__snapshots__/views.test.ts.snap +0 -4456
  293. package/tests/proxied/admin.test.ts +0 -340
  294. package/tests/proxied/feedgen.test.ts +0 -66
  295. package/tests/proxied/notif.test.ts +0 -85
  296. package/tests/proxied/procedures.test.ts +0 -175
  297. package/tests/proxied/proxy-catchall.test.ts +0 -256
  298. package/tests/proxied/proxy-header.test.ts +0 -239
  299. package/tests/proxied/proxy-oauth-aud.test.ts +0 -182
  300. package/tests/proxied/read-after-write.test.ts +0 -382
  301. package/tests/proxied/views.test.ts +0 -608
  302. package/tests/races.test.ts +0 -89
  303. package/tests/rate-limits.test.ts +0 -70
  304. package/tests/recovery.test.ts +0 -180
  305. package/tests/seeds/basic.ts +0 -165
  306. package/tests/seeds/follows.ts +0 -57
  307. package/tests/seeds/likes.ts +0 -14
  308. package/tests/seeds/reposts.ts +0 -18
  309. package/tests/seeds/thread.ts +0 -40
  310. package/tests/seeds/users-bulk.ts +0 -246
  311. package/tests/seeds/users.ts +0 -67
  312. package/tests/sequencer.test.ts +0 -251
  313. package/tests/server.test.ts +0 -151
  314. package/tests/sync/invertible-ops.test.ts +0 -99
  315. package/tests/sync/list.test.ts +0 -43
  316. package/tests/sync/subscribe-repos.test.ts +0 -672
  317. package/tests/sync/sync.test.ts +0 -316
  318. package/tests/takedown-appeal.test.ts +0 -166
  319. package/tsconfig.build.json +0 -9
  320. package/tsconfig.build.tsbuildinfo +0 -1
  321. package/tsconfig.json +0 -7
  322. 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
- }