@atproto/pds 0.4.122 → 0.4.124
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 +16 -0
- package/dist/account-manager/account-manager.js +17 -7
- package/dist/account-manager/account-manager.js.map +1 -1
- package/dist/account-manager/db/index.d.ts.map +1 -1
- package/dist/account-manager/db/migrations/005-oauth-account-management.d.ts +20 -0
- package/dist/account-manager/db/migrations/005-oauth-account-management.d.ts.map +1 -0
- package/dist/account-manager/db/migrations/005-oauth-account-management.js +72 -0
- package/dist/account-manager/db/migrations/005-oauth-account-management.js.map +1 -0
- package/dist/account-manager/db/migrations/index.d.ts +2 -0
- package/dist/account-manager/db/migrations/index.d.ts.map +1 -1
- package/dist/account-manager/db/migrations/index.js +19 -7
- package/dist/account-manager/db/migrations/index.js.map +1 -1
- package/dist/account-manager/db/schema/account-device.d.ts +13 -0
- package/dist/account-manager/db/schema/account-device.d.ts.map +1 -0
- package/dist/account-manager/db/schema/{device-account.js → account-device.js} +2 -2
- package/dist/account-manager/db/schema/account-device.js.map +1 -0
- package/dist/account-manager/db/schema/authorization-request.d.ts +4 -4
- package/dist/account-manager/db/schema/authorization-request.d.ts.map +1 -1
- package/dist/account-manager/db/schema/authorization-request.js.map +1 -1
- package/dist/account-manager/db/schema/authorized-client.d.ts +16 -0
- package/dist/account-manager/db/schema/authorized-client.d.ts.map +1 -0
- package/dist/account-manager/db/schema/authorized-client.js +5 -0
- package/dist/account-manager/db/schema/authorized-client.js.map +1 -0
- package/dist/account-manager/db/schema/index.d.ts +4 -3
- package/dist/account-manager/db/schema/index.d.ts.map +1 -1
- package/dist/account-manager/db/schema/token.d.ts +5 -5
- package/dist/account-manager/db/schema/token.d.ts.map +1 -1
- package/dist/account-manager/db/schema/token.js.map +1 -1
- package/dist/account-manager/helpers/account-device.d.ts +204 -0
- package/dist/account-manager/helpers/account-device.d.ts.map +1 -0
- package/dist/account-manager/helpers/account-device.js +54 -0
- package/dist/account-manager/helpers/account-device.js.map +1 -0
- package/dist/account-manager/helpers/account.d.ts +2 -1
- package/dist/account-manager/helpers/account.d.ts.map +1 -1
- package/dist/account-manager/helpers/auth.d.ts.map +1 -1
- package/dist/account-manager/helpers/auth.js +17 -7
- package/dist/account-manager/helpers/auth.js.map +1 -1
- package/dist/account-manager/helpers/authorization-request.d.ts.map +1 -1
- package/dist/account-manager/helpers/authorization-request.js +4 -4
- package/dist/account-manager/helpers/authorization-request.js.map +1 -1
- package/dist/account-manager/helpers/authorized-client.d.ts +6 -0
- package/dist/account-manager/helpers/authorized-client.d.ts.map +1 -0
- package/dist/account-manager/helpers/authorized-client.js +47 -0
- package/dist/account-manager/helpers/authorized-client.js.map +1 -0
- package/dist/account-manager/helpers/device.d.ts +1 -1
- package/dist/account-manager/helpers/device.d.ts.map +1 -1
- package/dist/account-manager/helpers/device.js.map +1 -1
- package/dist/account-manager/helpers/email-token.d.ts.map +1 -1
- package/dist/account-manager/helpers/invite.d.ts.map +1 -1
- package/dist/account-manager/helpers/password.d.ts.map +1 -1
- package/dist/account-manager/helpers/password.js +17 -7
- package/dist/account-manager/helpers/password.js.map +1 -1
- package/dist/account-manager/helpers/repo.d.ts.map +1 -1
- package/dist/account-manager/helpers/scrypt.d.ts.map +1 -1
- package/dist/account-manager/helpers/scrypt.js +17 -7
- package/dist/account-manager/helpers/scrypt.js.map +1 -1
- package/dist/account-manager/helpers/token.d.ts +566 -59
- package/dist/account-manager/helpers/token.d.ts.map +1 -1
- package/dist/account-manager/helpers/token.js +17 -32
- package/dist/account-manager/helpers/token.js.map +1 -1
- package/dist/account-manager/helpers/used-refresh-token.d.ts.map +1 -1
- package/dist/account-manager/oauth-store.d.ts +17 -7
- package/dist/account-manager/oauth-store.d.ts.map +1 -1
- package/dist/account-manager/oauth-store.js +138 -86
- package/dist/account-manager/oauth-store.js.map +1 -1
- package/dist/actor-store/actor-store.js +17 -7
- package/dist/actor-store/actor-store.js.map +1 -1
- package/dist/actor-store/blob/transactor.js +17 -7
- package/dist/actor-store/blob/transactor.js.map +1 -1
- package/dist/actor-store/db/index.d.ts.map +1 -1
- package/dist/actor-store/db/migrations/index.js +17 -7
- package/dist/actor-store/db/migrations/index.js.map +1 -1
- package/dist/actor-store/migrate.d.ts.map +1 -1
- package/dist/actor-store/preference/reader.d.ts.map +1 -1
- package/dist/actor-store/preference/util.d.ts.map +1 -1
- package/dist/actor-store/record/reader.d.ts.map +1 -1
- package/dist/actor-store/record/reader.js +17 -7
- package/dist/actor-store/record/reader.js.map +1 -1
- package/dist/actor-store/repo/sql-repo-reader.d.ts +1 -1
- package/dist/api/app/bsky/util/resolver.d.ts.map +1 -1
- package/dist/api/com/atproto/identity/signPlcOperation.js +17 -7
- package/dist/api/com/atproto/identity/signPlcOperation.js.map +1 -1
- package/dist/api/com/atproto/identity/submitPlcOperation.js +17 -7
- package/dist/api/com/atproto/identity/submitPlcOperation.js.map +1 -1
- package/dist/api/com/atproto/repo/describeRepo.js +17 -7
- package/dist/api/com/atproto/repo/describeRepo.js.map +1 -1
- package/dist/api/com/atproto/repo/importRepo.d.ts.map +1 -1
- package/dist/api/com/atproto/server/createAccount.js +17 -7
- package/dist/api/com/atproto/server/createAccount.js.map +1 -1
- package/dist/api/com/atproto/server/util.d.ts.map +1 -1
- package/dist/api/com/atproto/server/util.js +17 -7
- package/dist/api/com/atproto/server/util.js.map +1 -1
- package/dist/api/com/atproto/sync/getRecord.js +17 -7
- package/dist/api/com/atproto/sync/getRecord.js.map +1 -1
- package/dist/api/com/atproto/sync/getRepo.d.ts.map +1 -1
- package/dist/api/com/atproto/sync/util.d.ts.map +1 -1
- package/dist/api/proxy.d.ts.map +1 -1
- package/dist/auth-routes.d.ts.map +1 -1
- package/dist/auth-routes.js +2 -3
- package/dist/auth-routes.js.map +1 -1
- package/dist/auth-verifier.d.ts.map +1 -1
- package/dist/auth-verifier.js +19 -13
- package/dist/auth-verifier.js.map +1 -1
- package/dist/basic-routes.d.ts.map +1 -1
- package/dist/config/config.d.ts.map +1 -1
- package/dist/config/config.js +1 -1
- package/dist/config/config.js.map +1 -1
- package/dist/config/env.d.ts +1 -1
- package/dist/config/env.d.ts.map +1 -1
- package/dist/config/env.js +1 -1
- package/dist/config/env.js.map +1 -1
- package/dist/config/secrets.d.ts.map +1 -1
- package/dist/context.js +18 -8
- package/dist/context.js.map +1 -1
- package/dist/db/cast.d.ts +17 -13
- package/dist/db/cast.d.ts.map +1 -1
- package/dist/db/cast.js +13 -52
- package/dist/db/cast.js.map +1 -1
- package/dist/db/pagination.d.ts.map +1 -1
- package/dist/db/util.d.ts.map +1 -1
- package/dist/did-cache/db/index.d.ts.map +1 -1
- package/dist/disk-blobstore.d.ts.map +1 -1
- package/dist/handle/explicit-slurs.d.ts.map +1 -1
- package/dist/handle/index.d.ts.map +1 -1
- package/dist/index.js +17 -7
- package/dist/index.js.map +1 -1
- package/dist/lexicon/index.d.ts +4 -0
- package/dist/lexicon/index.d.ts.map +1 -1
- package/dist/lexicon/index.js +8 -0
- package/dist/lexicon/index.js.map +1 -1
- package/dist/lexicon/lexicons.d.ts +254 -4
- package/dist/lexicon/lexicons.d.ts.map +1 -1
- package/dist/lexicon/lexicons.js +134 -2
- package/dist/lexicon/lexicons.js.map +1 -1
- package/dist/lexicon/types/com/atproto/sync/defs.d.ts +2 -0
- package/dist/lexicon/types/com/atproto/sync/defs.d.ts.map +1 -0
- package/dist/lexicon/types/com/atproto/sync/defs.js +7 -0
- package/dist/lexicon/types/com/atproto/sync/defs.js.map +1 -0
- package/dist/lexicon/types/com/atproto/sync/getHostStatus.d.ts +43 -0
- package/dist/lexicon/types/com/atproto/sync/getHostStatus.d.ts.map +1 -0
- package/dist/lexicon/types/com/atproto/sync/getHostStatus.js +7 -0
- package/dist/lexicon/types/com/atproto/sync/getHostStatus.js.map +1 -0
- package/dist/lexicon/types/com/atproto/sync/listHosts.d.ts +51 -0
- package/dist/lexicon/types/com/atproto/sync/listHosts.d.ts.map +1 -0
- package/dist/lexicon/types/com/atproto/sync/listHosts.js +16 -0
- package/dist/lexicon/types/com/atproto/sync/listHosts.js.map +1 -0
- package/dist/lexicon/types/com/atproto/sync/requestCrawl.d.ts +1 -0
- package/dist/lexicon/types/com/atproto/sync/requestCrawl.d.ts.map +1 -1
- package/dist/lexicon/util.d.ts.map +1 -1
- package/dist/mailer/index.js +17 -7
- package/dist/mailer/index.js.map +1 -1
- package/dist/mailer/templates/plc-operation.js +1 -1
- package/dist/mailer/templates/plc-operation.js.map +1 -1
- package/dist/pipethrough.d.ts.map +1 -1
- package/dist/read-after-write/util.d.ts.map +1 -1
- package/dist/redis.d.ts.map +1 -1
- package/dist/repo/prepare.d.ts.map +1 -1
- package/dist/repo/prepare.js +17 -7
- package/dist/repo/prepare.js.map +1 -1
- package/dist/scripts/publish-identity.d.ts.map +1 -1
- package/dist/scripts/rebuild-repo.d.ts.map +1 -1
- package/dist/scripts/rotate-keys.d.ts.map +1 -1
- package/dist/scripts/sequencer-recovery/index.d.ts.map +1 -1
- package/dist/scripts/sequencer-recovery/recoverer.d.ts.map +1 -1
- package/dist/scripts/sequencer-recovery/recovery-db.d.ts.map +1 -1
- package/dist/scripts/sequencer-recovery/repair-repos.d.ts.map +1 -1
- package/dist/scripts/util.d.ts.map +1 -1
- package/dist/sequencer/db/index.d.ts.map +1 -1
- package/dist/sequencer/db/migrations/index.js +17 -7
- package/dist/sequencer/db/migrations/index.js.map +1 -1
- package/dist/sequencer/events.d.ts +6 -6
- package/dist/sequencer/events.d.ts.map +1 -1
- package/dist/sequencer/sequencer.d.ts.map +1 -1
- package/dist/util/debug.d.ts.map +1 -1
- package/dist/util/params.d.ts.map +1 -1
- package/dist/well-known.d.ts.map +1 -1
- package/package.json +6 -5
- package/src/account-manager/db/migrations/005-oauth-account-management.ts +112 -0
- package/src/account-manager/db/migrations/index.ts +2 -0
- package/src/account-manager/db/schema/account-device.ts +14 -0
- package/src/account-manager/db/schema/authorization-request.ts +5 -3
- package/src/account-manager/db/schema/authorized-client.ts +19 -0
- package/src/account-manager/db/schema/index.ts +5 -3
- package/src/account-manager/db/schema/token.ts +7 -4
- package/src/account-manager/helpers/account-device.ts +66 -0
- package/src/account-manager/helpers/authorization-request.ts +5 -5
- package/src/account-manager/helpers/authorized-client.ts +69 -0
- package/src/account-manager/helpers/device.ts +3 -1
- package/src/account-manager/helpers/token.ts +19 -57
- package/src/account-manager/oauth-store.ts +182 -103
- package/src/auth-routes.ts +11 -7
- package/src/auth-verifier.ts +2 -7
- package/src/config/config.ts +1 -1
- package/src/config/env.ts +2 -2
- package/src/context.ts +2 -2
- package/src/db/cast.ts +43 -50
- package/src/lexicon/index.ts +24 -0
- package/src/lexicon/lexicons.ts +141 -2
- package/src/lexicon/types/com/atproto/sync/defs.ts +23 -0
- package/src/lexicon/types/com/atproto/sync/getHostStatus.ts +61 -0
- package/src/lexicon/types/com/atproto/sync/listHosts.ts +77 -0
- package/src/lexicon/types/com/atproto/sync/requestCrawl.ts +1 -0
- package/src/mailer/templates/plc-operation.hbs +2 -2
- package/tests/db.test.ts +2 -1
- package/tsconfig.build.tsbuildinfo +1 -1
- package/tsconfig.tests.tsbuildinfo +1 -1
- package/dist/account-manager/db/schema/device-account.d.ts +0 -14
- package/dist/account-manager/db/schema/device-account.d.ts.map +0 -1
- package/dist/account-manager/db/schema/device-account.js.map +0 -1
- package/dist/account-manager/helpers/device-account.d.ts +0 -108
- package/dist/account-manager/helpers/device-account.d.ts.map +0 -1
- package/dist/account-manager/helpers/device-account.js +0 -83
- package/dist/account-manager/helpers/device-account.js.map +0 -1
- package/src/account-manager/db/schema/device-account.ts +0 -15
- package/src/account-manager/helpers/device-account.ts +0 -135
@@ -2,75 +2,34 @@ import { Selectable } from 'kysely'
|
|
2
2
|
import {
|
3
3
|
Code,
|
4
4
|
NewTokenData,
|
5
|
-
OAuthAuthorizationDetail,
|
6
5
|
RefreshToken,
|
7
6
|
TokenData,
|
8
7
|
TokenId,
|
9
|
-
TokenInfo,
|
10
8
|
} from '@atproto/oauth-provider'
|
11
|
-
import {
|
12
|
-
fromDateISO,
|
13
|
-
fromJsonArray,
|
14
|
-
fromJsonObject,
|
15
|
-
toDateISO,
|
16
|
-
toJsonArray,
|
17
|
-
toJsonObject,
|
18
|
-
} from '../../db'
|
9
|
+
import { fromDateISO, fromJson, toDateISO, toJson } from '../../db'
|
19
10
|
import { AccountDb, Token } from '../db'
|
20
|
-
import {
|
21
|
-
import {
|
22
|
-
SelectableDeviceAccount,
|
23
|
-
toAccount,
|
24
|
-
toDeviceAccountInfo,
|
25
|
-
} from './device-account'
|
26
|
-
|
27
|
-
type LeftJoined<T> = { [K in keyof T]: null | T[K] }
|
11
|
+
import { selectAccountQB } from './account'
|
28
12
|
|
29
|
-
export
|
30
|
-
|
31
|
-
LeftJoined<SelectableDeviceAccount>
|
32
|
-
|
33
|
-
export const toTokenInfo = (
|
34
|
-
row: ActorAccountToken,
|
35
|
-
audience: string,
|
36
|
-
): TokenInfo => ({
|
37
|
-
id: row.tokenId,
|
38
|
-
data: {
|
13
|
+
export function toTokenData(row: Selectable<Token>): TokenData {
|
14
|
+
return {
|
39
15
|
createdAt: fromDateISO(row.createdAt),
|
40
16
|
expiresAt: fromDateISO(row.expiresAt),
|
41
17
|
updatedAt: fromDateISO(row.updatedAt),
|
42
18
|
clientId: row.clientId,
|
43
|
-
clientAuth:
|
19
|
+
clientAuth: fromJson(row.clientAuth),
|
44
20
|
deviceId: row.deviceId,
|
45
21
|
sub: row.did,
|
46
|
-
parameters:
|
47
|
-
details: row.details
|
48
|
-
? fromJsonArray<OAuthAuthorizationDetail>(row.details)
|
49
|
-
: null,
|
22
|
+
parameters: fromJson(row.parameters),
|
50
23
|
code: row.code,
|
51
|
-
}
|
52
|
-
|
53
|
-
info:
|
54
|
-
row.authenticatedAt != null &&
|
55
|
-
row.authorizedClients != null &&
|
56
|
-
row.remember != null
|
57
|
-
? toDeviceAccountInfo(row as SelectableDeviceAccount)
|
58
|
-
: undefined,
|
59
|
-
currentRefreshToken: row.currentRefreshToken,
|
60
|
-
})
|
24
|
+
}
|
25
|
+
}
|
61
26
|
|
62
27
|
const selectTokenInfoQB = (db: AccountDb) =>
|
63
28
|
selectAccountQB(db, { includeDeactivated: true })
|
64
29
|
// uses "token_did_idx" index (though unlikely in practice)
|
65
30
|
.innerJoin('token', 'token.did', 'actor.did')
|
66
|
-
.leftJoin('device_account', (join) =>
|
67
|
-
join
|
68
|
-
// uses "device_account_pk" index
|
69
|
-
.on('device_account.did', '=', 'token.did')
|
70
|
-
// @ts-expect-error "deviceId" is nullable in token
|
71
|
-
.on('device_account.deviceId', '=', 'token.deviceId'),
|
72
|
-
)
|
73
31
|
.select([
|
32
|
+
'token.id',
|
74
33
|
'token.tokenId',
|
75
34
|
'token.createdAt',
|
76
35
|
'token.updatedAt',
|
@@ -83,9 +42,6 @@ const selectTokenInfoQB = (db: AccountDb) =>
|
|
83
42
|
'token.details',
|
84
43
|
'token.code',
|
85
44
|
'token.currentRefreshToken',
|
86
|
-
'device_account.authenticatedAt',
|
87
|
-
'device_account.authorizedClients',
|
88
|
-
'device_account.remember',
|
89
45
|
])
|
90
46
|
|
91
47
|
export const createQB = (
|
@@ -100,11 +56,11 @@ export const createQB = (
|
|
100
56
|
expiresAt: toDateISO(data.expiresAt),
|
101
57
|
updatedAt: toDateISO(data.updatedAt),
|
102
58
|
clientId: data.clientId,
|
103
|
-
clientAuth:
|
59
|
+
clientAuth: toJson(data.clientAuth),
|
104
60
|
deviceId: data.deviceId,
|
105
61
|
did: data.sub,
|
106
|
-
parameters:
|
107
|
-
details: data.details ?
|
62
|
+
parameters: toJson(data.parameters),
|
63
|
+
details: data.details ? toJson(data.details) : null,
|
108
64
|
code: data.code,
|
109
65
|
currentRefreshToken: refreshToken || null,
|
110
66
|
})
|
@@ -120,6 +76,7 @@ export const findByQB = (
|
|
120
76
|
db: AccountDb,
|
121
77
|
search: {
|
122
78
|
id?: number
|
79
|
+
did?: string
|
123
80
|
code?: Code
|
124
81
|
tokenId?: TokenId
|
125
82
|
currentRefreshToken?: RefreshToken
|
@@ -127,6 +84,7 @@ export const findByQB = (
|
|
127
84
|
) => {
|
128
85
|
if (
|
129
86
|
search.id === undefined &&
|
87
|
+
search.did === undefined &&
|
130
88
|
search.code === undefined &&
|
131
89
|
search.tokenId === undefined &&
|
132
90
|
search.currentRefreshToken === undefined
|
@@ -140,6 +98,10 @@ export const findByQB = (
|
|
140
98
|
// uses primary key index
|
141
99
|
qb.where('token.id', '=', search.id!),
|
142
100
|
)
|
101
|
+
.if(search.did !== undefined, (qb) =>
|
102
|
+
// uses "token_did_idx" index
|
103
|
+
qb.where('token.did', '=', search.did!),
|
104
|
+
)
|
143
105
|
.if(search.code !== undefined, (qb) =>
|
144
106
|
// uses "token_code_idx" partial index (hence the null check)
|
145
107
|
qb
|
@@ -175,7 +137,7 @@ export const rotateQB = (
|
|
175
137
|
|
176
138
|
expiresAt: toDateISO(newData.expiresAt),
|
177
139
|
updatedAt: toDateISO(newData.updatedAt),
|
178
|
-
clientAuth:
|
140
|
+
clientAuth: toJson(newData.clientAuth),
|
179
141
|
})
|
180
142
|
// uses primary key index
|
181
143
|
.where('id', '=', id)
|
@@ -1,13 +1,16 @@
|
|
1
|
+
import assert from 'node:assert'
|
1
2
|
import { Client, createOp as createPlcOp } from '@did-plc/lib'
|
2
3
|
import { Selectable } from 'kysely'
|
3
4
|
import { Keypair, Secp256k1Keypair } from '@atproto/crypto'
|
4
5
|
import {
|
5
6
|
Account,
|
6
|
-
AccountInfo,
|
7
7
|
AccountStore,
|
8
8
|
AuthenticateAccountData,
|
9
|
+
AuthorizedClientData,
|
10
|
+
AuthorizedClients,
|
11
|
+
ClientId,
|
9
12
|
Code,
|
10
|
-
|
13
|
+
DeviceAccount,
|
11
14
|
DeviceData,
|
12
15
|
DeviceId,
|
13
16
|
DeviceStore,
|
@@ -23,6 +26,7 @@ import {
|
|
23
26
|
ResetPasswordConfirmData,
|
24
27
|
ResetPasswordRequestData,
|
25
28
|
SignUpData,
|
29
|
+
Sub,
|
26
30
|
TokenData,
|
27
31
|
TokenId,
|
28
32
|
TokenInfo,
|
@@ -35,16 +39,21 @@ import {
|
|
35
39
|
} from '@atproto/xrpc-server'
|
36
40
|
import { ActorStore } from '../actor-store/actor-store'
|
37
41
|
import { BackgroundQueue } from '../background'
|
42
|
+
import { fromDateISO } from '../db'
|
38
43
|
import { ImageUrlBuilder } from '../image/image-url-builder'
|
44
|
+
import { dbLogger } from '../logger'
|
39
45
|
import { ServerMailer } from '../mailer'
|
40
46
|
import { Sequencer, syncEvtDataFromCommit } from '../sequencer'
|
41
47
|
import { AccountManager } from './account-manager'
|
42
|
-
import
|
43
|
-
import * as
|
44
|
-
import
|
45
|
-
import * as
|
46
|
-
import * as
|
47
|
-
import * as
|
48
|
+
import * as schemas from './db/schema'
|
49
|
+
import * as accountHelper from './helpers/account'
|
50
|
+
import { AccountStatus } from './helpers/account'
|
51
|
+
import * as accountDeviceHelper from './helpers/account-device'
|
52
|
+
import * as authRequestHelper from './helpers/authorization-request'
|
53
|
+
import * as authorizedClientHelper from './helpers/authorized-client'
|
54
|
+
import * as deviceHelper from './helpers/device'
|
55
|
+
import * as tokenHelper from './helpers/token'
|
56
|
+
import * as usedRefreshTokenHelper from './helpers/used-refresh-token'
|
48
57
|
|
49
58
|
/**
|
50
59
|
* This class' purpose is to implement the interface needed by the OAuthProvider
|
@@ -78,29 +87,6 @@ export class OAuthStore
|
|
78
87
|
return this.accountManager.serviceDid
|
79
88
|
}
|
80
89
|
|
81
|
-
private async buildAccount(row: Selectable<ActorAccount>): Promise<Account> {
|
82
|
-
const account = deviceAccount.toAccount(row, this.serviceDid)
|
83
|
-
|
84
|
-
if (!account.name || !account.picture) {
|
85
|
-
const did = account.sub
|
86
|
-
|
87
|
-
const profile = await this.actorStore.read(did, async (store) => {
|
88
|
-
return store.record.getProfileRecord()
|
89
|
-
})
|
90
|
-
|
91
|
-
if (profile) {
|
92
|
-
const { avatar, displayName } = profile
|
93
|
-
|
94
|
-
account.name ||= displayName
|
95
|
-
account.picture ||= avatar
|
96
|
-
? this.imageUrlBuilder.build('avatar', did, avatar.ref.toString())
|
97
|
-
: undefined
|
98
|
-
}
|
99
|
-
}
|
100
|
-
|
101
|
-
return account
|
102
|
-
}
|
103
|
-
|
104
90
|
private async verifyEmailAvailability(email: string): Promise<void> {
|
105
91
|
// @NOTE Email validity & disposability check performed by the OAuthProvider
|
106
92
|
|
@@ -244,72 +230,99 @@ export class OAuthStore
|
|
244
230
|
}
|
245
231
|
}
|
246
232
|
|
247
|
-
async
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
): Promise<DeviceAccountInfo> {
|
252
|
-
const [row] = await this.db.executeWithRetry(
|
253
|
-
deviceAccount.createOrUpdateQB(this.db, deviceId, sub, remember),
|
254
|
-
)
|
255
|
-
if (!row) throw new Error('Failed to create device account')
|
256
|
-
return deviceAccount.toDeviceAccountInfo(row)
|
257
|
-
}
|
258
|
-
|
259
|
-
async addAuthorizedClient(
|
260
|
-
deviceId: DeviceId,
|
261
|
-
sub: string,
|
262
|
-
clientId: string,
|
233
|
+
async setAuthorizedClient(
|
234
|
+
sub: Sub,
|
235
|
+
clientId: ClientId,
|
236
|
+
data: AuthorizedClientData,
|
263
237
|
): Promise<void> {
|
264
|
-
await this.db
|
265
|
-
|
266
|
-
.readQB(dbTxn, deviceId, sub)
|
267
|
-
.executeTakeFirstOrThrow()
|
238
|
+
await authorizedClientHelper.upsert(this.db, sub, clientId, data)
|
239
|
+
}
|
268
240
|
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
.execute()
|
276
|
-
}
|
241
|
+
async getAccount(sub: Sub): Promise<{
|
242
|
+
account: Account
|
243
|
+
authorizedClients: AuthorizedClients
|
244
|
+
}> {
|
245
|
+
const accountRow = await accountHelper.getAccount(this.db, sub, {
|
246
|
+
includeDeactivated: true,
|
277
247
|
})
|
248
|
+
|
249
|
+
assert(accountRow, 'Account not found')
|
250
|
+
|
251
|
+
const account = await this.buildAccount(accountRow)
|
252
|
+
const authorizedClients = await authorizedClientHelper.getAuthorizedClients(
|
253
|
+
this.db,
|
254
|
+
sub,
|
255
|
+
)
|
256
|
+
|
257
|
+
return { account, authorizedClients }
|
258
|
+
}
|
259
|
+
|
260
|
+
async upsertDeviceAccount(deviceId: DeviceId, sub: string): Promise<void> {
|
261
|
+
await this.db.executeWithRetry(
|
262
|
+
accountDeviceHelper.upsertQB(this.db, deviceId, sub),
|
263
|
+
)
|
278
264
|
}
|
279
265
|
|
280
266
|
async getDeviceAccount(
|
281
267
|
deviceId: DeviceId,
|
282
268
|
sub: string,
|
283
|
-
): Promise<
|
284
|
-
const row = await
|
285
|
-
.
|
269
|
+
): Promise<DeviceAccount | null> {
|
270
|
+
const row = await accountDeviceHelper
|
271
|
+
.selectQB(this.db, { deviceId, sub })
|
286
272
|
.executeTakeFirst()
|
287
273
|
|
288
274
|
if (!row) return null
|
289
275
|
|
290
276
|
return {
|
277
|
+
deviceId,
|
278
|
+
deviceData: deviceHelper.rowToDeviceData(row),
|
291
279
|
account: await this.buildAccount(row),
|
292
|
-
|
280
|
+
authorizedClients: await authorizedClientHelper.getAuthorizedClients(
|
281
|
+
this.db,
|
282
|
+
sub,
|
283
|
+
),
|
284
|
+
createdAt: fromDateISO(row.adCreatedAt),
|
285
|
+
updatedAt: fromDateISO(row.adUpdatedAt),
|
293
286
|
}
|
294
287
|
}
|
295
288
|
|
296
|
-
async
|
297
|
-
|
298
|
-
.
|
299
|
-
.execute()
|
300
|
-
|
301
|
-
return Promise.all(
|
302
|
-
rows.map(async (row) => ({
|
303
|
-
account: await this.buildAccount(row),
|
304
|
-
info: deviceAccount.toDeviceAccountInfo(row),
|
305
|
-
})),
|
289
|
+
async removeDeviceAccount(deviceId: DeviceId, sub: Sub): Promise<void> {
|
290
|
+
await this.db.executeWithRetry(
|
291
|
+
accountDeviceHelper.removeQB(this.db, deviceId, sub),
|
306
292
|
)
|
307
293
|
}
|
308
294
|
|
309
|
-
async
|
310
|
-
|
311
|
-
|
295
|
+
async listDeviceAccounts(
|
296
|
+
filter: { sub: Sub } | { deviceId: DeviceId },
|
297
|
+
): Promise<DeviceAccount[]> {
|
298
|
+
const rows = await accountDeviceHelper.selectQB(this.db, filter).execute()
|
299
|
+
|
300
|
+
const uniqueDids = [...new Set(rows.map((row) => row.did))]
|
301
|
+
|
302
|
+
// Enrich all distinct account with their profile data
|
303
|
+
const accounts = new Map(
|
304
|
+
await Promise.all(
|
305
|
+
Array.from(uniqueDids, async (did): Promise<[Sub, Account]> => {
|
306
|
+
const row = rows.find((r) => r.did === did)!
|
307
|
+
return [did, await this.buildAccount(row)]
|
308
|
+
}),
|
309
|
+
),
|
312
310
|
)
|
311
|
+
|
312
|
+
const authorizedClientsMap =
|
313
|
+
await authorizedClientHelper.getAuthorizedClientsMulti(
|
314
|
+
this.db,
|
315
|
+
uniqueDids,
|
316
|
+
)
|
317
|
+
|
318
|
+
return rows.map((row) => ({
|
319
|
+
deviceId: row.deviceId,
|
320
|
+
deviceData: deviceHelper.rowToDeviceData(row),
|
321
|
+
account: accounts.get(row.did)!,
|
322
|
+
authorizedClients: authorizedClientsMap.get(row.did)!,
|
323
|
+
createdAt: fromDateISO(row.adCreatedAt),
|
324
|
+
updatedAt: fromDateISO(row.adUpdatedAt),
|
325
|
+
}))
|
313
326
|
}
|
314
327
|
|
315
328
|
async resetPasswordRequest({
|
@@ -383,58 +396,70 @@ export class OAuthStore
|
|
383
396
|
// RequestStore
|
384
397
|
|
385
398
|
async createRequest(id: RequestId, data: RequestData): Promise<void> {
|
386
|
-
await this.db.executeWithRetry(
|
399
|
+
await this.db.executeWithRetry(
|
400
|
+
authRequestHelper.createQB(this.db, id, data),
|
401
|
+
)
|
387
402
|
}
|
388
403
|
|
389
404
|
async readRequest(id: RequestId): Promise<RequestData | null> {
|
390
405
|
try {
|
391
|
-
const row = await
|
406
|
+
const row = await authRequestHelper.readQB(this.db, id).executeTakeFirst()
|
392
407
|
if (!row) return null
|
393
|
-
return
|
408
|
+
return authRequestHelper.rowToRequestData(row)
|
394
409
|
} finally {
|
395
410
|
// Take the opportunity to clean up expired requests. Do this after we got
|
396
411
|
// the current (potentially expired) request data to allow the provider to
|
397
412
|
// handle expired requests.
|
398
413
|
this.backgroundQueue.add(async () => {
|
399
|
-
await this.db.executeWithRetry(
|
414
|
+
await this.db.executeWithRetry(
|
415
|
+
authRequestHelper.removeOldExpiredQB(this.db),
|
416
|
+
)
|
400
417
|
})
|
401
418
|
}
|
402
419
|
}
|
403
420
|
|
404
421
|
async updateRequest(id: RequestId, data: UpdateRequestData): Promise<void> {
|
405
|
-
await this.db.executeWithRetry(
|
422
|
+
await this.db.executeWithRetry(
|
423
|
+
authRequestHelper.updateQB(this.db, id, data),
|
424
|
+
)
|
406
425
|
}
|
407
426
|
|
408
427
|
async deleteRequest(id: RequestId): Promise<void> {
|
409
|
-
await this.db.executeWithRetry(
|
428
|
+
await this.db.executeWithRetry(authRequestHelper.removeByIdQB(this.db, id))
|
410
429
|
}
|
411
430
|
|
412
431
|
async findRequestByCode(code: Code): Promise<FoundRequestResult | null> {
|
413
|
-
const row = await
|
414
|
-
|
432
|
+
const row = await authRequestHelper
|
433
|
+
.findByCodeQB(this.db, code)
|
434
|
+
.executeTakeFirst()
|
435
|
+
return row ? authRequestHelper.rowToFoundRequestResult(row) : null
|
415
436
|
}
|
416
437
|
|
417
438
|
// DeviceStore
|
418
439
|
|
419
440
|
async createDevice(deviceId: DeviceId, data: DeviceData): Promise<void> {
|
420
|
-
await this.db.executeWithRetry(
|
441
|
+
await this.db.executeWithRetry(
|
442
|
+
deviceHelper.createQB(this.db, deviceId, data),
|
443
|
+
)
|
421
444
|
}
|
422
445
|
|
423
446
|
async readDevice(deviceId: DeviceId): Promise<null | DeviceData> {
|
424
|
-
const row = await
|
425
|
-
return row ?
|
447
|
+
const row = await deviceHelper.readQB(this.db, deviceId).executeTakeFirst()
|
448
|
+
return row ? deviceHelper.rowToDeviceData(row) : null
|
426
449
|
}
|
427
450
|
|
428
451
|
async updateDevice(
|
429
452
|
deviceId: DeviceId,
|
430
453
|
data: Partial<DeviceData>,
|
431
454
|
): Promise<void> {
|
432
|
-
await this.db.executeWithRetry(
|
455
|
+
await this.db.executeWithRetry(
|
456
|
+
deviceHelper.updateQB(this.db, deviceId, data),
|
457
|
+
)
|
433
458
|
}
|
434
459
|
|
435
460
|
async deleteDevice(deviceId: DeviceId): Promise<void> {
|
436
461
|
// Will cascade to device_account (device_account_device_id_fk)
|
437
|
-
await this.db.executeWithRetry(
|
462
|
+
await this.db.executeWithRetry(deviceHelper.removeQB(this.db, deviceId))
|
438
463
|
}
|
439
464
|
|
440
465
|
// TokenStore
|
@@ -446,7 +471,7 @@ export class OAuthStore
|
|
446
471
|
): Promise<void> {
|
447
472
|
await this.db.transaction(async (dbTxn) => {
|
448
473
|
if (refreshToken) {
|
449
|
-
const { count } = await
|
474
|
+
const { count } = await usedRefreshTokenHelper
|
450
475
|
.countQB(dbTxn, refreshToken)
|
451
476
|
.executeTakeFirstOrThrow()
|
452
477
|
|
@@ -455,18 +480,25 @@ export class OAuthStore
|
|
455
480
|
}
|
456
481
|
}
|
457
482
|
|
458
|
-
return
|
483
|
+
return tokenHelper.createQB(dbTxn, id, data, refreshToken).execute()
|
459
484
|
})
|
460
485
|
}
|
461
486
|
|
487
|
+
async listAccountTokens(sub: Sub): Promise<TokenInfo[]> {
|
488
|
+
const rows = await tokenHelper.findByQB(this.db, { did: sub }).execute()
|
489
|
+
return Promise.all(rows.map((row) => this.toTokenInfo(row)))
|
490
|
+
}
|
491
|
+
|
462
492
|
async readToken(tokenId: TokenId): Promise<TokenInfo | null> {
|
463
|
-
const row = await
|
464
|
-
|
493
|
+
const row = await tokenHelper
|
494
|
+
.findByQB(this.db, { tokenId })
|
495
|
+
.executeTakeFirst()
|
496
|
+
return row ? this.toTokenInfo(row) : null
|
465
497
|
}
|
466
498
|
|
467
499
|
async deleteToken(tokenId: TokenId): Promise<void> {
|
468
500
|
// Will cascade to used_refresh_token (used_refresh_token_fk)
|
469
|
-
await this.db.executeWithRetry(
|
501
|
+
await this.db.executeWithRetry(tokenHelper.removeQB(this.db, tokenId))
|
470
502
|
}
|
471
503
|
|
472
504
|
async rotateToken(
|
@@ -476,17 +508,17 @@ export class OAuthStore
|
|
476
508
|
newData: NewTokenData,
|
477
509
|
): Promise<void> {
|
478
510
|
const err = await this.db.transaction(async (dbTxn) => {
|
479
|
-
const { id, currentRefreshToken } = await
|
511
|
+
const { id, currentRefreshToken } = await tokenHelper
|
480
512
|
.forRotateQB(dbTxn, tokenId)
|
481
513
|
.executeTakeFirstOrThrow()
|
482
514
|
|
483
515
|
if (currentRefreshToken) {
|
484
|
-
await
|
516
|
+
await usedRefreshTokenHelper
|
485
517
|
.insertQB(dbTxn, id, currentRefreshToken)
|
486
518
|
.execute()
|
487
519
|
}
|
488
520
|
|
489
|
-
const { count } = await
|
521
|
+
const { count } = await usedRefreshTokenHelper
|
490
522
|
.countQB(dbTxn, newRefreshToken)
|
491
523
|
.executeTakeFirstOrThrow()
|
492
524
|
|
@@ -495,7 +527,7 @@ export class OAuthStore
|
|
495
527
|
return new Error('New refresh token already in use')
|
496
528
|
}
|
497
529
|
|
498
|
-
await
|
530
|
+
await tokenHelper
|
499
531
|
.rotateQB(dbTxn, id, newTokenId, newRefreshToken, newData)
|
500
532
|
.execute()
|
501
533
|
})
|
@@ -506,7 +538,7 @@ export class OAuthStore
|
|
506
538
|
async findTokenByRefreshToken(
|
507
539
|
refreshToken: RefreshToken,
|
508
540
|
): Promise<TokenInfo | null> {
|
509
|
-
const used = await
|
541
|
+
const used = await usedRefreshTokenHelper
|
510
542
|
.findByTokenQB(this.db, refreshToken)
|
511
543
|
.executeTakeFirst()
|
512
544
|
|
@@ -514,12 +546,59 @@ export class OAuthStore
|
|
514
546
|
? { id: used.tokenId }
|
515
547
|
: { currentRefreshToken: refreshToken }
|
516
548
|
|
517
|
-
const row = await
|
518
|
-
return row ?
|
549
|
+
const row = await tokenHelper.findByQB(this.db, search).executeTakeFirst()
|
550
|
+
return row ? this.toTokenInfo(row) : null
|
519
551
|
}
|
520
552
|
|
521
553
|
async findTokenByCode(code: Code): Promise<TokenInfo | null> {
|
522
|
-
const row = await
|
523
|
-
return row ?
|
554
|
+
const row = await tokenHelper.findByQB(this.db, { code }).executeTakeFirst()
|
555
|
+
return row ? this.toTokenInfo(row) : null
|
556
|
+
}
|
557
|
+
|
558
|
+
private async toTokenInfo(
|
559
|
+
row: accountHelper.ActorAccount & Selectable<schemas.Token>,
|
560
|
+
): Promise<TokenInfo> {
|
561
|
+
return {
|
562
|
+
id: row.tokenId,
|
563
|
+
data: tokenHelper.toTokenData(row),
|
564
|
+
account: await this.buildAccount(row),
|
565
|
+
currentRefreshToken: row.currentRefreshToken,
|
566
|
+
}
|
567
|
+
}
|
568
|
+
|
569
|
+
private async buildAccount(
|
570
|
+
row: accountHelper.ActorAccount,
|
571
|
+
): Promise<Account> {
|
572
|
+
const account: Account = {
|
573
|
+
sub: row.did,
|
574
|
+
aud: this.serviceDid,
|
575
|
+
email: row.email || undefined,
|
576
|
+
email_verified: row.email ? row.emailConfirmedAt != null : undefined,
|
577
|
+
preferred_username: row.handle || undefined,
|
578
|
+
}
|
579
|
+
|
580
|
+
if (!account.name || !account.picture) {
|
581
|
+
const did = account.sub
|
582
|
+
|
583
|
+
const profile = await this.actorStore
|
584
|
+
.read(did, async (store) => {
|
585
|
+
return store.record.getProfileRecord()
|
586
|
+
})
|
587
|
+
.catch((err) => {
|
588
|
+
dbLogger.error({ err }, 'Failed to get profile record')
|
589
|
+
return null // No need to propagate
|
590
|
+
})
|
591
|
+
|
592
|
+
if (profile) {
|
593
|
+
const { avatar, displayName } = profile
|
594
|
+
|
595
|
+
account.name ||= displayName
|
596
|
+
account.picture ||= avatar
|
597
|
+
? this.imageUrlBuilder.build('avatar', did, avatar.ref.toString())
|
598
|
+
: undefined
|
599
|
+
}
|
600
|
+
}
|
601
|
+
|
602
|
+
return account
|
524
603
|
}
|
525
604
|
}
|
package/src/auth-routes.ts
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
import { Router } from 'express'
|
2
|
-
import {
|
2
|
+
import {
|
3
|
+
oauthMiddleware,
|
4
|
+
oauthProtectedResourceMetadataSchema,
|
5
|
+
} from '@atproto/oauth-provider'
|
3
6
|
import { AppContext } from './context'
|
4
7
|
import { oauthLogger } from './logger'
|
5
8
|
|
@@ -30,12 +33,13 @@ export const createRouter = ({ oauthProvider, cfg }: AppContext): Router => {
|
|
30
33
|
})
|
31
34
|
|
32
35
|
if (oauthProvider) {
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
36
|
+
router.use(
|
37
|
+
oauthMiddleware(oauthProvider, {
|
38
|
+
onError: (req, res, err, message) => {
|
39
|
+
oauthLogger.error({ err, req }, message)
|
40
|
+
},
|
41
|
+
}),
|
42
|
+
)
|
39
43
|
}
|
40
44
|
|
41
45
|
return router
|
package/src/auth-verifier.ts
CHANGED
@@ -139,7 +139,7 @@ export class AuthVerifier {
|
|
139
139
|
|
140
140
|
accessStandard =
|
141
141
|
(opts: Partial<AccessOpts> = {}) =>
|
142
|
-
(ctx: ReqCtx): Promise<AccessOutput> => {
|
142
|
+
async (ctx: ReqCtx): Promise<AccessOutput> => {
|
143
143
|
return this.validateAccessToken(
|
144
144
|
ctx,
|
145
145
|
[
|
@@ -528,18 +528,13 @@ export class AuthVerifier {
|
|
528
528
|
)
|
529
529
|
}
|
530
530
|
|
531
|
-
const isPrivileged = [
|
532
|
-
AuthScope.Access,
|
533
|
-
AuthScope.AppPassPrivileged,
|
534
|
-
].includes(scopeEquivalent)
|
535
|
-
|
536
531
|
return {
|
537
532
|
credentials: {
|
538
533
|
type: 'access',
|
539
534
|
did: result.claims.sub,
|
540
535
|
scope: scopeEquivalent,
|
541
536
|
audience: this.dids.pds,
|
542
|
-
isPrivileged,
|
537
|
+
isPrivileged: scopeEquivalent === AuthScope.AppPassPrivileged,
|
543
538
|
},
|
544
539
|
artifacts: result.token,
|
545
540
|
}
|
package/src/config/config.ts
CHANGED
@@ -275,7 +275,7 @@ export const envToCfg = (env: ServerEnvironment): ServerConfig => {
|
|
275
275
|
name: env.serviceName ?? 'Personal PDS',
|
276
276
|
logo: env.logoUrl,
|
277
277
|
colors: {
|
278
|
-
|
278
|
+
primary: env.primaryColor,
|
279
279
|
error: env.errorColor,
|
280
280
|
success: env.successColor,
|
281
281
|
warning: env.warningColor,
|
package/src/config/env.ts
CHANGED
@@ -24,7 +24,7 @@ export const readEnv = (): ServerEnvironment => {
|
|
24
24
|
hcaptchaTokenSalt: envStr('PDS_HCAPTCHA_TOKEN_SALT'),
|
25
25
|
|
26
26
|
// branding
|
27
|
-
|
27
|
+
primaryColor: envStr('PDS_PRIMARY_COLOR'),
|
28
28
|
errorColor: envStr('PDS_ERROR_COLOR'),
|
29
29
|
warningColor: envStr('PDS_WARNING_COLOR'),
|
30
30
|
successColor: envStr('PDS_SUCCESS_COLOR'),
|
@@ -163,7 +163,7 @@ export type ServerEnvironment = {
|
|
163
163
|
hcaptchaTokenSalt?: string
|
164
164
|
|
165
165
|
// branding
|
166
|
-
|
166
|
+
primaryColor?: string
|
167
167
|
errorColor?: string
|
168
168
|
warningColor?: string
|
169
169
|
successColor?: string
|