@atproto/pds 0.4.164 → 0.4.166

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 (296) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/account-manager/account-manager.js +2 -2
  3. package/dist/account-manager/account-manager.js.map +1 -1
  4. package/dist/account-manager/helpers/account-device.d.ts +4 -4
  5. package/dist/account-manager/helpers/account.d.ts +1 -1
  6. package/dist/account-manager/helpers/auth.d.ts +1 -1
  7. package/dist/account-manager/helpers/auth.d.ts.map +1 -1
  8. package/dist/account-manager/helpers/auth.js +8 -8
  9. package/dist/account-manager/helpers/auth.js.map +1 -1
  10. package/dist/account-manager/helpers/authorization-request.d.ts +1 -1
  11. package/dist/account-manager/helpers/authorization-request.d.ts.map +1 -1
  12. package/dist/account-manager/helpers/authorization-request.js +16 -8
  13. package/dist/account-manager/helpers/authorization-request.js.map +1 -1
  14. package/dist/account-manager/helpers/token.d.ts +65 -65
  15. package/dist/actor-store/preference/reader.d.ts +2 -2
  16. package/dist/actor-store/preference/reader.d.ts.map +1 -1
  17. package/dist/actor-store/preference/reader.js +2 -2
  18. package/dist/actor-store/preference/reader.js.map +1 -1
  19. package/dist/actor-store/preference/transactor.d.ts +2 -2
  20. package/dist/actor-store/preference/transactor.d.ts.map +1 -1
  21. package/dist/actor-store/preference/transactor.js +5 -5
  22. package/dist/actor-store/preference/transactor.js.map +1 -1
  23. package/dist/actor-store/preference/util.d.ts +4 -2
  24. package/dist/actor-store/preference/util.d.ts.map +1 -1
  25. package/dist/actor-store/preference/util.js +9 -8
  26. package/dist/actor-store/preference/util.js.map +1 -1
  27. package/dist/actor-store/record/reader.d.ts +2 -2
  28. package/dist/api/app/bsky/actor/getPreferences.d.ts.map +1 -1
  29. package/dist/api/app/bsky/actor/getPreferences.js +29 -7
  30. package/dist/api/app/bsky/actor/getPreferences.js.map +1 -1
  31. package/dist/api/app/bsky/actor/getProfile.d.ts.map +1 -1
  32. package/dist/api/app/bsky/actor/getProfile.js +9 -1
  33. package/dist/api/app/bsky/actor/getProfile.js.map +1 -1
  34. package/dist/api/app/bsky/actor/getProfiles.d.ts.map +1 -1
  35. package/dist/api/app/bsky/actor/getProfiles.js +9 -1
  36. package/dist/api/app/bsky/actor/getProfiles.js.map +1 -1
  37. package/dist/api/app/bsky/actor/putPreferences.d.ts.map +1 -1
  38. package/dist/api/app/bsky/actor/putPreferences.js +30 -8
  39. package/dist/api/app/bsky/actor/putPreferences.js.map +1 -1
  40. package/dist/api/app/bsky/feed/getActorLikes.d.ts.map +1 -1
  41. package/dist/api/app/bsky/feed/getActorLikes.js +9 -1
  42. package/dist/api/app/bsky/feed/getActorLikes.js.map +1 -1
  43. package/dist/api/app/bsky/feed/getAuthorFeed.d.ts.map +1 -1
  44. package/dist/api/app/bsky/feed/getAuthorFeed.js +9 -1
  45. package/dist/api/app/bsky/feed/getAuthorFeed.js.map +1 -1
  46. package/dist/api/app/bsky/feed/getFeed.d.ts.map +1 -1
  47. package/dist/api/app/bsky/feed/getFeed.js +8 -1
  48. package/dist/api/app/bsky/feed/getFeed.js.map +1 -1
  49. package/dist/api/app/bsky/feed/getPostThread.d.ts.map +1 -1
  50. package/dist/api/app/bsky/feed/getPostThread.js +8 -1
  51. package/dist/api/app/bsky/feed/getPostThread.js.map +1 -1
  52. package/dist/api/app/bsky/feed/getTimeline.d.ts.map +1 -1
  53. package/dist/api/app/bsky/feed/getTimeline.js +9 -1
  54. package/dist/api/app/bsky/feed/getTimeline.js.map +1 -1
  55. package/dist/api/app/bsky/notification/registerPush.d.ts.map +1 -1
  56. package/dist/api/app/bsky/notification/registerPush.js +16 -4
  57. package/dist/api/app/bsky/notification/registerPush.js.map +1 -1
  58. package/dist/api/com/atproto/identity/getRecommendedDidCredentials.d.ts.map +1 -1
  59. package/dist/api/com/atproto/identity/getRecommendedDidCredentials.js +5 -1
  60. package/dist/api/com/atproto/identity/getRecommendedDidCredentials.js.map +1 -1
  61. package/dist/api/com/atproto/identity/requestPlcOperationSignature.d.ts.map +1 -1
  62. package/dist/api/com/atproto/identity/requestPlcOperationSignature.js +9 -2
  63. package/dist/api/com/atproto/identity/requestPlcOperationSignature.js.map +1 -1
  64. package/dist/api/com/atproto/identity/signPlcOperation.d.ts.map +1 -1
  65. package/dist/api/com/atproto/identity/signPlcOperation.js +9 -1
  66. package/dist/api/com/atproto/identity/signPlcOperation.js.map +1 -1
  67. package/dist/api/com/atproto/identity/submitPlcOperation.d.ts.map +1 -1
  68. package/dist/api/com/atproto/identity/submitPlcOperation.js +5 -1
  69. package/dist/api/com/atproto/identity/submitPlcOperation.js.map +1 -1
  70. package/dist/api/com/atproto/identity/updateHandle.d.ts.map +1 -1
  71. package/dist/api/com/atproto/identity/updateHandle.js +6 -1
  72. package/dist/api/com/atproto/identity/updateHandle.js.map +1 -1
  73. package/dist/api/com/atproto/moderation/createReport.d.ts.map +1 -1
  74. package/dist/api/com/atproto/moderation/createReport.js +8 -3
  75. package/dist/api/com/atproto/moderation/createReport.js.map +1 -1
  76. package/dist/api/com/atproto/repo/applyWrites.d.ts.map +1 -1
  77. package/dist/api/com/atproto/repo/applyWrites.js +25 -19
  78. package/dist/api/com/atproto/repo/applyWrites.js.map +1 -1
  79. package/dist/api/com/atproto/repo/createRecord.d.ts.map +1 -1
  80. package/dist/api/com/atproto/repo/createRecord.js +10 -1
  81. package/dist/api/com/atproto/repo/createRecord.js.map +1 -1
  82. package/dist/api/com/atproto/repo/deleteRecord.d.ts.map +1 -1
  83. package/dist/api/com/atproto/repo/deleteRecord.js +12 -1
  84. package/dist/api/com/atproto/repo/deleteRecord.js.map +1 -1
  85. package/dist/api/com/atproto/repo/importRepo.d.ts.map +1 -1
  86. package/dist/api/com/atproto/repo/importRepo.js +7 -2
  87. package/dist/api/com/atproto/repo/importRepo.js.map +1 -1
  88. package/dist/api/com/atproto/repo/listMissingBlobs.d.ts.map +1 -1
  89. package/dist/api/com/atproto/repo/listMissingBlobs.js +6 -2
  90. package/dist/api/com/atproto/repo/listMissingBlobs.js.map +1 -1
  91. package/dist/api/com/atproto/repo/putRecord.d.ts.map +1 -1
  92. package/dist/api/com/atproto/repo/putRecord.js +17 -11
  93. package/dist/api/com/atproto/repo/putRecord.js.map +1 -1
  94. package/dist/api/com/atproto/repo/uploadBlob.d.ts.map +1 -1
  95. package/dist/api/com/atproto/repo/uploadBlob.js +5 -1
  96. package/dist/api/com/atproto/repo/uploadBlob.js.map +1 -1
  97. package/dist/api/com/atproto/server/activateAccount.d.ts.map +1 -1
  98. package/dist/api/com/atproto/server/activateAccount.js +7 -1
  99. package/dist/api/com/atproto/server/activateAccount.js.map +1 -1
  100. package/dist/api/com/atproto/server/checkAccountStatus.d.ts.map +1 -1
  101. package/dist/api/com/atproto/server/checkAccountStatus.js +5 -1
  102. package/dist/api/com/atproto/server/checkAccountStatus.js.map +1 -1
  103. package/dist/api/com/atproto/server/confirmEmail.d.ts.map +1 -1
  104. package/dist/api/com/atproto/server/confirmEmail.js +6 -1
  105. package/dist/api/com/atproto/server/confirmEmail.js.map +1 -1
  106. package/dist/api/com/atproto/server/createAppPassword.d.ts.map +1 -1
  107. package/dist/api/com/atproto/server/createAppPassword.js +7 -1
  108. package/dist/api/com/atproto/server/createAppPassword.js.map +1 -1
  109. package/dist/api/com/atproto/server/deactivateAccount.d.ts.map +1 -1
  110. package/dist/api/com/atproto/server/deactivateAccount.js +9 -2
  111. package/dist/api/com/atproto/server/deactivateAccount.js.map +1 -1
  112. package/dist/api/com/atproto/server/deleteSession.d.ts.map +1 -1
  113. package/dist/api/com/atproto/server/deleteSession.js +3 -1
  114. package/dist/api/com/atproto/server/deleteSession.js.map +1 -1
  115. package/dist/api/com/atproto/server/getAccountInviteCodes.d.ts.map +1 -1
  116. package/dist/api/com/atproto/server/getAccountInviteCodes.js +8 -1
  117. package/dist/api/com/atproto/server/getAccountInviteCodes.js.map +1 -1
  118. package/dist/api/com/atproto/server/getServiceAuth.d.ts.map +1 -1
  119. package/dist/api/com/atproto/server/getServiceAuth.js +24 -13
  120. package/dist/api/com/atproto/server/getServiceAuth.js.map +1 -1
  121. package/dist/api/com/atproto/server/getSession.d.ts.map +1 -1
  122. package/dist/api/com/atproto/server/getSession.js +12 -19
  123. package/dist/api/com/atproto/server/getSession.js.map +1 -1
  124. package/dist/api/com/atproto/server/listAppPasswords.d.ts.map +1 -1
  125. package/dist/api/com/atproto/server/listAppPasswords.js +6 -1
  126. package/dist/api/com/atproto/server/listAppPasswords.js.map +1 -1
  127. package/dist/api/com/atproto/server/refreshSession.js +1 -1
  128. package/dist/api/com/atproto/server/refreshSession.js.map +1 -1
  129. package/dist/api/com/atproto/server/requestAccountDelete.d.ts.map +1 -1
  130. package/dist/api/com/atproto/server/requestAccountDelete.js +8 -1
  131. package/dist/api/com/atproto/server/requestAccountDelete.js.map +1 -1
  132. package/dist/api/com/atproto/server/requestEmailConfirmation.d.ts.map +1 -1
  133. package/dist/api/com/atproto/server/requestEmailConfirmation.js +6 -1
  134. package/dist/api/com/atproto/server/requestEmailConfirmation.js.map +1 -1
  135. package/dist/api/com/atproto/server/requestEmailUpdate.d.ts.map +1 -1
  136. package/dist/api/com/atproto/server/requestEmailUpdate.js +6 -1
  137. package/dist/api/com/atproto/server/requestEmailUpdate.js.map +1 -1
  138. package/dist/api/com/atproto/server/revokeAppPassword.d.ts.map +1 -1
  139. package/dist/api/com/atproto/server/revokeAppPassword.js +6 -1
  140. package/dist/api/com/atproto/server/revokeAppPassword.js.map +1 -1
  141. package/dist/api/com/atproto/server/updateEmail.d.ts.map +1 -1
  142. package/dist/api/com/atproto/server/updateEmail.js +8 -1
  143. package/dist/api/com/atproto/server/updateEmail.js.map +1 -1
  144. package/dist/api/com/atproto/sync/deprecated/getCheckout.d.ts.map +1 -1
  145. package/dist/api/com/atproto/sync/deprecated/getCheckout.js +7 -2
  146. package/dist/api/com/atproto/sync/deprecated/getCheckout.js.map +1 -1
  147. package/dist/api/com/atproto/sync/deprecated/getHead.d.ts.map +1 -1
  148. package/dist/api/com/atproto/sync/deprecated/getHead.js +7 -2
  149. package/dist/api/com/atproto/sync/deprecated/getHead.js.map +1 -1
  150. package/dist/api/com/atproto/sync/getBlob.d.ts.map +1 -1
  151. package/dist/api/com/atproto/sync/getBlob.js +7 -3
  152. package/dist/api/com/atproto/sync/getBlob.js.map +1 -1
  153. package/dist/api/com/atproto/sync/getBlocks.d.ts.map +1 -1
  154. package/dist/api/com/atproto/sync/getBlocks.js +7 -2
  155. package/dist/api/com/atproto/sync/getBlocks.js.map +1 -1
  156. package/dist/api/com/atproto/sync/getLatestCommit.d.ts.map +1 -1
  157. package/dist/api/com/atproto/sync/getLatestCommit.js +7 -2
  158. package/dist/api/com/atproto/sync/getLatestCommit.js.map +1 -1
  159. package/dist/api/com/atproto/sync/getRecord.d.ts.map +1 -1
  160. package/dist/api/com/atproto/sync/getRecord.js +7 -2
  161. package/dist/api/com/atproto/sync/getRecord.js.map +1 -1
  162. package/dist/api/com/atproto/sync/getRepo.d.ts.map +1 -1
  163. package/dist/api/com/atproto/sync/getRepo.js +7 -3
  164. package/dist/api/com/atproto/sync/getRepo.js.map +1 -1
  165. package/dist/api/com/atproto/sync/listBlobs.d.ts.map +1 -1
  166. package/dist/api/com/atproto/sync/listBlobs.js +7 -3
  167. package/dist/api/com/atproto/sync/listBlobs.js.map +1 -1
  168. package/dist/api/com/atproto/temp/checkSignupQueue.d.ts.map +1 -1
  169. package/dist/api/com/atproto/temp/checkSignupQueue.js +7 -3
  170. package/dist/api/com/atproto/temp/checkSignupQueue.js.map +1 -1
  171. package/dist/auth-output.d.ts +45 -0
  172. package/dist/auth-output.d.ts.map +1 -0
  173. package/dist/auth-output.js +3 -0
  174. package/dist/auth-output.js.map +1 -0
  175. package/dist/auth-scope.d.ts +16 -0
  176. package/dist/auth-scope.d.ts.map +1 -0
  177. package/dist/auth-scope.js +40 -0
  178. package/dist/auth-scope.js.map +1 -0
  179. package/dist/auth-verifier.d.ts +50 -115
  180. package/dist/auth-verifier.d.ts.map +1 -1
  181. package/dist/auth-verifier.js +275 -366
  182. package/dist/auth-verifier.js.map +1 -1
  183. package/dist/config/config.d.ts +2 -1
  184. package/dist/config/config.d.ts.map +1 -1
  185. package/dist/config/config.js +2 -1
  186. package/dist/config/config.js.map +1 -1
  187. package/dist/config/env.d.ts +1 -0
  188. package/dist/config/env.d.ts.map +1 -1
  189. package/dist/config/env.js +3 -1
  190. package/dist/config/env.js.map +1 -1
  191. package/dist/context.d.ts.map +1 -1
  192. package/dist/context.js +5 -5
  193. package/dist/context.js.map +1 -1
  194. package/dist/lexicon/index.d.ts +234 -230
  195. package/dist/lexicon/index.d.ts.map +1 -1
  196. package/dist/lexicon/index.js +682 -674
  197. package/dist/lexicon/index.js.map +1 -1
  198. package/dist/lexicon/lexicons.d.ts +17994 -17706
  199. package/dist/lexicon/lexicons.d.ts.map +1 -1
  200. package/dist/lexicon/lexicons.js +9126 -8980
  201. package/dist/lexicon/lexicons.js.map +1 -1
  202. package/dist/lexicon/types/app/bsky/graph/getLists.d.ts +2 -0
  203. package/dist/lexicon/types/app/bsky/graph/getLists.d.ts.map +1 -1
  204. package/dist/lexicon/types/app/bsky/graph/getListsWithMembership.d.ts +40 -0
  205. package/dist/lexicon/types/app/bsky/graph/getListsWithMembership.d.ts.map +1 -0
  206. package/dist/lexicon/types/app/bsky/graph/getListsWithMembership.js +16 -0
  207. package/dist/lexicon/types/app/bsky/graph/getListsWithMembership.js.map +1 -0
  208. package/dist/lexicon/types/app/bsky/graph/getStarterPacksWithMembership.d.ts +38 -0
  209. package/dist/lexicon/types/app/bsky/graph/getStarterPacksWithMembership.d.ts.map +1 -0
  210. package/dist/lexicon/types/app/bsky/graph/getStarterPacksWithMembership.js +16 -0
  211. package/dist/lexicon/types/app/bsky/graph/getStarterPacksWithMembership.js.map +1 -0
  212. package/dist/pipethrough.d.ts +5 -3
  213. package/dist/pipethrough.d.ts.map +1 -1
  214. package/dist/pipethrough.js +42 -15
  215. package/dist/pipethrough.js.map +1 -1
  216. package/dist/sequencer/events.d.ts +13 -13
  217. package/dist/util/http.d.ts +7 -0
  218. package/dist/util/http.d.ts.map +1 -0
  219. package/dist/util/http.js +31 -0
  220. package/dist/util/http.js.map +1 -0
  221. package/dist/util/types.d.ts +5 -0
  222. package/dist/util/types.d.ts.map +1 -0
  223. package/dist/util/types.js +3 -0
  224. package/dist/util/types.js.map +1 -0
  225. package/package.json +7 -6
  226. package/src/account-manager/account-manager.ts +1 -1
  227. package/src/account-manager/helpers/auth.ts +1 -1
  228. package/src/account-manager/helpers/authorization-request.ts +8 -4
  229. package/src/actor-store/preference/reader.ts +3 -4
  230. package/src/actor-store/preference/transactor.ts +6 -7
  231. package/src/actor-store/preference/util.ts +15 -5
  232. package/src/api/app/bsky/actor/getPreferences.ts +33 -8
  233. package/src/api/app/bsky/actor/getProfile.ts +9 -1
  234. package/src/api/app/bsky/actor/getProfiles.ts +9 -1
  235. package/src/api/app/bsky/actor/putPreferences.ts +35 -12
  236. package/src/api/app/bsky/feed/getActorLikes.ts +9 -1
  237. package/src/api/app/bsky/feed/getAuthorFeed.ts +9 -1
  238. package/src/api/app/bsky/feed/getFeed.ts +9 -2
  239. package/src/api/app/bsky/feed/getPostThread.ts +8 -1
  240. package/src/api/app/bsky/feed/getTimeline.ts +9 -1
  241. package/src/api/app/bsky/notification/registerPush.ts +16 -5
  242. package/src/api/com/atproto/identity/getRecommendedDidCredentials.ts +5 -1
  243. package/src/api/com/atproto/identity/requestPlcOperationSignature.ts +9 -2
  244. package/src/api/com/atproto/identity/signPlcOperation.ts +9 -1
  245. package/src/api/com/atproto/identity/submitPlcOperation.ts +5 -1
  246. package/src/api/com/atproto/identity/updateHandle.ts +6 -1
  247. package/src/api/com/atproto/moderation/createReport.ts +8 -3
  248. package/src/api/com/atproto/repo/applyWrites.ts +28 -20
  249. package/src/api/com/atproto/repo/createRecord.ts +12 -1
  250. package/src/api/com/atproto/repo/deleteRecord.ts +14 -1
  251. package/src/api/com/atproto/repo/importRepo.ts +9 -2
  252. package/src/api/com/atproto/repo/listMissingBlobs.ts +7 -2
  253. package/src/api/com/atproto/repo/putRecord.ts +18 -10
  254. package/src/api/com/atproto/repo/uploadBlob.ts +6 -2
  255. package/src/api/com/atproto/server/activateAccount.ts +10 -2
  256. package/src/api/com/atproto/server/checkAccountStatus.ts +5 -1
  257. package/src/api/com/atproto/server/confirmEmail.ts +6 -1
  258. package/src/api/com/atproto/server/createAppPassword.ts +9 -1
  259. package/src/api/com/atproto/server/deactivateAccount.ts +11 -2
  260. package/src/api/com/atproto/server/deleteSession.ts +3 -1
  261. package/src/api/com/atproto/server/getAccountInviteCodes.ts +11 -2
  262. package/src/api/com/atproto/server/getServiceAuth.ts +37 -18
  263. package/src/api/com/atproto/server/getSession.ts +20 -27
  264. package/src/api/com/atproto/server/listAppPasswords.ts +8 -1
  265. package/src/api/com/atproto/server/refreshSession.ts +1 -1
  266. package/src/api/com/atproto/server/requestAccountDelete.ts +11 -2
  267. package/src/api/com/atproto/server/requestEmailConfirmation.ts +6 -1
  268. package/src/api/com/atproto/server/requestEmailUpdate.ts +6 -1
  269. package/src/api/com/atproto/server/revokeAppPassword.ts +8 -1
  270. package/src/api/com/atproto/server/updateEmail.ts +11 -2
  271. package/src/api/com/atproto/sync/deprecated/getCheckout.ts +7 -6
  272. package/src/api/com/atproto/sync/deprecated/getHead.ts +7 -6
  273. package/src/api/com/atproto/sync/getBlob.ts +7 -7
  274. package/src/api/com/atproto/sync/getBlocks.ts +7 -6
  275. package/src/api/com/atproto/sync/getLatestCommit.ts +7 -6
  276. package/src/api/com/atproto/sync/getRecord.ts +7 -6
  277. package/src/api/com/atproto/sync/getRepo.ts +7 -7
  278. package/src/api/com/atproto/sync/listBlobs.ts +7 -7
  279. package/src/api/com/atproto/temp/checkSignupQueue.ts +8 -2
  280. package/src/auth-output.ts +51 -0
  281. package/src/auth-scope.ts +40 -0
  282. package/src/auth-verifier.ts +404 -520
  283. package/src/config/config.ts +7 -7
  284. package/src/config/env.ts +5 -1
  285. package/src/context.ts +6 -5
  286. package/src/lexicon/index.ts +1247 -1221
  287. package/src/lexicon/lexicons.ts +9494 -9341
  288. package/src/lexicon/types/app/bsky/graph/getLists.ts +2 -0
  289. package/src/lexicon/types/app/bsky/graph/getListsWithMembership.ts +63 -0
  290. package/src/lexicon/types/app/bsky/graph/getStarterPacksWithMembership.ts +65 -0
  291. package/src/pipethrough.ts +61 -18
  292. package/src/util/http.ts +31 -0
  293. package/src/util/types.ts +7 -0
  294. package/tests/oauth.test.ts +11 -37
  295. package/tests/preferences.test.ts +7 -3
  296. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1,13 +1,12 @@
