@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.
- package/CHANGELOG.md +22 -0
- package/dist/account-manager/{index.d.ts → account-manager.d.ts} +26 -35
- package/dist/account-manager/account-manager.d.ts.map +1 -0
- package/dist/account-manager/{index.js → account-manager.js} +52 -207
- package/dist/account-manager/account-manager.js.map +1 -0
- package/dist/account-manager/helpers/account.d.ts +3 -3
- package/dist/account-manager/helpers/device-account.d.ts +15 -15
- package/dist/account-manager/helpers/device-account.d.ts.map +1 -1
- package/dist/account-manager/helpers/device-account.js +2 -1
- package/dist/account-manager/helpers/device-account.js.map +1 -1
- package/dist/account-manager/helpers/token.d.ts +98 -98
- package/dist/account-manager/oauth-store.d.ts +58 -0
- package/dist/account-manager/oauth-store.d.ts.map +1 -0
- package/dist/account-manager/oauth-store.js +417 -0
- package/dist/account-manager/oauth-store.js.map +1 -0
- package/dist/actor-store/record/reader.d.ts +3 -3
- package/dist/actor-store/repo/reader.d.ts +2 -0
- package/dist/actor-store/repo/reader.d.ts.map +1 -1
- package/dist/actor-store/repo/reader.js +9 -0
- package/dist/actor-store/repo/reader.js.map +1 -1
- package/dist/actor-store/repo/sql-repo-reader.d.ts +1 -1
- package/dist/actor-store/repo/transactor.d.ts.map +1 -1
- package/dist/actor-store/repo/transactor.js +13 -4
- package/dist/actor-store/repo/transactor.js.map +1 -1
- package/dist/api/com/atproto/admin/deleteAccount.d.ts.map +1 -1
- package/dist/api/com/atproto/admin/deleteAccount.js +2 -3
- package/dist/api/com/atproto/admin/deleteAccount.js.map +1 -1
- package/dist/api/com/atproto/admin/updateAccountHandle.d.ts.map +1 -1
- package/dist/api/com/atproto/admin/updateAccountHandle.js +2 -6
- package/dist/api/com/atproto/admin/updateAccountHandle.js.map +1 -1
- package/dist/api/com/atproto/identity/resolveHandle.d.ts.map +1 -1
- package/dist/api/com/atproto/identity/resolveHandle.js +2 -36
- package/dist/api/com/atproto/identity/resolveHandle.js.map +1 -1
- package/dist/api/com/atproto/identity/updateHandle.d.ts.map +1 -1
- package/dist/api/com/atproto/identity/updateHandle.js +1 -7
- package/dist/api/com/atproto/identity/updateHandle.js.map +1 -1
- package/dist/api/com/atproto/server/activateAccount.d.ts.map +1 -1
- package/dist/api/com/atproto/server/activateAccount.js +2 -18
- package/dist/api/com/atproto/server/activateAccount.js.map +1 -1
- package/dist/api/com/atproto/server/createAccount.d.ts.map +1 -1
- package/dist/api/com/atproto/server/createAccount.js +5 -7
- package/dist/api/com/atproto/server/createAccount.js.map +1 -1
- package/dist/api/com/atproto/server/createSession.js +1 -1
- package/dist/api/com/atproto/server/createSession.js.map +1 -1
- package/dist/api/com/atproto/server/deleteAccount.d.ts.map +1 -1
- package/dist/api/com/atproto/server/deleteAccount.js +2 -3
- package/dist/api/com/atproto/server/deleteAccount.js.map +1 -1
- package/dist/api/com/atproto/server/getSession.js +1 -1
- package/dist/api/com/atproto/server/getSession.js.map +1 -1
- package/dist/api/com/atproto/server/refreshSession.js +1 -1
- package/dist/api/com/atproto/server/refreshSession.js.map +1 -1
- package/dist/api/com/atproto/sync/getRecord.d.ts.map +1 -1
- package/dist/api/com/atproto/sync/getRecord.js.map +1 -1
- package/dist/api/com/atproto/sync/getRepoStatus.js +1 -1
- package/dist/api/com/atproto/sync/getRepoStatus.js.map +1 -1
- package/dist/api/com/atproto/sync/listRepos.js +1 -1
- package/dist/api/com/atproto/sync/listRepos.js.map +1 -1
- package/dist/api/com/atproto/sync/subscribeRepos.d.ts.map +1 -1
- package/dist/api/com/atproto/sync/subscribeRepos.js +2 -10
- package/dist/api/com/atproto/sync/subscribeRepos.js.map +1 -1
- package/dist/app-view.d.ts +14 -0
- package/dist/app-view.d.ts.map +1 -0
- package/dist/app-view.js +36 -0
- package/dist/app-view.js.map +1 -0
- package/dist/auth-routes.d.ts +1 -1
- package/dist/auth-routes.d.ts.map +1 -1
- package/dist/auth-routes.js +9 -3
- package/dist/auth-routes.js.map +1 -1
- package/dist/auth-verifier.d.ts +1 -1
- package/dist/auth-verifier.d.ts.map +1 -1
- package/dist/config/config.d.ts +3 -2
- package/dist/config/config.d.ts.map +1 -1
- package/dist/config/config.js +17 -7
- package/dist/config/config.js.map +1 -1
- package/dist/config/env.d.ts +4 -0
- package/dist/config/env.d.ts.map +1 -1
- package/dist/config/env.js +5 -0
- package/dist/config/env.js.map +1 -1
- package/dist/context.d.ts +4 -4
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +24 -18
- package/dist/context.js.map +1 -1
- package/dist/handle/index.d.ts +0 -7
- package/dist/handle/index.d.ts.map +1 -1
- package/dist/handle/index.js +4 -58
- package/dist/handle/index.js.map +1 -1
- package/dist/image/image-url.d.ts +8 -0
- package/dist/image/image-url.d.ts.map +1 -0
- package/dist/image/image-url.js +26 -0
- package/dist/image/image-url.js.map +1 -0
- package/dist/lexicon/index.d.ts +6 -0
- package/dist/lexicon/index.d.ts.map +1 -1
- package/dist/lexicon/index.js +12 -0
- package/dist/lexicon/index.js.map +1 -1
- package/dist/lexicon/lexicons.d.ts +304 -130
- package/dist/lexicon/lexicons.d.ts.map +1 -1
- package/dist/lexicon/lexicons.js +168 -67
- package/dist/lexicon/lexicons.js.map +1 -1
- package/dist/lexicon/types/app/bsky/embed/video.d.ts +1 -0
- package/dist/lexicon/types/app/bsky/embed/video.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/embed/video.js.map +1 -1
- package/dist/lexicon/types/com/atproto/identity/defs.d.ts +17 -0
- package/dist/lexicon/types/com/atproto/identity/defs.d.ts.map +1 -0
- package/dist/lexicon/types/com/atproto/identity/defs.js +16 -0
- package/dist/lexicon/types/com/atproto/identity/defs.js.map +1 -0
- package/dist/lexicon/types/com/atproto/identity/refreshIdentity.d.ts +39 -0
- package/dist/lexicon/types/com/atproto/identity/refreshIdentity.d.ts.map +1 -0
- package/dist/lexicon/types/com/atproto/identity/refreshIdentity.js +7 -0
- package/dist/lexicon/types/com/atproto/identity/refreshIdentity.js.map +1 -0
- package/dist/lexicon/types/com/atproto/identity/resolveDid.d.ts +40 -0
- package/dist/lexicon/types/com/atproto/identity/resolveDid.d.ts.map +1 -0
- package/dist/lexicon/types/com/atproto/identity/resolveDid.js +7 -0
- package/dist/lexicon/types/com/atproto/identity/resolveDid.js.map +1 -0
- package/dist/lexicon/types/com/atproto/identity/resolveHandle.d.ts +1 -0
- package/dist/lexicon/types/com/atproto/identity/resolveHandle.d.ts.map +1 -1
- package/dist/lexicon/types/com/atproto/identity/resolveIdentity.d.ts +36 -0
- package/dist/lexicon/types/com/atproto/identity/resolveIdentity.d.ts.map +1 -0
- package/dist/lexicon/types/com/atproto/identity/resolveIdentity.js +7 -0
- package/dist/lexicon/types/com/atproto/identity/resolveIdentity.js.map +1 -0
- package/dist/lexicon/types/com/atproto/sync/subscribeRepos.d.ts +1 -30
- package/dist/lexicon/types/com/atproto/sync/subscribeRepos.d.ts.map +1 -1
- package/dist/lexicon/types/com/atproto/sync/subscribeRepos.js +0 -27
- package/dist/lexicon/types/com/atproto/sync/subscribeRepos.js.map +1 -1
- package/dist/mailer/index.d.ts +5 -5
- package/dist/mailer/index.d.ts.map +1 -1
- package/dist/mailer/index.js +6 -5
- package/dist/mailer/index.js.map +1 -1
- package/dist/read-after-write/viewer.d.ts +1 -1
- package/dist/read-after-write/viewer.d.ts.map +1 -1
- package/dist/repo/types.d.ts +6 -2
- package/dist/repo/types.d.ts.map +1 -1
- package/dist/repo/types.js.map +1 -1
- package/dist/scripts/rebuild-repo.d.ts.map +1 -1
- package/dist/scripts/rebuild-repo.js +2 -1
- package/dist/scripts/rebuild-repo.js.map +1 -1
- package/dist/sequencer/db/schema.d.ts +1 -1
- package/dist/sequencer/db/schema.d.ts.map +1 -1
- package/dist/sequencer/events.d.ts +29 -41
- package/dist/sequencer/events.d.ts.map +1 -1
- package/dist/sequencer/events.js +24 -58
- package/dist/sequencer/events.js.map +1 -1
- package/dist/sequencer/sequencer.d.ts +2 -3
- package/dist/sequencer/sequencer.d.ts.map +1 -1
- package/dist/sequencer/sequencer.js +5 -17
- package/dist/sequencer/sequencer.js.map +1 -1
- package/package.json +15 -15
- package/src/account-manager/{index.ts → account-manager.ts} +107 -307
- package/src/account-manager/helpers/device-account.ts +1 -0
- package/src/account-manager/oauth-store.ts +494 -0
- package/src/actor-store/repo/reader.ts +11 -0
- package/src/actor-store/repo/transactor.ts +15 -4
- package/src/api/com/atproto/admin/deleteAccount.ts +2 -3
- package/src/api/com/atproto/admin/updateAccountHandle.ts +7 -8
- package/src/api/com/atproto/identity/resolveHandle.ts +2 -11
- package/src/api/com/atproto/identity/updateHandle.ts +4 -7
- package/src/api/com/atproto/server/activateAccount.ts +4 -18
- package/src/api/com/atproto/server/createAccount.ts +10 -11
- package/src/api/com/atproto/server/createSession.ts +1 -1
- package/src/api/com/atproto/server/deleteAccount.ts +2 -3
- package/src/api/com/atproto/server/getSession.ts +1 -1
- package/src/api/com/atproto/server/refreshSession.ts +1 -1
- package/src/api/com/atproto/sync/getRecord.ts +0 -1
- package/src/api/com/atproto/sync/getRepoStatus.ts +1 -1
- package/src/api/com/atproto/sync/listRepos.ts +1 -1
- package/src/api/com/atproto/sync/subscribeRepos.ts +2 -9
- package/src/app-view.ts +24 -0
- package/src/auth-routes.ts +9 -3
- package/src/auth-verifier.ts +1 -1
- package/src/config/config.ts +25 -13
- package/src/config/env.ts +12 -0
- package/src/context.ts +44 -24
- package/src/handle/index.ts +6 -52
- package/src/image/image-url.ts +16 -0
- package/src/lexicon/index.ts +36 -0
- package/src/lexicon/lexicons.ts +183 -67
- package/src/lexicon/types/app/bsky/embed/video.ts +1 -0
- package/src/lexicon/types/com/atproto/identity/defs.ts +30 -0
- package/src/lexicon/types/com/atproto/identity/refreshIdentity.ts +52 -0
- package/src/lexicon/types/com/atproto/identity/resolveDid.ts +52 -0
- package/src/lexicon/types/com/atproto/identity/resolveHandle.ts +1 -0
- package/src/lexicon/types/com/atproto/identity/resolveIdentity.ts +48 -0
- package/src/lexicon/types/com/atproto/sync/subscribeRepos.ts +0 -59
- package/src/mailer/index.ts +7 -5
- package/src/read-after-write/viewer.ts +1 -1
- package/src/repo/types.ts +7 -2
- package/src/scripts/rebuild-repo.ts +4 -1
- package/src/sequencer/db/schema.ts +1 -8
- package/src/sequencer/events.ts +29 -75
- package/src/sequencer/sequencer.ts +9 -23
- package/tests/account-deletion.test.ts +3 -5
- package/tests/oauth.test.ts +286 -71
- package/tests/sequencer.test.ts +18 -27
- package/tests/sync/subscribe-repos.test.ts +67 -45
- package/tsconfig.build.tsbuildinfo +1 -1
- package/dist/account-manager/index.d.ts.map +0 -1
- package/dist/account-manager/index.js.map +0 -1
- package/dist/actor-store/repo/util.d.ts +0 -5
- package/dist/actor-store/repo/util.d.ts.map +0 -1
- package/dist/actor-store/repo/util.js +0 -25
- package/dist/actor-store/repo/util.js.map +0 -1
- package/dist/oauth/provider.d.ts +0 -10
- package/dist/oauth/provider.d.ts.map +0 -1
- package/dist/oauth/provider.js +0 -38
- package/dist/oauth/provider.js.map +0 -1
- package/src/actor-store/repo/util.ts +0 -22
- 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
|
-
|
7
|
-
|
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 {
|
8
|
+
import { DatabaseConfig } from '../config'
|
30
9
|
import { softDeleted } from '../db'
|
31
|
-
import {
|
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
|
-
|
52
|
-
{
|
53
|
-
db: AccountDb
|
30
|
+
export class AccountManager {
|
31
|
+
readonly db: AccountDb
|
54
32
|
|
55
33
|
constructor(
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
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(
|
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
|
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
|
-
|
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 = (
|