@atproto/pds 0.4.104 → 0.4.105

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 (206) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/account-manager/{index.d.ts → account-manager.d.ts} +26 -35
  3. package/dist/account-manager/account-manager.d.ts.map +1 -0
  4. package/dist/account-manager/{index.js → account-manager.js} +52 -207
  5. package/dist/account-manager/account-manager.js.map +1 -0
  6. package/dist/account-manager/helpers/account.d.ts +3 -3
  7. package/dist/account-manager/helpers/device-account.d.ts +15 -15
  8. package/dist/account-manager/helpers/device-account.d.ts.map +1 -1
  9. package/dist/account-manager/helpers/device-account.js +2 -1
  10. package/dist/account-manager/helpers/device-account.js.map +1 -1
  11. package/dist/account-manager/helpers/token.d.ts +98 -98
  12. package/dist/account-manager/oauth-store.d.ts +58 -0
  13. package/dist/account-manager/oauth-store.d.ts.map +1 -0
  14. package/dist/account-manager/oauth-store.js +417 -0
  15. package/dist/account-manager/oauth-store.js.map +1 -0
  16. package/dist/actor-store/record/reader.d.ts +3 -3
  17. package/dist/actor-store/repo/reader.d.ts +2 -0
  18. package/dist/actor-store/repo/reader.d.ts.map +1 -1
  19. package/dist/actor-store/repo/reader.js +9 -0
  20. package/dist/actor-store/repo/reader.js.map +1 -1
  21. package/dist/actor-store/repo/sql-repo-reader.d.ts +1 -1
  22. package/dist/actor-store/repo/transactor.d.ts.map +1 -1
  23. package/dist/actor-store/repo/transactor.js +13 -4
  24. package/dist/actor-store/repo/transactor.js.map +1 -1
  25. package/dist/api/com/atproto/admin/deleteAccount.d.ts.map +1 -1
  26. package/dist/api/com/atproto/admin/deleteAccount.js +2 -3
  27. package/dist/api/com/atproto/admin/deleteAccount.js.map +1 -1
  28. package/dist/api/com/atproto/admin/updateAccountHandle.d.ts.map +1 -1
  29. package/dist/api/com/atproto/admin/updateAccountHandle.js +2 -6
  30. package/dist/api/com/atproto/admin/updateAccountHandle.js.map +1 -1
  31. package/dist/api/com/atproto/identity/resolveHandle.d.ts.map +1 -1
  32. package/dist/api/com/atproto/identity/resolveHandle.js +2 -36
  33. package/dist/api/com/atproto/identity/resolveHandle.js.map +1 -1
  34. package/dist/api/com/atproto/identity/updateHandle.d.ts.map +1 -1
  35. package/dist/api/com/atproto/identity/updateHandle.js +1 -7
  36. package/dist/api/com/atproto/identity/updateHandle.js.map +1 -1
  37. package/dist/api/com/atproto/server/activateAccount.d.ts.map +1 -1
  38. package/dist/api/com/atproto/server/activateAccount.js +2 -18
  39. package/dist/api/com/atproto/server/activateAccount.js.map +1 -1
  40. package/dist/api/com/atproto/server/createAccount.d.ts.map +1 -1
  41. package/dist/api/com/atproto/server/createAccount.js +5 -7
  42. package/dist/api/com/atproto/server/createAccount.js.map +1 -1
  43. package/dist/api/com/atproto/server/createSession.js +1 -1
  44. package/dist/api/com/atproto/server/createSession.js.map +1 -1
  45. package/dist/api/com/atproto/server/deleteAccount.d.ts.map +1 -1
  46. package/dist/api/com/atproto/server/deleteAccount.js +2 -3
  47. package/dist/api/com/atproto/server/deleteAccount.js.map +1 -1
  48. package/dist/api/com/atproto/server/getSession.js +1 -1
  49. package/dist/api/com/atproto/server/getSession.js.map +1 -1
  50. package/dist/api/com/atproto/server/refreshSession.js +1 -1
  51. package/dist/api/com/atproto/server/refreshSession.js.map +1 -1
  52. package/dist/api/com/atproto/sync/getRecord.d.ts.map +1 -1
  53. package/dist/api/com/atproto/sync/getRecord.js.map +1 -1
  54. package/dist/api/com/atproto/sync/getRepoStatus.js +1 -1
  55. package/dist/api/com/atproto/sync/getRepoStatus.js.map +1 -1
  56. package/dist/api/com/atproto/sync/listRepos.js +1 -1
  57. package/dist/api/com/atproto/sync/listRepos.js.map +1 -1
  58. package/dist/api/com/atproto/sync/subscribeRepos.d.ts.map +1 -1
  59. package/dist/api/com/atproto/sync/subscribeRepos.js +2 -10
  60. package/dist/api/com/atproto/sync/subscribeRepos.js.map +1 -1
  61. package/dist/app-view.d.ts +14 -0
  62. package/dist/app-view.d.ts.map +1 -0
  63. package/dist/app-view.js +36 -0
  64. package/dist/app-view.js.map +1 -0
  65. package/dist/auth-routes.d.ts +1 -1
  66. package/dist/auth-routes.d.ts.map +1 -1
  67. package/dist/auth-routes.js +9 -3
  68. package/dist/auth-routes.js.map +1 -1
  69. package/dist/auth-verifier.d.ts +1 -1
  70. package/dist/auth-verifier.d.ts.map +1 -1
  71. package/dist/config/config.d.ts +3 -2
  72. package/dist/config/config.d.ts.map +1 -1
  73. package/dist/config/config.js +17 -7
  74. package/dist/config/config.js.map +1 -1
  75. package/dist/config/env.d.ts +4 -0
  76. package/dist/config/env.d.ts.map +1 -1
  77. package/dist/config/env.js +5 -0
  78. package/dist/config/env.js.map +1 -1
  79. package/dist/context.d.ts +4 -4
  80. package/dist/context.d.ts.map +1 -1
  81. package/dist/context.js +24 -18
  82. package/dist/context.js.map +1 -1
  83. package/dist/handle/index.d.ts +0 -7
  84. package/dist/handle/index.d.ts.map +1 -1
  85. package/dist/handle/index.js +4 -58
  86. package/dist/handle/index.js.map +1 -1
  87. package/dist/image/image-url.d.ts +8 -0
  88. package/dist/image/image-url.d.ts.map +1 -0
  89. package/dist/image/image-url.js +26 -0
  90. package/dist/image/image-url.js.map +1 -0
  91. package/dist/lexicon/index.d.ts +6 -0
  92. package/dist/lexicon/index.d.ts.map +1 -1
  93. package/dist/lexicon/index.js +12 -0
  94. package/dist/lexicon/index.js.map +1 -1
  95. package/dist/lexicon/lexicons.d.ts +304 -130
  96. package/dist/lexicon/lexicons.d.ts.map +1 -1
  97. package/dist/lexicon/lexicons.js +168 -67
  98. package/dist/lexicon/lexicons.js.map +1 -1
  99. package/dist/lexicon/types/app/bsky/embed/video.d.ts +1 -0
  100. package/dist/lexicon/types/app/bsky/embed/video.d.ts.map +1 -1
  101. package/dist/lexicon/types/app/bsky/embed/video.js.map +1 -1
  102. package/dist/lexicon/types/com/atproto/identity/defs.d.ts +17 -0
  103. package/dist/lexicon/types/com/atproto/identity/defs.d.ts.map +1 -0
  104. package/dist/lexicon/types/com/atproto/identity/defs.js +16 -0
  105. package/dist/lexicon/types/com/atproto/identity/defs.js.map +1 -0
  106. package/dist/lexicon/types/com/atproto/identity/refreshIdentity.d.ts +39 -0
  107. package/dist/lexicon/types/com/atproto/identity/refreshIdentity.d.ts.map +1 -0
  108. package/dist/lexicon/types/com/atproto/identity/refreshIdentity.js +7 -0
  109. package/dist/lexicon/types/com/atproto/identity/refreshIdentity.js.map +1 -0
  110. package/dist/lexicon/types/com/atproto/identity/resolveDid.d.ts +40 -0
  111. package/dist/lexicon/types/com/atproto/identity/resolveDid.d.ts.map +1 -0
  112. package/dist/lexicon/types/com/atproto/identity/resolveDid.js +7 -0
  113. package/dist/lexicon/types/com/atproto/identity/resolveDid.js.map +1 -0
  114. package/dist/lexicon/types/com/atproto/identity/resolveHandle.d.ts +1 -0
  115. package/dist/lexicon/types/com/atproto/identity/resolveHandle.d.ts.map +1 -1
  116. package/dist/lexicon/types/com/atproto/identity/resolveIdentity.d.ts +36 -0
  117. package/dist/lexicon/types/com/atproto/identity/resolveIdentity.d.ts.map +1 -0
  118. package/dist/lexicon/types/com/atproto/identity/resolveIdentity.js +7 -0
  119. package/dist/lexicon/types/com/atproto/identity/resolveIdentity.js.map +1 -0
  120. package/dist/lexicon/types/com/atproto/sync/subscribeRepos.d.ts +1 -30
  121. package/dist/lexicon/types/com/atproto/sync/subscribeRepos.d.ts.map +1 -1
  122. package/dist/lexicon/types/com/atproto/sync/subscribeRepos.js +0 -27
  123. package/dist/lexicon/types/com/atproto/sync/subscribeRepos.js.map +1 -1
  124. package/dist/mailer/index.d.ts +5 -5
  125. package/dist/mailer/index.d.ts.map +1 -1
  126. package/dist/mailer/index.js +6 -5
  127. package/dist/mailer/index.js.map +1 -1
  128. package/dist/read-after-write/viewer.d.ts +1 -1
  129. package/dist/read-after-write/viewer.d.ts.map +1 -1
  130. package/dist/repo/types.d.ts +6 -2
  131. package/dist/repo/types.d.ts.map +1 -1
  132. package/dist/repo/types.js.map +1 -1
  133. package/dist/scripts/rebuild-repo.d.ts.map +1 -1
  134. package/dist/scripts/rebuild-repo.js +2 -1
  135. package/dist/scripts/rebuild-repo.js.map +1 -1
  136. package/dist/sequencer/db/schema.d.ts +1 -1
  137. package/dist/sequencer/db/schema.d.ts.map +1 -1
  138. package/dist/sequencer/events.d.ts +29 -41
  139. package/dist/sequencer/events.d.ts.map +1 -1
  140. package/dist/sequencer/events.js +24 -58
  141. package/dist/sequencer/events.js.map +1 -1
  142. package/dist/sequencer/sequencer.d.ts +2 -3
  143. package/dist/sequencer/sequencer.d.ts.map +1 -1
  144. package/dist/sequencer/sequencer.js +5 -17
  145. package/dist/sequencer/sequencer.js.map +1 -1
  146. package/package.json +15 -15
  147. package/src/account-manager/{index.ts → account-manager.ts} +107 -307
  148. package/src/account-manager/helpers/device-account.ts +1 -0
  149. package/src/account-manager/oauth-store.ts +494 -0
  150. package/src/actor-store/repo/reader.ts +11 -0
  151. package/src/actor-store/repo/transactor.ts +15 -4
  152. package/src/api/com/atproto/admin/deleteAccount.ts +2 -3
  153. package/src/api/com/atproto/admin/updateAccountHandle.ts +7 -8
  154. package/src/api/com/atproto/identity/resolveHandle.ts +2 -11
  155. package/src/api/com/atproto/identity/updateHandle.ts +4 -7
  156. package/src/api/com/atproto/server/activateAccount.ts +4 -18
  157. package/src/api/com/atproto/server/createAccount.ts +10 -11
  158. package/src/api/com/atproto/server/createSession.ts +1 -1
  159. package/src/api/com/atproto/server/deleteAccount.ts +2 -3
  160. package/src/api/com/atproto/server/getSession.ts +1 -1
  161. package/src/api/com/atproto/server/refreshSession.ts +1 -1
  162. package/src/api/com/atproto/sync/getRecord.ts +0 -1
  163. package/src/api/com/atproto/sync/getRepoStatus.ts +1 -1
  164. package/src/api/com/atproto/sync/listRepos.ts +1 -1
  165. package/src/api/com/atproto/sync/subscribeRepos.ts +2 -9
  166. package/src/app-view.ts +24 -0
  167. package/src/auth-routes.ts +9 -3
  168. package/src/auth-verifier.ts +1 -1
  169. package/src/config/config.ts +25 -13
  170. package/src/config/env.ts +12 -0
  171. package/src/context.ts +44 -24
  172. package/src/handle/index.ts +6 -52
  173. package/src/image/image-url.ts +16 -0
  174. package/src/lexicon/index.ts +36 -0
  175. package/src/lexicon/lexicons.ts +183 -67
  176. package/src/lexicon/types/app/bsky/embed/video.ts +1 -0
  177. package/src/lexicon/types/com/atproto/identity/defs.ts +30 -0
  178. package/src/lexicon/types/com/atproto/identity/refreshIdentity.ts +52 -0
  179. package/src/lexicon/types/com/atproto/identity/resolveDid.ts +52 -0
  180. package/src/lexicon/types/com/atproto/identity/resolveHandle.ts +1 -0
  181. package/src/lexicon/types/com/atproto/identity/resolveIdentity.ts +48 -0
  182. package/src/lexicon/types/com/atproto/sync/subscribeRepos.ts +0 -59
  183. package/src/mailer/index.ts +7 -5
  184. package/src/read-after-write/viewer.ts +1 -1
  185. package/src/repo/types.ts +7 -2
  186. package/src/scripts/rebuild-repo.ts +4 -1
  187. package/src/sequencer/db/schema.ts +1 -8
  188. package/src/sequencer/events.ts +29 -75
  189. package/src/sequencer/sequencer.ts +9 -23
  190. package/tests/account-deletion.test.ts +3 -5
  191. package/tests/oauth.test.ts +286 -71
  192. package/tests/sequencer.test.ts +18 -27
  193. package/tests/sync/subscribe-repos.test.ts +67 -45
  194. package/tsconfig.build.tsbuildinfo +1 -1
  195. package/dist/account-manager/index.d.ts.map +0 -1
  196. package/dist/account-manager/index.js.map +0 -1
  197. package/dist/actor-store/repo/util.d.ts +0 -5
  198. package/dist/actor-store/repo/util.d.ts.map +0 -1
  199. package/dist/actor-store/repo/util.js +0 -25
  200. package/dist/actor-store/repo/util.js.map +0 -1
  201. package/dist/oauth/provider.d.ts +0 -10
  202. package/dist/oauth/provider.d.ts.map +0 -1
  203. package/dist/oauth/provider.js +0 -38
  204. package/dist/oauth/provider.js.map +0 -1
  205. package/src/actor-store/repo/util.ts +0 -22
  206. package/src/oauth/provider.ts +0 -59