1
- import { AuthScope } from '../../auth-verifier'
2
1
  import { ActorDb } from '../db'
3
- import { prefInScope } from './util'
2
+ import { PrefAllowedOptions, prefAllowed } from './util'
4
3
 
5
4
  export class PreferenceReader {
6
5
  constructor(public db: ActorDb) {}
7
6
 
8
7
  async getPreferences(
9
8
  namespace: string,
10
- scope: AuthScope,
9
+ opts: PrefAllowedOptions,
11
10
  ): Promise<AccountPreference[]> {
12
11
  const prefsRes = await this.db.db
13
12
  .selectFrom('account_pref')
@@ -16,7 +15,7 @@ export class PreferenceReader {
16
15
  .execute()
17
16
  return prefsRes
18
17
  .filter((pref) => !namespace || prefMatchNamespace(namespace, pref.name))
19
- .filter((pref) => prefInScope(scope, pref.name))
18
+ .filter((pref) => prefAllowed(pref.name, opts))
20
19
  .map((pref) => JSON.parse(pref.valueJson) as AccountPreference)
21
20
  }
22
21
  }
@@ -1,17 +1,16 @@
1
1
  import { InvalidRequestError } from '@atproto/xrpc-server'
2
- import { AuthScope } from '../../auth-verifier'
3
2
  import {
4
3
  AccountPreference,
5
4
  PreferenceReader,
6
5
  prefMatchNamespace,
7
6
  } from './reader'
8
- import { prefInScope } from './util'
7
+ import { PrefAllowedOptions, prefAllowed } from './util'
9
8
 
10
9
  export class PreferenceTransactor extends PreferenceReader {
11
10
  async putPreferences(
12
11
  values: AccountPreference[],
13
12
  namespace: string,
14
- scope: AuthScope,
13
+ opts: PrefAllowedOptions,
15
14
  ): Promise<void> {
16
15
  this.db.assertTransaction()
17
16
  if (!values.every((value) => prefMatchNamespace(namespace, value.$type))) {
@@ -19,10 +18,10 @@ export class PreferenceTransactor extends PreferenceReader {
19
18
  `Some preferences are not in the ${namespace} namespace`,
20
19
  )
21
20
  }
22
- const notInScope = values.filter((val) => !prefInScope(scope, val.$type))
23
- if (notInScope.length > 0) {
21
+ const forbiddenPrefs = values.filter((val) => !prefAllowed(val.$type, opts))
22
+ if (forbiddenPrefs.length > 0) {
24
23
  throw new InvalidRequestError(
25
- `Do not have authorization to set preferences: ${notInScope.join(', ')}`,
24
+ `Do not have authorization to set preferences: ${forbiddenPrefs.map((p) => p.$type).join(', ')}`,
26
25
  )
27
26
  }
28
27
  // get all current prefs for user and prep new pref rows
@@ -38,7 +37,7 @@ export class PreferenceTransactor extends PreferenceReader {
38
37
  })
39
38
  const allPrefIdsInNamespace = allPrefs
40
39
  .filter((pref) => prefMatchNamespace(namespace, pref.name))
41
- .filter((pref) => prefInScope(scope, pref.name))
40
+ .filter((pref) => prefAllowed(pref.name, opts))
42
41
  .map((pref) => pref.id)
43
42
  // replace all prefs in given namespace
44
43
  if (allPrefIdsInNamespace.length) {
@@ -1,8 +1,18 @@
1
- import { AuthScope } from '../../auth-verifier'
1
+ const FULL_ACCESS_ONLY_PREFS = new Set([
2
+ 'app.bsky.actor.defs#personalDetailsPref',
3
+ ])
2
4
 
3
- const FULL_ACCESS_ONLY_PREFS = ['app.bsky.actor.defs#personalDetailsPref']
5
+ export type PrefAllowedOptions = {
6
+ hasAccessFull?: boolean
7
+ }
8
+
9
+ export function prefAllowed(
10
+ prefType: string,
11
+ options?: PrefAllowedOptions,
12
+ ): boolean {
13
+ if (options?.hasAccessFull === true) {
14
+ return true
15
+ }
4
16
 
5
- export const prefInScope = (scope: AuthScope, prefType: string) => {
6
- if (scope === AuthScope.Access) return true
7
- return !FULL_ACCESS_ONLY_PREFS.includes(prefType)
17
+ return !FULL_ACCESS_ONLY_PREFS.has(prefType)
8
18
  }
@@ -1,19 +1,44 @@
1
- import { AuthScope } from '../../../../auth-verifier'
1
+ import { AuthScope, isAccessFull } from '../../../../auth-scope'
2
2
  import { AppContext } from '../../../../context'
3
3
  import { Server } from '../../../../lexicon'
4
+ import { ids } from '../../../../lexicon/lexicons'
5
+ import { computeProxyTo, pipethrough } from '../../../../pipethrough'
4
6
 
5
7
  export default function (server: Server, ctx: AppContext) {
6
- if (!ctx.bskyAppView) return
8
+ const { bskyAppView } = ctx
9
+ if (!bskyAppView) return
7
10
 
8
11
  server.app.bsky.actor.getPreferences({
9
- auth: ctx.authVerifier.accessStandard({
12
+ auth: ctx.authVerifier.authorization({
10
13
  additional: [AuthScope.Takendown],
14
+ authorize: (permissions, { req }) => {
15
+ const lxm = ids.AppBskyActorGetPreferences
16
+ const aud = computeProxyTo(ctx, req, lxm)
17
+ permissions.assertRpc({ aud, lxm })
18
+ },
11
19
  }),
12
- handler: async ({ auth }) => {
13
- const requester = auth.credentials.did
14
- const preferences = await ctx.actorStore.read(requester, (store) =>
15
- store.pref.getPreferences('app.bsky', auth.credentials.scope),
16
- )
20
+ handler: async ({ auth, req }) => {
21
+ const { did } = auth.credentials
22
+
23
+ // If the request has a proxy header different from the bsky app view,
24
+ // we need to proxy the request to the requested app view.
25
+ // @TODO This behavior should not be implemented as part of the XRPC framework
26
+ const lxm = ids.AppBskyActorGetPreferences
27
+ const aud = computeProxyTo(ctx, req, lxm)
28
+ if (aud !== `${bskyAppView.did}#bsky_appview`) {
29
+ return pipethrough(ctx, req, { iss: did, aud, lxm })
30
+ }
31
+
32
+ const hasAccessFull =
33
+ auth.credentials.type === 'access' &&
34
+ isAccessFull(auth.credentials.scope)
35
+
36
+ const preferences = await ctx.actorStore.read(did, (store) => {
37
+ return store.pref.getPreferences('app.bsky', {
38
+ hasAccessFull,
39
+ })
40
+ })
41
+
17
42
  return {
18
43
  encoding: 'application/json',
19
44
  body: { preferences },
@@ -1,6 +1,8 @@
1
1
  import { AppContext } from '../../../../context'
2
2
  import { Server } from '../../../../lexicon'
3
+ import { ids } from '../../../../lexicon/lexicons'
3
4
  import { OutputSchema } from '../../../../lexicon/types/app/bsky/actor/getProfile'
5
+ import { computeProxyTo } from '../../../../pipethrough'
4
6
  import {
5
7
  LocalRecords,
6
8
  LocalViewer,
@@ -11,7 +13,13 @@ export default function (server: Server, ctx: AppContext) {
11
13
  if (!ctx.bskyAppView) return
12
14
 
13
15
  server.app.bsky.actor.getProfile({
14
- auth: ctx.authVerifier.accessStandard(),
16
+ auth: ctx.authVerifier.authorization({
17
+ authorize: (permissions, { req }) => {
18
+ const lxm = ids.AppBskyActorGetProfile
19
+ const aud = computeProxyTo(ctx, req, lxm)
20
+ permissions.assertRpc({ aud, lxm })
21
+ },
22
+ }),
15
23
  handler: async (reqCtx) => {
16
24
  return pipethroughReadAfterWrite(ctx, reqCtx, getProfileMunge)
17
25
  },
@@ -1,6 +1,8 @@
1
1
  import { AppContext } from '../../../../context'
2
2
  import { Server } from '../../../../lexicon'
3
+ import { ids } from '../../../../lexicon/lexicons'
3
4
  import { OutputSchema } from '../../../../lexicon/types/app/bsky/actor/getProfiles'
5
+ import { computeProxyTo } from '../../../../pipethrough'
4
6
  import {
5
7
  LocalRecords,
6
8
  LocalViewer,
@@ -11,7 +13,13 @@ export default function (server: Server, ctx: AppContext) {
11
13
  if (!ctx.bskyAppView) return
12
14
 
13
15
  server.app.bsky.actor.getProfiles({
14
- auth: ctx.authVerifier.accessStandard(),
16
+ auth: ctx.authVerifier.authorization({
17
+ authorize: (permissions, { req }) => {
18
+ const lxm = ids.AppBskyActorGetProfiles
19
+ const aud = computeProxyTo(ctx, req, lxm)
20
+ permissions.assertRpc({ aud, lxm })
21
+ },
22
+ }),
15
23
  handler: async (reqCtx) => {
16
24
  return pipethroughReadAfterWrite(ctx, reqCtx, getProfilesMunge)
17
25
  },
@@ -1,30 +1,53 @@
1
1
  import { InvalidRequestError } from '@atproto/xrpc-server'
2
2
  import { AccountPreference } from '../../../../actor-store/preference/reader'
3
+ import { isAccessFull } from '../../../../auth-scope'
3
4
  import { AppContext } from '../../../../context'
4
5
  import { Server } from '../../../../lexicon'
6
+ import { ids } from '../../../../lexicon/lexicons'
7
+ import { computeProxyTo, pipethrough } from '../../../../pipethrough'
5
8
 
6
9
  export default function (server: Server, ctx: AppContext) {
7
- if (!ctx.bskyAppView) return
10
+ const { bskyAppView } = ctx
11
+ if (!bskyAppView) return
8
12
 
9
13
  server.app.bsky.actor.putPreferences({
10
- auth: ctx.authVerifier.accessStandard({ checkTakedown: true }),
11
- handler: async ({ auth, input }) => {
12
- const { preferences } = input.body
13
- const requester = auth.credentials.did
14
+ auth: ctx.authVerifier.authorization({
15
+ checkTakedown: true,
16
+ authorize: (permissions, { req }) => {
17
+ const lxm = ids.AppBskyActorPutPreferences
18
+ const aud = computeProxyTo(ctx, req, lxm)
19
+ permissions.assertRpc({ aud, lxm })
20
+ },
21
+ }),
22
+ handler: async ({ req, auth, input }) => {
23
+ const { did } = auth.credentials
24
+
25
+ // If the request has a proxy header different from the bsky app view,
26
+ // we need to proxy the request to the requested app view.
27
+ // @TODO This behavior should not be implemented as part of the XRPC framework
28
+ const lxm = ids.AppBskyActorPutPreferences
29
+ const aud = computeProxyTo(ctx, req, lxm)
30
+ if (aud !== `${bskyAppView.did}#bsky_appview`) {
31
+ return pipethrough(ctx, req, { iss: did, aud, lxm })
32
+ }
33
+
14
34
  const checkedPreferences: AccountPreference[] = []
15
- for (const pref of preferences) {
35
+ for (const pref of input.body.preferences) {
16
36
  if (typeof pref.$type === 'string') {
17
37
  checkedPreferences.push(pref as AccountPreference)
18
38
  } else {
19
39
  throw new InvalidRequestError('Preference is missing a $type')
20
40
  }
21
41
  }
22
- await ctx.actorStore.transact(requester, async (actorTxn) => {
23
- await actorTxn.pref.putPreferences(
24
- checkedPreferences,
25
- 'app.bsky',
26
- auth.credentials.scope,
27
- )
42
+
43
+ const hasAccessFull =
44
+ auth.credentials.type === 'access' &&
45
+ isAccessFull(auth.credentials.scope)
46
+
47
+ await ctx.actorStore.transact(did, async (actorTxn) => {
48
+ await actorTxn.pref.putPreferences(checkedPreferences, 'app.bsky', {
49
+ hasAccessFull,
50
+ })
28
51
  })
29
52
  },
30
53
  })
@@ -1,6 +1,8 @@
1
1
  import { AppContext } from '../../../../context'
2
2
  import { Server } from '../../../../lexicon'
3
+ import { ids } from '../../../../lexicon/lexicons'
3
4
  import { OutputSchema } from '../../../../lexicon/types/app/bsky/feed/getActorLikes'
5
+ import { computeProxyTo } from '../../../../pipethrough'
4
6
  import {
5
7
  LocalRecords,
6
8
  LocalViewer,
@@ -11,7 +13,13 @@ export default function (server: Server, ctx: AppContext) {
11
13
  if (!ctx.bskyAppView) return
12
14
 
13
15
  server.app.bsky.feed.getActorLikes({
14
- auth: ctx.authVerifier.accessStandard(),
16
+ auth: ctx.authVerifier.authorization({
17
+ authorize: (permissions, { req }) => {
18
+ const lxm = ids.AppBskyFeedGetActorLikes
19
+ const aud = computeProxyTo(ctx, req, lxm)
20
+ permissions.assertRpc({ aud, lxm })
21
+ },
22
+ }),
15
23
  handler: async (reqCtx) => {
16
24
  return pipethroughReadAfterWrite(ctx, reqCtx, getAuthorMunge)
17
25
  },
@@ -1,7 +1,9 @@
1
1
  import { AppContext } from '../../../../context'
2
2
  import { Server } from '../../../../lexicon'
3
+ import { ids } from '../../../../lexicon/lexicons'
3
4
  import { isReasonRepost } from '../../../../lexicon/types/app/bsky/feed/defs'
4
5
  import { OutputSchema } from '../../../../lexicon/types/app/bsky/feed/getAuthorFeed'
6
+ import { computeProxyTo } from '../../../../pipethrough'
5
7
  import {
6
8
  LocalRecords,
7
9
  LocalViewer,
@@ -12,7 +14,13 @@ export default function (server: Server, ctx: AppContext) {
12
14
  if (!ctx.bskyAppView) return
13
15
 
14
16
  server.app.bsky.feed.getAuthorFeed({
15
- auth: ctx.authVerifier.accessStandard(),
17
+ auth: ctx.authVerifier.authorization({
18
+ authorize: (permissions, { req }) => {
19
+ const lxm = ids.AppBskyFeedGetAuthorFeed
20
+ const aud = computeProxyTo(ctx, req, lxm)
21
+ permissions.assertRpc({ aud, lxm })
22
+ },
23
+ }),
16
24
  handler: async (reqCtx) => {
17
25
  return pipethroughReadAfterWrite(ctx, reqCtx, getAuthorMunge)
18
26
  },
@@ -3,14 +3,21 @@ import { AtUri } from '@atproto/syntax'
3
3
  import { AppContext } from '../../../../context'
4
4
  import { Server } from '../../../../lexicon'
5
5
  import { ids } from '../../../../lexicon/lexicons'
6
- import { pipethrough } from '../../../../pipethrough'
6
+ import { computeProxyTo, pipethrough } from '../../../../pipethrough'
7
7
 
8
8
  export default function (server: Server, ctx: AppContext) {
9
9
  const { bskyAppView } = ctx
10
10
  if (!bskyAppView) return
11
11
 
12
12
  server.app.bsky.feed.getFeed({
13
- auth: ctx.authVerifier.accessStandard(),
13
+ auth: ctx.authVerifier.authorization({
14
+ authorize: (permissions, { req }) => {
15
+ const lxm = ids.AppBskyFeedGetFeed
16
+ const aud = computeProxyTo(ctx, req, lxm)
17
+ permissions.assertRpc({ aud, lxm })
18
+ permissions.assertRpc({ aud, lxm: ids.AppBskyFeedGetFeedSkeleton })
19
+ },
20
+ }),
14
21
  handler: async ({ params, auth, req }) => {
15
22
  const requester = auth.credentials.did
16
23
 
@@ -14,6 +14,7 @@ import {
14
14
  } from '../../../../lexicon/types/app/bsky/feed/getPostThread'
15
15
  import { Record as PostRecord } from '../../../../lexicon/types/app/bsky/feed/post'
16
16
  import { $Typed } from '../../../../lexicon/util'
17
+ import { computeProxyTo } from '../../../../pipethrough'
17
18
  import {
18
19
  LocalRecords,
19
20
  LocalViewer,
@@ -28,7 +29,13 @@ export default function (server: Server, ctx: AppContext) {
28
29
  if (!ctx.bskyAppView) return
29
30
 
30
31
  server.app.bsky.feed.getPostThread({
31
- auth: ctx.authVerifier.accessStandard(),
32
+ auth: ctx.authVerifier.authorization({
33
+ authorize: (permissions, { req }) => {
34
+ const lxm = ids.AppBskyFeedGetPostThread
35
+ const aud = computeProxyTo(ctx, req, lxm)
36
+ permissions.assertRpc({ aud, lxm })
37
+ },
38
+ }),
32
39
  handler: async (reqCtx) => {
33
40
  try {
34
41
  return await pipethroughReadAfterWrite(ctx, reqCtx, getPostThreadMunge)
@@ -1,6 +1,8 @@
1
1
  import { AppContext } from '../../../../context'
2
2
  import { Server } from '../../../../lexicon'
3
+ import { ids } from '../../../../lexicon/lexicons'
3
4
  import { OutputSchema } from '../../../../lexicon/types/app/bsky/feed/getTimeline'
5
+ import { computeProxyTo } from '../../../../pipethrough'
4
6
  import {
5
7
  LocalRecords,
6
8
  LocalViewer,
@@ -11,7 +13,13 @@ export default function (server: Server, ctx: AppContext) {
11
13
  if (!ctx.bskyAppView) return
12
14
 
13
15
  server.app.bsky.feed.getTimeline({
14
- auth: ctx.authVerifier.accessStandard(),
16
+ auth: ctx.authVerifier.authorization({
17
+ authorize: (permissions, { req }) => {
18
+ const lxm = ids.AppBskyFeedGetTimeline
19
+ const aud = computeProxyTo(ctx, req, lxm)
20
+ permissions.assertRpc({ aud, lxm })
21
+ },
22
+ }),
15
23
  handler: async (reqCtx) => {
16
24
  return pipethroughReadAfterWrite(ctx, reqCtx, getTimelineMunge)
17
25
  },
@@ -1,7 +1,7 @@
1
1
  import { AtpAgent } from '@atproto/api'
2
2
  import { getNotif } from '@atproto/identity'
3
3
  import { InvalidRequestError } from '@atproto/xrpc-server'
4
- import { AuthScope } from '../../../../auth-verifier'
4
+ import { AuthScope } from '../../../../auth-scope'
5
5
  import { AppContext } from '../../../../context'
6
6
  import { Server } from '../../../../lexicon'
7
7
  import { ids } from '../../../../lexicon/lexicons'
@@ -12,14 +12,25 @@ export default function (server: Server, ctx: AppContext) {
12
12
  if (!bskyAppView) return
13
13
 
14
14
  server.app.bsky.notification.registerPush({
15
- auth: ctx.authVerifier.accessStandard({
15
+ auth: ctx.authVerifier.authorization({
16
16
  additional: [AuthScope.SignupQueued],
17
+ authorize: () => {
18
+ // @NOTE this endpoint predates generic service proxying but we want to
19
+ // map the permission to the "RPC" scope for consistency. However, since
20
+ // the service info is only available in the request body, we can't
21
+ // assert permissions here.
22
+ },
17
23
  }),
18
24
  handler: async ({ auth, input }) => {
19
25
  const { serviceDid } = input.body
20
- const {
21
- credentials: { did },
22
- } = auth
26
+ const { did } = auth.credentials
27
+
28
+ if (auth.credentials.type === 'oauth') {
29
+ auth.credentials.permissions.assertRpc({
30
+ aud: `${serviceDid}#bsky_notif`,
31
+ lxm: ids.AppBskyNotificationRegisterPush,
32
+ })
33
+ }
23
34
 
24
35
  const authHeaders = await ctx.serviceAuthHeaders(
25
36
  did,
@@ -3,7 +3,11 @@ import { Server } from '../../../../lexicon'
3
3
 
4
4
  export default function (server: Server, ctx: AppContext) {
5
5
  server.com.atproto.identity.getRecommendedDidCredentials({
6
- auth: ctx.authVerifier.accessStandard(),
6
+ auth: ctx.authVerifier.authorization({
7
+ authorize: () => {
8
+ // always allow
9
+ },
10
+ }),
7
11
  handler: async ({ auth }) => {
8
12
  const requester = auth.credentials.did
9
13
  const signingKey = await ctx.actorStore.keypair(requester)
@@ -1,12 +1,19 @@
1
1
  import { InvalidRequestError } from '@atproto/xrpc-server'
2
- import { AuthScope } from '../../../../auth-verifier'
2
+ import { ACCESS_FULL, AuthScope } from '../../../../auth-scope'
3
3
  import { AppContext } from '../../../../context'
4
4
  import { Server } from '../../../../lexicon'
5
5
  import { ids } from '../../../../lexicon/lexicons'
6
6
 
7
7
  export default function (server: Server, ctx: AppContext) {
8
8
  server.com.atproto.identity.requestPlcOperationSignature({
9
- auth: ctx.authVerifier.accessFull({ additional: [AuthScope.Takendown] }),
9
+ auth: ctx.authVerifier.authorization({
10
+ // @NOTE Reflect any change in signPlcOperation
11
+ scopes: ACCESS_FULL,
12
+ additional: [AuthScope.Takendown],
13
+ authorize: (permissions) => {
14
+ permissions.assertIdentity({ attr: '*' })
15
+ },
16
+ }),
10
17
  handler: async ({ auth, req }) => {
11
18
  if (ctx.entrywayAgent) {
12
19
  await ctx.entrywayAgent.com.atproto.identity.requestPlcOperationSignature(
@@ -1,6 +1,7 @@
1
1
  import * as plc from '@did-plc/lib'
2
2
  import { check } from '@atproto/common'
3
3
  import { InvalidRequestError } from '@atproto/xrpc-server'
4
+ import { ACCESS_FULL, AuthScope } from '../../../../auth-scope'
4
5
  import { AppContext } from '../../../../context'
5
6
  import { Server } from '../../../../lexicon'
6
7
  import { ids } from '../../../../lexicon/lexicons'
@@ -8,7 +9,14 @@ import { resultPassthru } from '../../../proxy'
8
9
 
9
10
  export default function (server: Server, ctx: AppContext) {
10
11
  server.com.atproto.identity.signPlcOperation({
11
- auth: ctx.authVerifier.accessFull(),
12
+ auth: ctx.authVerifier.authorization({
13
+ // @NOTE Should match auth rules from requestPlcOperationSignature
14
+ scopes: ACCESS_FULL,
15
+ additional: [AuthScope.Takendown],
16
+ authorize: (permissions) => {
17
+ permissions.assertIdentity({ attr: '*' })
18
+ },
19
+ }),
12
20
  handler: async ({ auth, input, req }) => {
13
21
  if (ctx.entrywayAgent) {
14
22
  return resultPassthru(
@@ -7,7 +7,11 @@ import { httpLogger as log } from '../../../../logger'
7
7
 
8
8
  export default function (server: Server, ctx: AppContext) {
9
9
  server.com.atproto.identity.submitPlcOperation({
10
- auth: ctx.authVerifier.accessStandard(),
10
+ auth: ctx.authVerifier.authorization({
11
+ authorize: (permissions) => {
12
+ permissions.assertIdentity({ attr: '*' })
13
+ },
14
+ }),
11
15
  handler: async ({ auth, input }) => {
12
16
  const requester = auth.credentials.did
13
17
  const op = input.body.operation
@@ -7,7 +7,12 @@ import { httpLogger } from '../../../../logger'
7
7
 
8
8
  export default function (server: Server, ctx: AppContext) {
9
9
  server.com.atproto.identity.updateHandle({
10
- auth: ctx.authVerifier.accessStandard({ checkTakedown: true }),
10
+ auth: ctx.authVerifier.authorization({
11
+ checkTakedown: true,
12
+ authorize: (permissions) => {
13
+ permissions.assertIdentity({ attr: 'handle' })
14
+ },
15
+ }),
11
16
  rateLimit: [
12
17
  {
13
18
  durationMs: 5 * MINUTE,
@@ -1,14 +1,19 @@
1
1
  import { AtpAgent } from '@atproto/api'
2
- import { AuthScope } from '../../../../auth-verifier'
2
+ import { AuthScope } from '../../../../auth-scope'
3
3
  import { AppContext } from '../../../../context'
4
4
  import { Server } from '../../../../lexicon'
5
5
  import { ids } from '../../../../lexicon/lexicons'
6
- import { parseProxyInfo } from '../../../../pipethrough'
6
+ import { computeProxyTo, parseProxyInfo } from '../../../../pipethrough'
7
7
 
8
8
  export default function (server: Server, ctx: AppContext) {
9
9
  server.com.atproto.moderation.createReport({
10
- auth: ctx.authVerifier.accessStandard({
10
+ auth: ctx.authVerifier.authorization({
11
11
  additional: [AuthScope.Takendown],
12
+ authorize: (permissions, { req }) => {
13
+ const lxm = ids.ComAtprotoModerationCreateReport
14
+ const aud = computeProxyTo(ctx, req, lxm)
15
+ permissions.assertRpc({ aud, lxm })
16
+ },
12
17
  }),
13
18
  handler: async ({ auth, input, req }) => {
14
19
  const { url, did: aud } = await parseProxyInfo(
@@ -38,10 +38,14 @@ const ratelimitPoints = ({ input }: { input: HandlerInput }) => {
38
38
 
39
39
  export default function (server: Server, ctx: AppContext) {
40
40
  server.com.atproto.repo.applyWrites({
41
- auth: ctx.authVerifier.accessStandard({
41
+ auth: ctx.authVerifier.authorization({
42
42
  checkTakedown: true,
43
43
  checkDeactivated: true,
44
+ authorize: () => {
45
+ // Performed in the handler as it is based on the request body
46
+ },
44
47
  }),
48
+
45
49
  rateLimit: [
46
50
  {
47
51
  name: 'repo-write-hour',
@@ -56,31 +60,35 @@ export default function (server: Server, ctx: AppContext) {
56
60
  ],
57
61
 
58
62
  handler: async ({ input, auth }) => {
59
- const tx = input.body
60
- const { repo, validate, swapCommit } = tx
61
- const account = await ctx.accountManager.getAccount(repo, {
62
- includeDeactivated: true,
63
- })
64
-
65
- if (!account) {
66
- throw new InvalidRequestError(`Could not find repo: ${repo}`)
67
- } else if (account.deactivatedAt) {
68
- throw new InvalidRequestError('Account is deactivated')
69
- }
63
+ const { repo, validate, swapCommit, writes } = input.body
70
64
 
71
- const did = account.did
72
- if (did !== auth.credentials.did) {
65
+ const { did } = auth.credentials
66
+ if (repo !== did) {
73
67
  throw new AuthRequiredError()
74
68
  }
75
- if (tx.writes.length > 200) {
69
+ if (writes.length > 200) {
76
70
  throw new InvalidRequestError('Too many writes. Max: 200')
77
71
  }
78
72
 
73
+ // Verify permission of every unique "action" / "collection" pair
74
+ if (auth.credentials.type === 'oauth') {
75
+ // @NOTE Unlike "importRepo", we do not require "action" = "*" here.
76
+ for (const [action, collections] of [
77
+ ['create', new Set(writes.filter(isCreate).map((w) => w.collection))],
78
+ ['update', new Set(writes.filter(isUpdate).map((w) => w.collection))],
79
+ ['delete', new Set(writes.filter(isDelete).map((w) => w.collection))],
80
+ ] as const) {
81
+ for (const collection of collections) {
82
+ auth.credentials.permissions.assertRepo({ action, collection })
83
+ }
84
+ }
85
+ }
86
+
79
87
  // @NOTE should preserve order of ts.writes for final use in response
80
- let writes: PreparedWrite[]
88
+ let preparedWrites: PreparedWrite[]
81
89
  try {
82
- writes = await Promise.all(
83
- tx.writes.map((write) => {
90
+ preparedWrites = await Promise.all(
91
+ writes.map(async (write) => {
84
92
  if (isCreate(write)) {
85
93
  return prepareCreate({
86
94
  did,
@@ -121,7 +129,7 @@ export default function (server: Server, ctx: AppContext) {
121
129
 
122
130
  const commit = await ctx.actorStore.transact(did, async (actorTxn) => {
123
131
  const commit = await actorTxn.repo
124
- .processWrites(writes, swapCommitCid)
132
+ .processWrites(preparedWrites, swapCommitCid)
125
133
  .catch((err) => {
126
134
  if (err instanceof BadCommitSwapError) {
127
135
  throw new InvalidRequestError(err.message, 'InvalidSwap')
@@ -150,7 +158,7 @@ export default function (server: Server, ctx: AppContext) {
150
158
  cid: commit.cid.toString(),
151
159
  rev: commit.rev,
152
160
  },
153
- results: writes.map(writeToOutputResult),
161
+ results: preparedWrites.map(writeToOutputResult),
154
162
  },
155
163
  }
156
164
  },
@@ -14,9 +14,12 @@ import {
14
14
 
15
15
  export default function (server: Server, ctx: AppContext) {
16
16
  server.com.atproto.repo.createRecord({
17
- auth: ctx.authVerifier.accessStandard({
17
+ auth: ctx.authVerifier.authorization({
18
18
  checkTakedown: true,
19
19
  checkDeactivated: true,
20
+ authorize: () => {
21
+ // Performed in the handler as it requires the request body
22
+ },
20
23
  }),
21
24
  rateLimit: [
22
25
  {
@@ -33,6 +36,14 @@ export default function (server: Server, ctx: AppContext) {
33
36
  handler: async ({ input, auth }) => {
34
37
  const { repo, collection, rkey, record, swapCommit, validate } =
35
38
  input.body
39
+
40
+ if (auth.credentials.type === 'oauth') {
41
+ auth.credentials.permissions.assertRepo({
42
+ action: 'create',
43
+ collection,
44
+ })
45
+ }
46
+
36
47
  const account = await ctx.accountManager.getAccount(repo, {
37
48
  includeDeactivated: true,
38
49
  })