@atproto/pds 0.5.12 → 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 +36 -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
package/src/auth-scope.ts DELETED
@@ -1,40 +0,0 @@
1
- // @TODO sync-up with current method names, consider backwards compat.
2
- export enum AuthScope {
3
- Access = 'com.atproto.access',
4
- Refresh = 'com.atproto.refresh',
5
- AppPass = 'com.atproto.appPass',
6
- AppPassPrivileged = 'com.atproto.appPassPrivileged',
7
- SignupQueued = 'com.atproto.signupQueued',
8
- Takendown = 'com.atproto.takendown',
9
- }
10
-
11
- export const ACCESS_FULL = [AuthScope.Access] as const
12
- export const ACCESS_PRIVILEGED = [
13
- ...ACCESS_FULL,
14
- AuthScope.AppPassPrivileged,
15
- ] as const
16
- export const ACCESS_STANDARD = [
17
- ...ACCESS_PRIVILEGED,
18
- AuthScope.AppPass,
19
- ] as const
20
-
21
- const authScopesValues = new Set(Object.values(AuthScope))
22
- export function isAuthScope(val: unknown): val is AuthScope {
23
- return (authScopesValues as Set<unknown>).has(val)
24
- }
25
-
26
- export function isAccessFull(
27
- scope: AuthScope,
28
- ): scope is (typeof ACCESS_FULL)[number] {
29
- return (ACCESS_FULL as readonly string[]).includes(scope)
30
- }
31
-
32
- export function isAccessPrivileged(
33
- scope: AuthScope,
34
- ): scope is (typeof ACCESS_PRIVILEGED)[number] {
35
- return (ACCESS_PRIVILEGED as readonly string[]).includes(scope)
36
- }
37
-
38
- export function isTakendown(scope: unknown): scope is AuthScope.Takendown {
39
- return scope === AuthScope.Takendown
40
- }
@@ -1,668 +0,0 @@
1
- import { KeyObject, createPublicKey, createSecretKey } from 'node:crypto'
2
- import { IncomingMessage, ServerResponse } from 'node:http'
3
- import * as jose from 'jose'
4
- import KeyEncoderModule from 'key-encoder'
5
- import { getVerificationMaterial } from '@atproto/common'
6
- import { IdResolver, getDidKeyFromMultibase } from '@atproto/identity'
7
- import { AtIdentifierString, DidString, isDidString } from '@atproto/lex'
8
- import {
9
- OAuthError,
10
- OAuthVerifier,
11
- VerifyTokenPayloadOptions,
12
- WWWAuthenticateError,
13
- } from '@atproto/oauth-provider'
14
- import {
15
- ScopePermissions,
16
- ScopePermissionsTransition,
17
- } from '@atproto/oauth-scopes'
18
- import {
19
- AuthRequiredError,
20
- Awaitable,
21
- ForbiddenError,
22
- InvalidRequestError,
23
- MethodAuthContext,
24
- MethodAuthVerifier,
25
- Params,
26
- XRPCError,
27
- parseReqNsid,
28
- verifyJwt as verifyServiceJwt,
29
- } from '@atproto/xrpc-server'
30
- import { AccountManager } from './account-manager/account-manager.js'
31
- import { ActorAccount } from './account-manager/helpers/account.js'
32
- import {
33
- AccessOutput,
34
- AdminTokenOutput,
35
- ModServiceOutput,
36
- OAuthOutput,
37
- RefreshOutput,
38
- UnauthenticatedOutput,
39
- UserServiceAuthOutput,
40
- } from './auth-output.js'
41
- import { ACCESS_STANDARD, AuthScope, isAuthScope } from './auth-scope.js'
42
- import { softDeleted } from './db/index.js'
43
- import { appendVary } from './util/http.js'
44
- import { WithRequired } from './util/types.js'
45
-
46
- // key-encoder is CJS with exports.default; Node ESM interop wraps it as { default: Class }
47
- const KeyEncoder = ((m) => m.default ?? m)(KeyEncoderModule)
48
-
49
- export type VerifiedOptions = {
50
- checkTakedown?: boolean
51
- checkDeactivated?: boolean
52
- }
53
-
54
- export type ScopedOptions<S extends AuthScope = AuthScope> = {
55
- scopes?: readonly S[]
56
- }
57
-
58
- export type ExtraScopedOptions<S extends AuthScope = AuthScope> = {
59
- additional?: readonly S[]
60
- }
61
-
62
- export type AuthorizedOptions<P extends Params = Params> = {
63
- authorize: (
64
- permissions: ScopePermissions,
65
- ctx: MethodAuthContext<P>,
66
- ) => Awaitable<void>
67
- }
68
-
69
- export type AuthVerifierOpts = {
70
- publicUrl: string
71
- jwtKey: KeyObject
72
- adminPass: string
73
- dids: {
74
- pds: string
75
- entryway?: string
76
- modService?: string
77
- }
78
- }
79
-
80
- export type VerifyBearerJwtOptions<S extends AuthScope = AuthScope> =
81
- WithRequired<
82
- Omit<jose.JWTVerifyOptions, 'scopes'> & {
83
- scopes: readonly S[]
84
- },
85
- 'audience' | 'typ'
86
- >
87
-
88
- export type VerifyBearerJwtResult<S extends AuthScope = AuthScope> = {
89
- sub: DidString
90
- aud: string
91
- jti: string | undefined
92
- scope: S
93
- }
94
-
95
- export class AuthVerifier {
96
- private _publicUrl: string
97
- private _jwtKey: KeyObject
98
- private _adminPass: string
99
- public dids: AuthVerifierOpts['dids']
100
-
101
- constructor(
102
- public accountManager: AccountManager,
103
- public idResolver: IdResolver,
104
- public oauthVerifier: OAuthVerifier,
105
- opts: AuthVerifierOpts,
106
- ) {
107
- this._publicUrl = opts.publicUrl
108
- this._jwtKey = opts.jwtKey
109
- this._adminPass = opts.adminPass
110
- this.dids = opts.dids
111
- }
112
-
113
- // verifiers (arrow fns to preserve scope)
114
-
115
- public unauthenticated: MethodAuthVerifier<UnauthenticatedOutput> = (ctx) => {
116
- setAuthHeaders(ctx.res)
117
-
118
- // @NOTE this auth method is typically used as fallback when no other auth
119
- // method is applicable. This means that the presence of an "authorization"
120
- // header means that that header is invalid (as it did not match any of the
121
- // other auth methods).
122
- if (ctx.req.headers['authorization']) {
123
- throw new AuthRequiredError('Invalid authorization header')
124
- }
125
-
126
- return {
127
- credentials: null,
128
- }
129
- }
130
-
131
- public adminToken: MethodAuthVerifier<AdminTokenOutput> = async (ctx) => {
132
- setAuthHeaders(ctx.res)
133
- const parsed = parseBasicAuth(ctx.req)
134
- if (!parsed) {
135
- throw new AuthRequiredError()
136
- }
137
- const { username, password } = parsed
138
- if (username !== 'admin' || password !== this._adminPass) {
139
- throw new AuthRequiredError()
140
- }
141
-
142
- return { credentials: { type: 'admin_token' } }
143
- }
144
-
145
- public modService: MethodAuthVerifier<ModServiceOutput> = async (ctx) => {
146
- setAuthHeaders(ctx.res)
147
- if (!this.dids.modService) {
148
- throw new AuthRequiredError('Untrusted issuer', 'UntrustedIss')
149
- }
150
- const payload = await this.verifyServiceJwt(ctx.req, {
151
- iss: [this.dids.modService, `${this.dids.modService}#atproto_labeler`],
152
- })
153
- return {
154
- credentials: {
155
- type: 'mod_service',
156
- did: payload.iss,
157
- },
158
- }
159
- }
160
-
161
- public moderator: MethodAuthVerifier<AdminTokenOutput | ModServiceOutput> =
162
- async (ctx) => {
163
- const type = extractAuthType(ctx.req)
164
- if (type === AuthType.BEARER) {
165
- return this.modService(ctx)
166
- } else {
167
- return this.adminToken(ctx)
168
- }
169
- }
170
-
171
- protected access<S extends AuthScope>(
172
- options: VerifiedOptions & Required<ScopedOptions<S>>,
173
- ): MethodAuthVerifier<AccessOutput<S>> {
174
- const { scopes, ...statusOptions } = options
175
-
176
- const verifyJwtOptions: VerifyBearerJwtOptions<S> = {
177
- audience: this.dids.pds,
178
- typ: 'at+jwt',
179
- scopes:
180
- // @NOTE We can reject taken down credentials based on the scope if
181
- // "checkTakedown" is set.
182
- statusOptions.checkTakedown && scopes.includes(AuthScope.Takendown as S)
183
- ? scopes.filter((s) => s !== AuthScope.Takendown)
184
- : scopes,
185
- }
186
-
187
- return async (ctx) => {
188
- setAuthHeaders(ctx.res)
189
-
190
- const { sub: did, scope } = await this.verifyBearerJwt(
191
- ctx.req,
192
- verifyJwtOptions,
193
- )
194
-
195
- await this.verifyStatus(did, statusOptions)
196
-
197
- return {
198
- credentials: { type: 'access', did, scope },
199
- }
200
- }
201
- }
202
-
203
- public refresh(options?: {
204
- allowExpired?: boolean
205
- }): MethodAuthVerifier<RefreshOutput> {
206
- const verifyOptions: VerifyBearerJwtOptions<AuthScope.Refresh> = {
207
- clockTolerance: options?.allowExpired ? Infinity : undefined,
208
- typ: 'refresh+jwt',
209
- // when using entryway, proxying refresh credentials
210
- audience: this.dids.entryway ? this.dids.entryway : this.dids.pds,
211
- scopes: [AuthScope.Refresh],
212
- }
213
-
214
- return async (ctx) => {
215
- setAuthHeaders(ctx.res)
216
-
217
- const result = await this.verifyBearerJwt(ctx.req, verifyOptions)
218
-
219
- const tokenId = result.jti
220
- if (!tokenId) {
221
- throw new AuthRequiredError(
222
- 'Unexpected missing refresh token id',
223
- 'MissingTokenId',
224
- )
225
- }
226
-
227
- return {
228
- credentials: {
229
- type: 'refresh',
230
- did: result.sub,
231
- scope: result.scope,
232
- tokenId,
233
- },
234
- }
235
- }
236
- }
237
-
238
- public authorization<P extends Params>({
239
- scopes = ACCESS_STANDARD,
240
- additional = [],
241
- ...options
242
- }: VerifiedOptions &
243
- ScopedOptions &
244
- ExtraScopedOptions &
245
- AuthorizedOptions<P>): MethodAuthVerifier<AccessOutput | OAuthOutput, P> {
246
- const access = this.access({
247
- ...options,
248
- scopes: [...scopes, ...additional],
249
- })
250
- const oauth = this.oauth(options)
251
-
252
- return async (ctx) => {
253
- const type = extractAuthType(ctx.req)
254
-
255
- if (type === AuthType.BEARER) {
256
- return access(ctx)
257
- }
258
-
259
- if (type === AuthType.DPOP) {
260
- return oauth(ctx)
261
- }
262
-
263
- // Auth headers are set through the access and oauth methods so we only
264
- // need to set them here if we reach this point
265
- setAuthHeaders(ctx.res)
266
-
267
- if (type !== null) {
268
- throw new InvalidRequestError(
269
- 'Unexpected authorization type',
270
- 'InvalidToken',
271
- )
272
- }
273
-
274
- throw new AuthRequiredError(undefined, 'AuthMissing')
275
- }
276
- }
277
-
278
- public authorizationOrAdminTokenOptional<P extends Params>(
279
- opts: VerifiedOptions & ExtraScopedOptions & AuthorizedOptions<P>,
280
- ): MethodAuthVerifier<
281
- OAuthOutput | AccessOutput | AdminTokenOutput | UnauthenticatedOutput,
282
- P
283
- > {
284
- const authorization = this.authorization(opts)
285
- return async (ctx) => {
286
- const type = extractAuthType(ctx.req)
287
- if (type === AuthType.BEARER || type === AuthType.DPOP) {
288
- return authorization(ctx)
289
- } else if (type === AuthType.BASIC) {
290
- return this.adminToken(ctx)
291
- } else {
292
- return this.unauthenticated(ctx)
293
- }
294
- }
295
- }
296
-
297
- public userServiceAuth: MethodAuthVerifier<UserServiceAuthOutput> = async (
298
- ctx,
299
- ) => {
300
- setAuthHeaders(ctx.res)
301
- const payload = await this.verifyServiceJwt(ctx.req)
302
- return {
303
- credentials: {
304
- type: 'user_service_auth',
305
- did: payload.iss,
306
- },
307
- }
308
- }
309
-
310
- public userServiceAuthOptional: MethodAuthVerifier<
311
- UserServiceAuthOutput | UnauthenticatedOutput
312
- > = async (ctx) => {
313
- const type = extractAuthType(ctx.req)
314
- if (type === AuthType.BEARER) {
315
- return await this.userServiceAuth(ctx)
316
- } else {
317
- return this.unauthenticated(ctx)
318
- }
319
- }
320
-
321
- public authorizationOrUserServiceAuth<P extends Params>(
322
- options: VerifiedOptions &
323
- ScopedOptions &
324
- ExtraScopedOptions &
325
- AuthorizedOptions<P>,
326
- ): MethodAuthVerifier<UserServiceAuthOutput | OAuthOutput | AccessOutput, P> {
327
- const authorizationVerifier = this.authorization(options)
328
- return async (ctx) => {
329
- if (isDefinitelyServiceAuth(ctx.req)) {
330
- return this.userServiceAuth(ctx)
331
- } else {
332
- return authorizationVerifier(ctx)
333
- }
334
- }
335
- }
336
-
337
- protected oauth<P extends Params>({
338
- authorize,
339
- ...verifyStatusOptions
340
- }: VerifiedOptions & AuthorizedOptions<P>): MethodAuthVerifier<
341
- OAuthOutput,
342
- P
343
- > {
344
- const verifyTokenOptions: VerifyTokenPayloadOptions = {
345
- audience: [this.dids.pds],
346
- scope: ['atproto'],
347
- }
348
-
349
- return async (ctx) => {
350
- setAuthHeaders(ctx.res)
351
-
352
- const { req, res } = ctx
353
-
354
- // https://datatracker.ietf.org/doc/html/rfc9449#section-8.2
355
- const dpopNonce = this.oauthVerifier.nextDpopNonce()
356
- if (dpopNonce) {
357
- res.setHeader('DPoP-Nonce', dpopNonce)
358
- res.appendHeader('Access-Control-Expose-Headers', 'DPoP-Nonce')
359
- }
360
-
361
- const originalUrl = req.originalUrl || req.url || '/'
362
- const url = new URL(originalUrl, this._publicUrl)
363
-
364
- const { scope, sub: did } = await this.oauthVerifier
365
- .authenticateRequest(
366
- req.method || 'GET',
367
- url,
368
- req.headers,
369
- verifyTokenOptions,
370
- )
371
- .catch((err) => {
372
- // Make sure to include any WWW-Authenticate header in the response
373
- // (particularly useful for DPoP's "use_dpop_nonce" error)
374
- if (err instanceof WWWAuthenticateError) {
375
- res.setHeader('WWW-Authenticate', err.wwwAuthenticateHeader)
376
- res.appendHeader(
377
- 'Access-Control-Expose-Headers',
378
- 'WWW-Authenticate',
379
- )
380
- }
381
-
382
- if (err instanceof OAuthError) {
383
- throw new XRPCError(err.status, err.error_description, err.error)
384
- }
385
-
386
- throw err
387
- })
388
-
389
- if (!isDidString(did)) {
390
- throw new InvalidRequestError('Malformed token', 'InvalidToken')
391
- }
392
-
393
- await this.verifyStatus(did, verifyStatusOptions)
394
-
395
- const permissions = new ScopePermissionsTransition(scope?.split(' '))
396
-
397
- // Should never happen
398
- if (!permissions.scopes.has('atproto')) {
399
- throw new InvalidRequestError(
400
- 'OAuth token does not have "atproto" scope',
401
- 'InvalidToken',
402
- )
403
- }
404
-
405
- await authorize(permissions, ctx)
406
-
407
- return {
408
- credentials: {
409
- type: 'oauth',
410
- did,
411
- permissions,
412
- },
413
- }
414
- }
415
- }
416
-
417
- protected async verifyStatus(
418
- did: DidString,
419
- options: VerifiedOptions,
420
- ): Promise<void> {
421
- if (options.checkDeactivated || options.checkTakedown) {
422
- await this.findAccount(did, options)
423
- }
424
- }
425
-
426
- /**
427
- * Finds an account by its handle or DID, returning possibly deactivated or
428
- * taken down accounts (unless `options.checkDeactivated` or
429
- * `options.checkTakedown` are set to true, respectively).
430
- */
431
- public async findAccount(
432
- handleOrDid: AtIdentifierString,
433
- options: VerifiedOptions,
434
- ): Promise<ActorAccount> {
435
- const account = await this.accountManager.getAccount(handleOrDid, {
436
- includeDeactivated: true,
437
- includeTakenDown: true,
438
- })
439
- if (!account) {
440
- // will be turned into ExpiredToken for the client if proxied by entryway
441
- throw new ForbiddenError('Account not found', 'AccountNotFound')
442
- }
443
- if (options.checkTakedown && softDeleted(account)) {
444
- throw new AuthRequiredError(
445
- 'Account has been taken down',
446
- 'AccountTakedown',
447
- )
448
- }
449
- if (options.checkDeactivated && account.deactivatedAt) {
450
- throw new AuthRequiredError(
451
- 'Account is deactivated',
452
- 'AccountDeactivated',
453
- )
454
- }
455
- return account
456
- }
457
-
458
- /**
459
- * Wraps {@link jose.jwtVerify} into a function that also validates the token
460
- * payload's type and wraps errors into {@link InvalidRequestError}.
461
- */
462
- protected async verifyBearerJwt<S extends AuthScope = AuthScope>(
463
- req: IncomingMessage,
464
- { scopes, ...options }: VerifyBearerJwtOptions<S>,
465
- ): Promise<VerifyBearerJwtResult<S>> {
466
- const token = bearerTokenFromReq(req)
467
- if (!token) {
468
- throw new AuthRequiredError(undefined, 'AuthMissing')
469
- }
470
-
471
- const { payload } = await jose
472
- .jwtVerify(token, this._jwtKey, options)
473
- .catch((cause) => {
474
- if (cause instanceof jose.errors.JWTExpired) {
475
- throw new InvalidRequestError('Token has expired', 'ExpiredToken', {
476
- cause,
477
- })
478
- } else {
479
- throw new InvalidRequestError(
480
- 'Token could not be verified',
481
- 'InvalidToken',
482
- { cause },
483
- )
484
- }
485
- })
486
-
487
- const { sub, aud, scope, lxm, cnf, jti } = payload
488
-
489
- if (typeof lxm !== 'undefined') {
490
- // Service auth tokens should never make it to here. But since service
491
- // auth tokens do not have a "typ" header, the "typ" check above will not
492
- // catch them. This check here is mainly to protect against the
493
- // hypothetical case in which a PDS would issue service auth tokens using
494
- // its private key.
495
- throw new InvalidRequestError('Malformed token', 'InvalidToken')
496
- }
497
- if (typeof cnf !== 'undefined') {
498
- // Proof-of-Possession (PoP) tokens are not allowed here
499
- // https://www.rfc-editor.org/rfc/rfc7800.html
500
- throw new InvalidRequestError('Malformed token', 'InvalidToken')
501
- }
502
- if (typeof sub !== 'string' || !isDidString(sub)) {
503
- throw new InvalidRequestError('Malformed token', 'InvalidToken')
504
- }
505
- if (typeof aud !== 'string' || !aud.startsWith('did:')) {
506
- throw new InvalidRequestError('Malformed token', 'InvalidToken')
507
- }
508
- if (typeof jti !== 'string' && typeof jti !== 'undefined') {
509
- throw new InvalidRequestError('Malformed token', 'InvalidToken')
510
- }
511
- if (!isAuthScope(scope) || !scopes.includes(scope as any)) {
512
- throw new InvalidRequestError('Bad token scope', 'InvalidToken')
513
- }
514
-
515
- return { sub, aud, jti, scope: scope as S }
516
- }
517
-
518
- protected async verifyServiceJwt(
519
- req: IncomingMessage,
520
- opts?: { iss?: string[] },
521
- ) {
522
- const jwtStr = bearerTokenFromReq(req)
523
- if (!jwtStr) {
524
- throw new AuthRequiredError('missing jwt', 'MissingJwt')
525
- }
526
-
527
- const nsid = parseReqNsid(req)
528
- const payload = await verifyServiceJwt(
529
- jwtStr,
530
- null,
531
- nsid,
532
- async (iss, forceRefresh) => {
533
- if (opts?.iss && !opts.iss.includes(iss)) {
534
- throw new AuthRequiredError('Untrusted issuer', 'UntrustedIss')
535
- }
536
- const [did, serviceId] = iss.split('#')
537
- const keyId =
538
- serviceId === 'atproto_labeler' ? 'atproto_label' : 'atproto'
539
- const didDoc = await this.idResolver.did.resolve(did, forceRefresh)
540
- if (!didDoc) {
541
- throw new AuthRequiredError('could not resolve iss did')
542
- }
543
- const parsedKey = getVerificationMaterial(didDoc, keyId)
544
- if (!parsedKey) {
545
- throw new AuthRequiredError('missing or bad key in did doc')
546
- }
547
- const didKey = getDidKeyFromMultibase(parsedKey)
548
- if (!didKey) {
549
- throw new AuthRequiredError('missing or bad key in did doc')
550
- }
551
- return didKey
552
- },
553
- )
554
- if (
555
- payload.aud !== this.dids.pds &&
556
- (!this.dids.entryway || payload.aud !== this.dids.entryway)
557
- ) {
558
- throw new AuthRequiredError(
559
- 'jwt audience does not match service did',
560
- 'BadJwtAudience',
561
- )
562
- }
563
- return payload
564
- }
565
- }
566
-
567
- // HELPERS
568
- // ---------
569
-
570
- export function isUserOrAdmin(
571
- auth: AccessOutput | OAuthOutput | AdminTokenOutput | UnauthenticatedOutput,
572
- did: string,
573
- ): boolean {
574
- if (!auth.credentials) {
575
- return false
576
- } else if (auth.credentials.type === 'admin_token') {
577
- return true
578
- } else {
579
- return auth.credentials.did === did
580
- }
581
- }
582
-
583
- enum AuthType {
584
- BASIC = 'Basic',
585
- BEARER = 'Bearer',
586
- DPOP = 'DPoP',
587
- }
588
-
589
- const parseAuthorizationHeader = (
590
- req: IncomingMessage,
591
- ): [type: null] | [type: AuthType, token: string] => {
592
- const authorization = req.headers['authorization']
593
- if (!authorization) return [null]
594
-
595
- const result = authorization.split(' ')
596
- if (result.length !== 2) {
597
- throw new InvalidRequestError(
598
- 'Malformed authorization header',
599
- 'InvalidToken',
600
- )
601
- }
602
-
603
- // authorization type is case-insensitive
604
- const authType = result[0].toUpperCase()
605
-
606
- const type = Object.hasOwn(AuthType, authType) ? AuthType[authType] : null
607
- if (type) return [type, result[1]]
608
-
609
- throw new InvalidRequestError(
610
- `Unsupported authorization type: ${result[0]}`,
611
- 'InvalidToken',
612
- )
613
- }
614
-
615
- /**
616
- * @note Not all service auth tokens are guaranteed to have "lxm" claim, so this
617
- * function should not be used to verify service auth tokens. It is only used to
618
- * check if a token is definitely a service auth token.
619
- */
620
- const isDefinitelyServiceAuth = (req: IncomingMessage): boolean => {
621
- const token = bearerTokenFromReq(req)
622
- if (!token) return false
623
- const payload = jose.decodeJwt(token)
624
- return payload['lxm'] != null
625
- }
626
-
627
- const extractAuthType = (req: IncomingMessage): AuthType | null => {
628
- const [type] = parseAuthorizationHeader(req)
629
- return type
630
- }
631
-
632
- export const bearerTokenFromReq = (req: IncomingMessage) => {
633
- const [type, token] = parseAuthorizationHeader(req)
634
- return type === AuthType.BEARER ? token : null
635
- }
636
-
637
- const parseBasicAuth = (
638
- req: IncomingMessage,
639
- ): { username: string; password: string } | null => {
640
- try {
641
- const [type, b64] = parseAuthorizationHeader(req)
642
- if (type !== AuthType.BASIC) return null
643
- const decoded = Buffer.from(b64, 'base64').toString('utf8')
644
- // We must not use split(':') because the password can contain colons
645
- const colon = decoded.indexOf(':')
646
- if (colon === -1) return null
647
- const username = decoded.slice(0, colon)
648
- const password = decoded.slice(colon + 1)
649
- return { username, password }
650
- } catch (err) {
651
- return null
652
- }
653
- }
654
-
655
- export const createSecretKeyObject = (secret: string): KeyObject => {
656
- return createSecretKey(Buffer.from(secret))
657
- }
658
-
659
- const keyEncoder = new KeyEncoder('secp256k1')
660
- export const createPublicKeyObject = (publicKeyHex: string): KeyObject => {
661
- const key = keyEncoder.encodePublic(publicKeyHex, 'raw', 'pem')
662
- return createPublicKey({ format: 'pem', key })
663
- }
664
-
665
- function setAuthHeaders(res: ServerResponse) {
666
- res.setHeader('Cache-Control', 'private')
667
- appendVary(res, 'Authorization')
668
- }