@@ -1,67 +1,43 @@
1
1
  import { KeyObject } from 'node:crypto'
2
- import { Selectable } from 'kysely'
3
2
  import { CID } from 'multiformats/cid'
4
3
  import { HOUR, wait } from '@atproto/common'
5
- import {
6
- Account,
7
- AccountInfo,
8
- AccountStore,
9
- Code,
10
- DeviceData,
11
- DeviceId,
12
- DeviceStore,
13
- FoundRequestResult,
14
- NewTokenData,
15
- RefreshToken,
16
- RequestData,
17
- RequestId,
18
- RequestStore,
19
- SignInCredentials,
20
- TokenData,
21
- TokenId,
22
- TokenInfo,
23
- TokenStore,
24
- UpdateRequestData,
25
- } from '@atproto/oauth-provider'
26
- import { AuthRequiredError } from '@atproto/xrpc-server'
27
- import { ActorStore } from '../actor-store/actor-store'
4
+ import { IdResolver } from '@atproto/identity'
5
+ import { isValidTld } from '@atproto/syntax'
6
+ import { AuthRequiredError, InvalidRequestError } from '@atproto/xrpc-server'
28
7
  import { AuthScope } from '../auth-verifier'
29
- import { BackgroundQueue } from '../background'
8
+ import { DatabaseConfig } from '../config'
30
9
  import { softDeleted } from '../db'
31
- import { ImageUrlBuilder } from '../image/image-url-builder'
10
+ import { hasExplicitSlur } from '../handle/explicit-slurs'
11
+ import {
12
+ baseNormalizeAndValidate,
13
+ ensureHandleServiceConstraints,
14
+ isServiceDomain,
15
+ } from '../handle/index'
32
16
  import { StatusAttr } from '../lexicon/types/com/atproto/admin/defs'
33
17
  import { AccountDb, EmailTokenPurpose, getDb, getMigrator } from './db'
34
18
  import * as account from './helpers/account'
35
19
  import { AccountStatus, ActorAccount } from './helpers/account'
36
20
  import * as auth from './helpers/auth'
37
- import * as authRequest from './helpers/authorization-request'
38
- import * as device from './helpers/device'
39
- import * as deviceAccount from './helpers/device-account'
40
21
  import * as emailToken from './helpers/email-token'
41
22
  import * as invite from './helpers/invite'
42
23
  import * as password from './helpers/password'
43
24
  import * as repo from './helpers/repo'
44
25
  import * as scrypt from './helpers/scrypt'
45
26
  import * as token from './helpers/token'
46
- import * as usedRefreshToken from './helpers/used-refresh-token'
47
27
 
48
28
  export { AccountStatus, formatAccountStatus } from './helpers/account'
49
29
 
50
- export class AccountManager
51
- implements AccountStore, RequestStore, DeviceStore, TokenStore
52
- {
53
- db: AccountDb
30
+ export class AccountManager {
31
+ readonly db: AccountDb
54
32
 
55
33
  constructor(
56
- private actorStore: ActorStore,
57
- private imageUrlBuilder: ImageUrlBuilder,
58
- private backgroundQueue: BackgroundQueue,
59
- dbLocation: string,
60
- private jwtKey: KeyObject,
61
- private serviceDid: string,
62
- disableWalAutoCheckpoint = false,
34
+ readonly idResolver: IdResolver,
35
+ readonly jwtKey: KeyObject,
36
+ readonly serviceDid: string,
37
+ readonly serviceHandleDomains: string[],
38
+ db: DatabaseConfig,
63
39
  ) {
64
- this.db = getDb(dbLocation, disableWalAutoCheckpoint)
40
+ this.db = getDb(db.accountDbLoc, db.disableWalAutoCheckpoint)
65
41
  }
66
42
 
67
43
  async migrateOrThrow() {
@@ -121,7 +97,67 @@ export class AccountManager
121
97
  return res.active ? AccountStatus.Active : res.status
122
98
  }
123
99
 
124
- async createAccount(opts: {
100
+ async normalizeAndValidateHandle(
101
+ handle: string,
102
+ {
103
+ did,
104
+ allowAnyValid,
105
+ }: {
106
+ did?: string
107
+ allowAnyValid?: boolean
108
+ } = {},
109
+ ): Promise<string> {
110
+ const normalized = baseNormalizeAndValidate(handle)
111
+
112
+ // tld validation
113
+ if (!isValidTld(normalized)) {
114
+ throw new InvalidRequestError(
115
+ 'Handle TLD is invalid or disallowed',
116
+ 'InvalidHandle',
117
+ )
118
+ }
119
+ // slur check
120
+ if (!allowAnyValid && hasExplicitSlur(normalized)) {
121
+ throw new InvalidRequestError(
122
+ 'Inappropriate language in handle',
123
+ 'InvalidHandle',
124
+ )
125
+ }
126
+ if (isServiceDomain(normalized, this.serviceHandleDomains)) {
127
+ // verify constraints on a service domain
128
+ ensureHandleServiceConstraints(
129
+ normalized,
130
+ this.serviceHandleDomains,
131
+ allowAnyValid,
132
+ )
133
+ } else {
134
+ if (did == null) {
135
+ throw new InvalidRequestError(
136
+ 'Not a supported handle domain',
137
+ 'UnsupportedDomain',
138
+ )
139
+ }
140
+ // verify resolution of a non-service domain
141
+ const resolvedDid = await this.idResolver.handle.resolve(normalized)
142
+ if (resolvedDid !== did) {
143
+ throw new InvalidRequestError('External handle did not resolve to DID')
144
+ }
145
+ }
146
+
147
+ return normalized
148
+ }
149
+
150
+ async createAccount({
151
+ did,
152
+ handle,
153
+ email,
154
+ password,
155
+ repoCid,
156
+ repoRev,
157
+ inviteCode,
158
+ deactivated,
159
+ refreshJwt,
160
+ }: {
125
161
  did: string
126
162
  handle: string
127
163
  email?: string
@@ -130,28 +166,12 @@ export class AccountManager
130
166
  repoRev: string
131
167
  inviteCode?: string
132
168
  deactivated?: boolean
169
+ refreshJwt?: string
133
170
  }) {
134
- const {
135
- did,
136
- handle,
137
- email,
138
- password,
139
- repoCid,
140
- repoRev,
141
- inviteCode,
142
- deactivated,
143
- } = opts
144
171
  const passwordScrypt = password
145
172
  ? await scrypt.genSaltAndHash(password)
146
173
  : undefined
147
174
 
148
- const { accessJwt, refreshJwt } = await auth.createTokens({
149
- did,
150
- jwtKey: this.jwtKey,
151
- serviceDid: this.serviceDid,
152
- scope: AuthScope.Access,
153
- })
154
- const refreshPayload = auth.decodeRefreshToken(refreshJwt)
155
175
  const now = new Date().toISOString()
156
176
  await this.db.transaction(async (dbTxn) => {
157
177
  if (inviteCode) {
@@ -167,10 +187,36 @@ export class AccountManager
167
187
  inviteCode,
168
188
  now,
169
189
  }),
170
- auth.storeRefreshToken(dbTxn, refreshPayload, null),
190
+ refreshJwt &&
191
+ auth.storeRefreshToken(
192
+ dbTxn,
193
+ auth.decodeRefreshToken(refreshJwt),
194
+ null,
195
+ ),
171
196
  repo.updateRoot(dbTxn, did, repoCid, repoRev),
172
197
  ])
173
198
  })
199
+ }
200
+
201
+ async createAccountAndSession(opts: {
202
+ did: string
203
+ handle: string
204
+ email?: string
205
+ password?: string
206
+ repoCid: CID
207
+ repoRev: string
208
+ inviteCode?: string
209
+ deactivated?: boolean
210
+ }) {
211
+ const { accessJwt, refreshJwt } = await auth.createTokens({
212
+ did: opts.did,
213
+ jwtKey: this.jwtKey,
214
+ serviceDid: this.serviceDid,
215
+ scope: AuthScope.Access,
216
+ })
217
+
218
+ await this.createAccount({ ...opts, refreshJwt })
219
+
174
220
  return { accessJwt, refreshJwt }
175
221
  }
176
222
 
@@ -502,250 +548,4 @@ export class AccountManager
502
548
  ]),
503
549
  )
504
550
  }
505
-
506
- // AccountStore
507
-
508
- private async buildAccount(row: Selectable<ActorAccount>): Promise<Account> {
509
- const account = deviceAccount.toAccount(row, this.serviceDid)
510
-
511
- if (!account.name || !account.picture) {
512
- const did = account.sub
513
-
514
- const profile = await this.actorStore.read(did, async (store) => {
515
- return store.record.getProfileRecord()
516
- })
517
-
518
- if (profile) {
519
- const { avatar, displayName } = profile
520
-
521
- account.name ||= displayName
522
- account.picture ||= avatar
523
- ? this.imageUrlBuilder.build('avatar', did, avatar.ref.toString())
524
- : undefined
525
- }
526
- }
527
-
528
- return account
529
- }
530
-
531
- async authenticateAccount(
532
- { username: identifier, password, remember = false }: SignInCredentials,
533
- deviceId: DeviceId,
534
- ): Promise<AccountInfo | null> {
535
- try {
536
- const { user, appPassword } = await this.login({ identifier, password })
537
-
538
- if (appPassword) {
539
- throw new AuthRequiredError('App passwords are not allowed')
540
- }
541
-
542
- await this.db.executeWithRetry(
543
- deviceAccount.createOrUpdateQB(this.db, deviceId, user.did, remember),
544
- )
545
-
546
- return await this.getDeviceAccount(deviceId, user.did)
547
- } catch (err) {
548
- if (err instanceof AuthRequiredError) return null
549
- throw err
550
- }
551
- }
552
-
553
- async addAuthorizedClient(
554
- deviceId: DeviceId,
555
- sub: string,
556
- clientId: string,
557
- ): Promise<void> {
558
- await this.db.transaction(async (dbTxn) => {
559
- const row = await deviceAccount
560
- .readQB(dbTxn, deviceId, sub)
561
- .executeTakeFirstOrThrow()
562
-
563
- const { authorizedClients } = deviceAccount.toDeviceAccountInfo(row)
564
- if (!authorizedClients.includes(clientId)) {
565
- await deviceAccount
566
- .updateQB(dbTxn, deviceId, sub, {
567
- authorizedClients: [...authorizedClients, clientId],
568
- })
569
- .execute()
570
- }
571
- })
572
- }
573
-
574
- async getDeviceAccount(
575
- deviceId: DeviceId,
576
- sub: string,
577
- ): Promise<AccountInfo | null> {
578
- const row = await deviceAccount
579
- .getAccountInfoQB(this.db, deviceId, sub)
580
- .executeTakeFirst()
581
-
582
- if (!row) return null
583
-
584
- return {
585
- account: await this.buildAccount(row),
586
- info: deviceAccount.toDeviceAccountInfo(row),
587
- }
588
- }
589
-
590
- async listDeviceAccounts(deviceId: DeviceId): Promise<AccountInfo[]> {
591
- const rows = await deviceAccount
592
- .listRememberedQB(this.db, deviceId)
593
- .execute()
594
-
595
- return Promise.all(
596
- rows.map(async (row) => ({
597
- account: await this.buildAccount(row),
598
- info: deviceAccount.toDeviceAccountInfo(row),
599
- })),
600
- )
601
- }
602
-
603
- async removeDeviceAccount(deviceId: DeviceId, sub: string): Promise<void> {
604
- await this.db.executeWithRetry(
605
- deviceAccount.removeQB(this.db, deviceId, sub),
606
- )
607
- }
608
-
609
- // RequestStore
610
-
611
- async createRequest(id: RequestId, data: RequestData): Promise<void> {
612
- await this.db.executeWithRetry(authRequest.createQB(this.db, id, data))
613
- }
614
-
615
- async readRequest(id: RequestId): Promise<RequestData | null> {
616
- try {
617
- const row = await authRequest.readQB(this.db, id).executeTakeFirst()
618
- if (!row) return null
619
- return authRequest.rowToRequestData(row)
620
- } finally {
621
- // Take the opportunity to clean up expired requests. Do this after we got
622
- // the current (potentially expired) request data to allow the provider to
623
- // handle expired requests.
624
- this.backgroundQueue.add(async () => {
625
- await this.db.executeWithRetry(authRequest.removeOldExpiredQB(this.db))
626
- })
627
- }
628
- }
629
-
630
- async updateRequest(id: RequestId, data: UpdateRequestData): Promise<void> {
631
- await this.db.executeWithRetry(authRequest.updateQB(this.db, id, data))
632
- }
633
-
634
- async deleteRequest(id: RequestId): Promise<void> {
635
- await this.db.executeWithRetry(authRequest.removeByIdQB(this.db, id))
636
- }
637
-
638
- async findRequestByCode(code: Code): Promise<FoundRequestResult | null> {
639
- const row = await authRequest.findByCodeQB(this.db, code).executeTakeFirst()
640
- return row ? authRequest.rowToFoundRequestResult(row) : null
641
- }
642
-
643
- // DeviceStore
644
-
645
- async createDevice(deviceId: DeviceId, data: DeviceData): Promise<void> {
646
- await this.db.executeWithRetry(device.createQB(this.db, deviceId, data))
647
- }
648
-
649
- async readDevice(deviceId: DeviceId): Promise<null | DeviceData> {
650
- const row = await device.readQB(this.db, deviceId).executeTakeFirst()
651
- return row ? device.rowToDeviceData(row) : null
652
- }
653
-
654
- async updateDevice(
655
- deviceId: DeviceId,
656
- data: Partial<DeviceData>,
657
- ): Promise<void> {
658
- await this.db.executeWithRetry(device.updateQB(this.db, deviceId, data))
659
- }
660
-
661
- async deleteDevice(deviceId: DeviceId): Promise<void> {
662
- // Will cascade to device_account (device_account_device_id_fk)
663
- await this.db.executeWithRetry(device.removeQB(this.db, deviceId))
664
- }
665
-
666
- // TokenStore
667
-
668
- async createToken(
669
- id: TokenId,
670
- data: TokenData,
671
- refreshToken?: RefreshToken,
672
- ): Promise<void> {
673
- await this.db.transaction(async (dbTxn) => {
674
- if (refreshToken) {
675
- const { count } = await usedRefreshToken
676
- .countQB(dbTxn, refreshToken)
677
- .executeTakeFirstOrThrow()
678
-
679
- if (count > 0) {
680
- throw new Error('Refresh token already in use')
681
- }
682
- }
683
-
684
- return token.createQB(dbTxn, id, data, refreshToken).execute()
685
- })
686
- }
687
-
688
- async readToken(tokenId: TokenId): Promise<TokenInfo | null> {
689
- const row = await token.findByQB(this.db, { tokenId }).executeTakeFirst()
690
- return row ? token.toTokenInfo(row, this.serviceDid) : null
691
- }
692
-
693
- async deleteToken(tokenId: TokenId): Promise<void> {
694
- // Will cascade to used_refresh_token (used_refresh_token_fk)
695
- await this.db.executeWithRetry(token.removeQB(this.db, tokenId))
696
- }
697
-
698
- async rotateToken(
699
- tokenId: TokenId,
700
- newTokenId: TokenId,
701
- newRefreshToken: RefreshToken,
702
- newData: NewTokenData,
703
- ): Promise<void> {
704
- const err = await this.db.transaction(async (dbTxn) => {
705
- const { id, currentRefreshToken } = await token
706
- .forRotateQB(dbTxn, tokenId)
707
- .executeTakeFirstOrThrow()
708
-
709
- if (currentRefreshToken) {
710
- await usedRefreshToken
711
- .insertQB(dbTxn, id, currentRefreshToken)
712
- .execute()
713
- }
714
-
715
- const { count } = await usedRefreshToken
716
- .countQB(dbTxn, newRefreshToken)
717
- .executeTakeFirstOrThrow()
718
-
719
- if (count > 0) {
720
- // Do NOT throw (we don't want the transaction to be rolled back)
721
- return new Error('New refresh token already in use')
722
- }
723
-
724
- await token
725
- .rotateQB(dbTxn, id, newTokenId, newRefreshToken, newData)
726
- .execute()
727
- })
728
-
729
- if (err) throw err
730
- }
731
-
732
- async findTokenByRefreshToken(
733
- refreshToken: RefreshToken,
734
- ): Promise<TokenInfo | null> {
735
- const used = await usedRefreshToken
736
- .findByTokenQB(this.db, refreshToken)
737
- .executeTakeFirst()
738
-
739
- const search = used
740
- ? { id: used.tokenId }
741
- : { currentRefreshToken: refreshToken }
742
-
743
- const row = await token.findByQB(this.db, search).executeTakeFirst()
744
- return row ? token.toTokenInfo(row, this.serviceDid) : null
745
- }
746
-
747
- async findTokenByCode(code: Code): Promise<TokenInfo | null> {
748
- const row = await token.findByQB(this.db, { code }).executeTakeFirst()
749
- return row ? token.toTokenInfo(row, this.serviceDid) : null
750
- }
751
551
  }
@@ -114,6 +114,7 @@ export const createOrUpdateQB = (
114
114
  .insertInto('device_account')
115
115
  .values({ did, deviceId, authorizedClients, ...values })
116
116
  .onConflict((oc) => oc.columns(['deviceId', 'did']).doUpdateSet(values))
117
+ .returning(['remember', 'authorizedClients', 'authenticatedAt'])
117
118
  }
118
119
 
119
120
  export const getAccountInfoQB = (