@atproto/pds 0.5.1 → 0.5.2
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 +11 -0
- package/dist/account-manager/account-manager.d.ts +35 -4
- package/dist/account-manager/account-manager.d.ts.map +1 -1
- package/dist/account-manager/account-manager.js +67 -6
- package/dist/account-manager/account-manager.js.map +1 -1
- package/dist/account-manager/helpers/account.d.ts +1 -1
- package/dist/account-manager/helpers/account.d.ts.map +1 -1
- package/dist/account-manager/helpers/account.js +10 -4
- package/dist/account-manager/helpers/account.js.map +1 -1
- package/dist/account-manager/oauth-store.d.ts +2 -1
- package/dist/account-manager/oauth-store.d.ts.map +1 -1
- package/dist/account-manager/oauth-store.js +61 -12
- package/dist/account-manager/oauth-store.js.map +1 -1
- package/dist/actor-store/record/reader.d.ts +1 -1
- package/dist/actor-store/record/reader.d.ts.map +1 -1
- package/dist/actor-store/record/reader.js.map +1 -1
- package/dist/api/com/atproto/admin/updateAccountHandle.d.ts.map +1 -1
- package/dist/api/com/atproto/admin/updateAccountHandle.js +33 -43
- package/dist/api/com/atproto/admin/updateAccountHandle.js.map +1 -1
- package/dist/api/com/atproto/identity/updateHandle.d.ts.map +1 -1
- package/dist/api/com/atproto/identity/updateHandle.js +39 -61
- package/dist/api/com/atproto/identity/updateHandle.js.map +1 -1
- package/dist/api/com/atproto/repo/getRecord.js +3 -3
- package/dist/api/com/atproto/repo/getRecord.js.map +1 -1
- package/dist/api/com/atproto/repo/putRecord.js +2 -2
- package/dist/api/com/atproto/repo/putRecord.js.map +1 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +2 -2
- package/dist/context.js.map +1 -1
- package/dist/lexicons/app/bsky/actor/defs.defs.d.ts +8 -0
- package/dist/lexicons/app/bsky/actor/defs.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/actor/defs.defs.js +3 -0
- package/dist/lexicons/app/bsky/actor/defs.defs.js.map +1 -1
- package/dist/lexicons/app/bsky/actor/profile.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/actor/status.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/draft/defs.defs.d.ts +22 -0
- package/dist/lexicons/app/bsky/draft/defs.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/draft/defs.defs.js +11 -0
- package/dist/lexicons/app/bsky/draft/defs.defs.js.map +1 -1
- package/dist/lexicons/app/bsky/embed/gallery.d.ts +3 -0
- package/dist/lexicons/app/bsky/embed/gallery.d.ts.map +1 -0
- package/dist/lexicons/app/bsky/embed/gallery.defs.d.ts +130 -0
- package/dist/lexicons/app/bsky/embed/gallery.defs.d.ts.map +1 -0
- package/dist/lexicons/app/bsky/embed/gallery.defs.js +47 -0
- package/dist/lexicons/app/bsky/embed/gallery.defs.js.map +1 -0
- package/dist/lexicons/app/bsky/embed/gallery.js +6 -0
- package/dist/lexicons/app/bsky/embed/gallery.js.map +1 -0
- package/dist/lexicons/app/bsky/embed/record.defs.d.ts +2 -1
- package/dist/lexicons/app/bsky/embed/record.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/embed/record.defs.js +2 -0
- package/dist/lexicons/app/bsky/embed/record.defs.js.map +1 -1
- package/dist/lexicons/app/bsky/embed/recordWithMedia.defs.d.ts +13 -12
- package/dist/lexicons/app/bsky/embed/recordWithMedia.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/embed/recordWithMedia.defs.js +3 -0
- package/dist/lexicons/app/bsky/embed/recordWithMedia.defs.js.map +1 -1
- package/dist/lexicons/app/bsky/embed.d.ts +1 -0
- package/dist/lexicons/app/bsky/embed.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/embed.js +1 -0
- package/dist/lexicons/app/bsky/embed.js.map +1 -1
- package/dist/lexicons/app/bsky/feed/defs.defs.d.ts +2 -1
- package/dist/lexicons/app/bsky/feed/defs.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/feed/defs.defs.js +2 -0
- package/dist/lexicons/app/bsky/feed/defs.defs.js.map +1 -1
- package/dist/lexicons/app/bsky/feed/generator.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/feed/like.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/feed/post.defs.d.ts +12 -11
- package/dist/lexicons/app/bsky/feed/post.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/feed/post.defs.js +2 -0
- package/dist/lexicons/app/bsky/feed/post.defs.js.map +1 -1
- package/dist/lexicons/app/bsky/feed/postgate.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/feed/repost.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/feed/threadgate.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/graph/block.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/graph/follow.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/graph/list.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/graph/listblock.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/graph/listitem.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/graph/starterpack.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/graph/verification.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/labeler/service.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/notification/declaration.defs.d.ts.map +1 -1
- package/dist/lexicons/chat/bsky/actor/declaration.defs.d.ts.map +1 -1
- package/dist/lexicons/chat/bsky/authFullChatClient.defs.d.ts.map +1 -1
- package/dist/lexicons/chat/bsky/authFullChatClient.defs.js +1 -0
- package/dist/lexicons/chat/bsky/authFullChatClient.defs.js.map +1 -1
- package/dist/lexicons/chat/bsky/convo/defs.defs.d.ts +53 -14
- package/dist/lexicons/chat/bsky/convo/defs.defs.d.ts.map +1 -1
- package/dist/lexicons/chat/bsky/convo/defs.defs.js +33 -5
- package/dist/lexicons/chat/bsky/convo/defs.defs.js.map +1 -1
- package/dist/lexicons/chat/bsky/convo/getConvoForMembers.defs.d.ts +1 -1
- package/dist/lexicons/chat/bsky/convo/getConvoForMembers.defs.d.ts.map +1 -1
- package/dist/lexicons/chat/bsky/convo/getConvoForMembers.defs.js +1 -0
- package/dist/lexicons/chat/bsky/convo/getConvoForMembers.defs.js.map +1 -1
- package/dist/lexicons/chat/bsky/convo/getLog.defs.d.ts +2 -2
- package/dist/lexicons/chat/bsky/convo/getLog.defs.d.ts.map +1 -1
- package/dist/lexicons/chat/bsky/convo/getLog.defs.js +3 -0
- package/dist/lexicons/chat/bsky/convo/getLog.defs.js.map +1 -1
- package/dist/lexicons/chat/bsky/embed/joinLink.d.ts +3 -0
- package/dist/lexicons/chat/bsky/embed/joinLink.d.ts.map +1 -0
- package/dist/lexicons/chat/bsky/embed/joinLink.defs.d.ts +99 -0
- package/dist/lexicons/chat/bsky/embed/joinLink.defs.d.ts.map +1 -0
- package/dist/lexicons/chat/bsky/embed/joinLink.defs.js +28 -0
- package/dist/lexicons/chat/bsky/embed/joinLink.defs.js.map +1 -0
- package/dist/lexicons/chat/bsky/embed/joinLink.js +6 -0
- package/dist/lexicons/chat/bsky/embed/joinLink.js.map +1 -0
- package/dist/lexicons/chat/bsky/embed.d.ts +2 -0
- package/dist/lexicons/chat/bsky/embed.d.ts.map +1 -0
- package/dist/lexicons/chat/bsky/embed.js +5 -0
- package/dist/lexicons/chat/bsky/embed.js.map +1 -0
- package/dist/lexicons/chat/bsky/group/addMembers.defs.d.ts +1 -1
- package/dist/lexicons/chat/bsky/group/addMembers.defs.d.ts.map +1 -1
- package/dist/lexicons/chat/bsky/group/addMembers.defs.js +1 -0
- package/dist/lexicons/chat/bsky/group/addMembers.defs.js.map +1 -1
- package/dist/lexicons/chat/bsky/group/createGroup.defs.d.ts +1 -1
- package/dist/lexicons/chat/bsky/group/createGroup.defs.d.ts.map +1 -1
- package/dist/lexicons/chat/bsky/group/createGroup.defs.js +1 -0
- package/dist/lexicons/chat/bsky/group/createGroup.defs.js.map +1 -1
- package/dist/lexicons/chat/bsky/group/getJoinLinkPreviews.defs.d.ts +1 -1
- package/dist/lexicons/chat/bsky/group/getJoinLinkPreviews.defs.d.ts.map +1 -1
- package/dist/lexicons/chat/bsky/group/getJoinLinkPreviews.defs.js +1 -1
- package/dist/lexicons/chat/bsky/group/getJoinLinkPreviews.defs.js.map +1 -1
- package/dist/lexicons/chat/bsky/group/updateJoinRequestsRead.d.ts +3 -0
- package/dist/lexicons/chat/bsky/group/updateJoinRequestsRead.d.ts.map +1 -0
- package/dist/lexicons/chat/bsky/group/updateJoinRequestsRead.defs.d.ts +20 -0
- package/dist/lexicons/chat/bsky/group/updateJoinRequestsRead.defs.d.ts.map +1 -0
- package/dist/lexicons/chat/bsky/group/updateJoinRequestsRead.defs.js +19 -0
- package/dist/lexicons/chat/bsky/group/updateJoinRequestsRead.defs.js.map +1 -0
- package/dist/lexicons/chat/bsky/group/updateJoinRequestsRead.js +6 -0
- package/dist/lexicons/chat/bsky/group/updateJoinRequestsRead.js.map +1 -0
- package/dist/lexicons/chat/bsky/group/withdrawJoinRequest.d.ts +3 -0
- package/dist/lexicons/chat/bsky/group/withdrawJoinRequest.d.ts.map +1 -0
- package/dist/lexicons/chat/bsky/group/withdrawJoinRequest.defs.d.ts +20 -0
- package/dist/lexicons/chat/bsky/group/withdrawJoinRequest.defs.d.ts.map +1 -0
- package/dist/lexicons/chat/bsky/group/withdrawJoinRequest.defs.js +18 -0
- package/dist/lexicons/chat/bsky/group/withdrawJoinRequest.defs.js.map +1 -0
- package/dist/lexicons/chat/bsky/group/withdrawJoinRequest.js +6 -0
- package/dist/lexicons/chat/bsky/group/withdrawJoinRequest.js.map +1 -0
- package/dist/lexicons/chat/bsky/group.d.ts +2 -0
- package/dist/lexicons/chat/bsky/group.d.ts.map +1 -1
- package/dist/lexicons/chat/bsky/group.js +2 -0
- package/dist/lexicons/chat/bsky/group.js.map +1 -1
- package/dist/lexicons/chat/bsky/moderation/defs.d.ts +2 -0
- package/dist/lexicons/chat/bsky/moderation/defs.d.ts.map +1 -0
- package/dist/lexicons/chat/bsky/moderation/defs.defs.d.ts +58 -0
- package/dist/lexicons/chat/bsky/moderation/defs.defs.d.ts.map +1 -0
- package/dist/lexicons/chat/bsky/moderation/defs.defs.js +38 -0
- package/dist/lexicons/chat/bsky/moderation/defs.defs.js.map +1 -0
- package/dist/lexicons/chat/bsky/moderation/defs.js +5 -0
- package/dist/lexicons/chat/bsky/moderation/defs.js.map +1 -0
- package/dist/lexicons/chat/bsky/moderation/getConvo.d.ts +3 -0
- package/dist/lexicons/chat/bsky/moderation/getConvo.d.ts.map +1 -0
- package/dist/lexicons/chat/bsky/moderation/getConvo.defs.d.ts +22 -0
- package/dist/lexicons/chat/bsky/moderation/getConvo.defs.d.ts.map +1 -0
- package/dist/lexicons/chat/bsky/moderation/getConvo.defs.js +18 -0
- package/dist/lexicons/chat/bsky/moderation/getConvo.defs.js.map +1 -0
- package/dist/lexicons/chat/bsky/moderation/getConvo.js +6 -0
- package/dist/lexicons/chat/bsky/moderation/getConvo.js.map +1 -0
- package/dist/lexicons/chat/bsky/moderation/getConvoMembers.d.ts +3 -0
- package/dist/lexicons/chat/bsky/moderation/getConvoMembers.d.ts.map +1 -0
- package/dist/lexicons/chat/bsky/moderation/getConvoMembers.defs.d.ts +28 -0
- package/dist/lexicons/chat/bsky/moderation/getConvoMembers.defs.d.ts.map +1 -0
- package/dist/lexicons/chat/bsky/moderation/getConvoMembers.defs.js +24 -0
- package/dist/lexicons/chat/bsky/moderation/getConvoMembers.defs.js.map +1 -0
- package/dist/lexicons/chat/bsky/moderation/getConvoMembers.js +6 -0
- package/dist/lexicons/chat/bsky/moderation/getConvoMembers.js.map +1 -0
- package/dist/lexicons/chat/bsky/moderation/getConvos.d.ts +3 -0
- package/dist/lexicons/chat/bsky/moderation/getConvos.d.ts.map +1 -0
- package/dist/lexicons/chat/bsky/moderation/getConvos.defs.d.ts +22 -0
- package/dist/lexicons/chat/bsky/moderation/getConvos.defs.d.ts.map +1 -0
- package/dist/lexicons/chat/bsky/moderation/getConvos.defs.js +22 -0
- package/dist/lexicons/chat/bsky/moderation/getConvos.defs.js.map +1 -0
- package/dist/lexicons/chat/bsky/moderation/getConvos.js +6 -0
- package/dist/lexicons/chat/bsky/moderation/getConvos.js.map +1 -0
- package/dist/lexicons/chat/bsky/moderation/subscribeModEvents.defs.d.ts +20 -2
- package/dist/lexicons/chat/bsky/moderation/subscribeModEvents.defs.d.ts.map +1 -1
- package/dist/lexicons/chat/bsky/moderation/subscribeModEvents.defs.js +11 -0
- package/dist/lexicons/chat/bsky/moderation/subscribeModEvents.defs.js.map +1 -1
- package/dist/lexicons/chat/bsky/moderation.d.ts +4 -0
- package/dist/lexicons/chat/bsky/moderation.d.ts.map +1 -1
- package/dist/lexicons/chat/bsky/moderation.js +4 -0
- package/dist/lexicons/chat/bsky/moderation.js.map +1 -1
- package/dist/lexicons/chat/bsky.d.ts +1 -0
- package/dist/lexicons/chat/bsky.d.ts.map +1 -1
- package/dist/lexicons/chat/bsky.js +1 -0
- package/dist/lexicons/chat/bsky.js.map +1 -1
- package/dist/lexicons/com/atproto/lexicon/schema.defs.d.ts.map +1 -1
- package/dist/lexicons/com/germnetwork/declaration.defs.d.ts.map +1 -1
- package/dist/lexicons/site/standard/document.defs.d.ts.map +1 -1
- package/dist/lexicons/site/standard/graph/recommend.defs.d.ts.map +1 -1
- package/dist/lexicons/site/standard/graph/subscription.defs.d.ts.map +1 -1
- package/dist/lexicons/site/standard/publication.defs.d.ts.map +1 -1
- package/dist/lexicons/site/standard/theme/basic.defs.d.ts.map +1 -1
- package/dist/lexicons/tools/ozone/moderation/defs.defs.d.ts +11 -3
- package/dist/lexicons/tools/ozone/moderation/defs.defs.d.ts.map +1 -1
- package/dist/lexicons/tools/ozone/moderation/defs.defs.js +9 -0
- package/dist/lexicons/tools/ozone/moderation/defs.defs.js.map +1 -1
- package/dist/lexicons/tools/ozone/moderation/queryEvents.defs.d.ts +2 -2
- package/dist/lexicons/tools/ozone/moderation/queryEvents.defs.d.ts.map +1 -1
- package/dist/lexicons/tools/ozone/moderation/queryEvents.defs.js.map +1 -1
- package/dist/lexicons/tools/ozone/moderation/queryStatuses.defs.d.ts +2 -2
- package/dist/lexicons/tools/ozone/moderation/queryStatuses.defs.d.ts.map +1 -1
- package/dist/lexicons/tools/ozone/moderation/queryStatuses.defs.js.map +1 -1
- package/dist/read-after-write/viewer.d.ts +2 -2
- package/package.json +10 -10
- package/src/account-manager/account-manager.ts +105 -7
- package/src/account-manager/helpers/account.ts +15 -7
- package/src/account-manager/oauth-store.ts +76 -18
- package/src/actor-store/record/reader.ts +1 -1
- package/src/api/com/atproto/admin/updateAccountHandle.ts +37 -46
- package/src/api/com/atproto/identity/updateHandle.ts +45 -76
- package/src/api/com/atproto/repo/getRecord.ts +3 -3
- package/src/api/com/atproto/repo/putRecord.ts +2 -2
- package/src/context.ts +12 -9
- package/tests/_puppeteer.ts +8 -2
- package/tests/account-manager.test.ts +123 -50
- package/tests/oauth.test.ts +5 -5
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -1,97 +1,66 @@
|
|
|
1
1
|
import { DAY, MINUTE } from '@atproto/common'
|
|
2
|
-
import {
|
|
2
|
+
import { MethodRateLimit, Server } from '@atproto/xrpc-server'
|
|
3
|
+
import { AccessOutput, OAuthOutput } from '../../../../auth-output.js'
|
|
3
4
|
import { AppContext } from '../../../../context.js'
|
|
4
5
|
import { com } from '../../../../lexicons/index.js'
|
|
5
|
-
import { httpLogger } from '../../../../logger.js'
|
|
6
6
|
|
|
7
7
|
export default function (server: Server, ctx: AppContext) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
{
|
|
17
|
-
durationMs: 5 * MINUTE,
|
|
18
|
-
points: 10,
|
|
19
|
-
calcKey: ({ auth }) => auth.credentials.did,
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
durationMs: DAY,
|
|
23
|
-
points: 50,
|
|
24
|
-
calcKey: ({ auth }) => auth.credentials.did,
|
|
25
|
-
},
|
|
26
|
-
],
|
|
27
|
-
handler: async ({ auth, input, req }) => {
|
|
28
|
-
const requester = auth.credentials.did
|
|
8
|
+
const { entrywayClient } = ctx
|
|
9
|
+
|
|
10
|
+
const auth = ctx.authVerifier.authorization({
|
|
11
|
+
checkTakedown: true,
|
|
12
|
+
authorize: (permissions) => {
|
|
13
|
+
permissions.assertIdentity({ attr: 'handle' })
|
|
14
|
+
},
|
|
15
|
+
})
|
|
29
16
|
|
|
30
|
-
|
|
17
|
+
const rateLimit: MethodRateLimit<AccessOutput | OAuthOutput> = [
|
|
18
|
+
{
|
|
19
|
+
durationMs: 5 * MINUTE,
|
|
20
|
+
points: 10,
|
|
21
|
+
calcKey: ({ auth }) => auth.credentials.did,
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
durationMs: DAY,
|
|
25
|
+
points: 50,
|
|
26
|
+
calcKey: ({ auth }) => auth.credentials.did,
|
|
27
|
+
},
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
if (entrywayClient) {
|
|
31
|
+
server.add(com.atproto.identity.updateHandle, {
|
|
32
|
+
auth,
|
|
33
|
+
rateLimit,
|
|
34
|
+
handler: async ({ auth, input, req }) => {
|
|
31
35
|
const { headers } = await ctx.entrywayAuthHeaders(
|
|
32
36
|
req,
|
|
33
37
|
auth.credentials.did,
|
|
34
38
|
com.atproto.identity.updateHandle.$lxm,
|
|
35
39
|
)
|
|
36
|
-
|
|
40
|
+
|
|
41
|
+
// The full flow is:
|
|
37
42
|
// -> entryway(identity.updateHandle) [update handle, submit plc op]
|
|
38
43
|
// -> pds(admin.updateAccountHandle) [track handle, sequence handle update]
|
|
39
|
-
await
|
|
44
|
+
await entrywayClient.xrpc(com.atproto.identity.updateHandle, {
|
|
40
45
|
headers,
|
|
41
46
|
body: {
|
|
42
47
|
handle: input.body.handle,
|
|
43
48
|
// @ts-expect-error "did" is not in the schema
|
|
44
|
-
did:
|
|
49
|
+
did: auth.credentials.did,
|
|
45
50
|
},
|
|
46
51
|
})
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
includeDeactivated: true,
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
if (!account) {
|
|
61
|
-
if (requester.startsWith('did:plc:')) {
|
|
62
|
-
await ctx.plcClient.updateHandle(
|
|
63
|
-
requester,
|
|
64
|
-
ctx.plcRotationKey,
|
|
65
|
-
handle,
|
|
66
|
-
)
|
|
67
|
-
} else {
|
|
68
|
-
const resolved = await ctx.idResolver.did.resolveAtprotoData(
|
|
69
|
-
requester,
|
|
70
|
-
true,
|
|
71
|
-
)
|
|
72
|
-
if (resolved.handle !== handle) {
|
|
73
|
-
throw new InvalidRequestError(
|
|
74
|
-
'DID is not properly configured for handle',
|
|
75
|
-
)
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
await ctx.accountManager.updateHandle(requester, handle)
|
|
79
|
-
} else {
|
|
80
|
-
// if we found an account with matching handle, check if it is the same as requester
|
|
81
|
-
// if so emit an identity event, otherwise error.
|
|
82
|
-
if (account.did !== requester) {
|
|
83
|
-
throw new InvalidRequestError(`Handle already taken: ${handle}`)
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
try {
|
|
88
|
-
await ctx.sequencer.sequenceIdentityEvt(requester, handle)
|
|
89
|
-
} catch (err) {
|
|
90
|
-
httpLogger.error(
|
|
91
|
-
{ err, did: requester, handle },
|
|
92
|
-
'failed to sequence handle update',
|
|
52
|
+
},
|
|
53
|
+
})
|
|
54
|
+
} else {
|
|
55
|
+
server.add(com.atproto.identity.updateHandle, {
|
|
56
|
+
auth,
|
|
57
|
+
rateLimit,
|
|
58
|
+
handler: async ({ auth, input }) => {
|
|
59
|
+
await ctx.accountManager.updateHandle(
|
|
60
|
+
auth.credentials.did,
|
|
61
|
+
input.body.handle,
|
|
93
62
|
)
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
63
|
+
},
|
|
64
|
+
})
|
|
65
|
+
}
|
|
97
66
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { atUri } from '@atproto/lex'
|
|
2
2
|
import { InvalidRequestError, Server } from '@atproto/xrpc-server'
|
|
3
3
|
import { AppContext } from '../../../../context.js'
|
|
4
4
|
import { com } from '../../../../lexicons/index.js'
|
|
@@ -11,7 +11,7 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
11
11
|
|
|
12
12
|
// fetch from pds if available, if not then fetch from appview
|
|
13
13
|
if (did) {
|
|
14
|
-
const uri =
|
|
14
|
+
const uri = atUri(did, collection, rkey)
|
|
15
15
|
const record = await ctx.actorStore.read(did, (store) =>
|
|
16
16
|
store.record.getRecord(uri, cid ?? null),
|
|
17
17
|
)
|
|
@@ -24,7 +24,7 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
24
24
|
return {
|
|
25
25
|
encoding: 'application/json' as const,
|
|
26
26
|
body: {
|
|
27
|
-
uri
|
|
27
|
+
uri,
|
|
28
28
|
cid: record.cid,
|
|
29
29
|
value: record.value,
|
|
30
30
|
},
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { atUri } from '@atproto/lex'
|
|
1
2
|
import {
|
|
2
3
|
LegacyBlobRef,
|
|
3
4
|
LexMap,
|
|
@@ -5,7 +6,6 @@ import {
|
|
|
5
6
|
isLegacyBlobRef,
|
|
6
7
|
parseCid,
|
|
7
8
|
} from '@atproto/lex-data'
|
|
8
|
-
import { AtUri } from '@atproto/syntax'
|
|
9
9
|
import {
|
|
10
10
|
AuthRequiredError,
|
|
11
11
|
InvalidRequestError,
|
|
@@ -90,7 +90,7 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
90
90
|
})
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
const uri =
|
|
93
|
+
const uri = atUri(did, collection, rkey)
|
|
94
94
|
const swapCommitCid = swapCommit ? parseCid(swapCommit) : undefined
|
|
95
95
|
const swapRecordCid =
|
|
96
96
|
typeof swapRecord === 'string' ? parseCid(swapRecord) : swapRecord
|
package/src/context.ts
CHANGED
|
@@ -267,25 +267,28 @@ export class AppContext {
|
|
|
267
267
|
backgroundQueue,
|
|
268
268
|
})
|
|
269
269
|
|
|
270
|
+
const plcRotationKey =
|
|
271
|
+
secrets.plcRotationKey.provider === 'kms'
|
|
272
|
+
? await KmsKeypair.load({
|
|
273
|
+
keyId: secrets.plcRotationKey.keyId,
|
|
274
|
+
})
|
|
275
|
+
: await crypto.Secp256k1Keypair.import(
|
|
276
|
+
secrets.plcRotationKey.privateKeyHex,
|
|
277
|
+
)
|
|
278
|
+
|
|
270
279
|
const accountManager = new AccountManager(
|
|
271
280
|
idResolver,
|
|
272
281
|
jwtSecretKey,
|
|
273
282
|
mailer,
|
|
283
|
+
sequencer,
|
|
284
|
+
plcClient,
|
|
285
|
+
plcRotationKey,
|
|
274
286
|
cfg.service.did,
|
|
275
287
|
cfg.identity.serviceHandleDomains,
|
|
276
288
|
cfg.db,
|
|
277
289
|
)
|
|
278
290
|
await accountManager.migrateOrThrow()
|
|
279
291
|
|
|
280
|
-
const plcRotationKey =
|
|
281
|
-
secrets.plcRotationKey.provider === 'kms'
|
|
282
|
-
? await KmsKeypair.load({
|
|
283
|
-
keyId: secrets.plcRotationKey.keyId,
|
|
284
|
-
})
|
|
285
|
-
: await crypto.Secp256k1Keypair.import(
|
|
286
|
-
secrets.plcRotationKey.privateKeyHex,
|
|
287
|
-
)
|
|
288
|
-
|
|
289
292
|
const localViewer = LocalViewer.creator(
|
|
290
293
|
accountManager,
|
|
291
294
|
imageUrlBuilder,
|
package/tests/_puppeteer.ts
CHANGED
|
@@ -85,6 +85,8 @@ export class PageHelper implements AsyncDisposable {
|
|
|
85
85
|
async typeIn(selector: string, text: string) {
|
|
86
86
|
const elementHandle = await this.getVisibleElement(selector)
|
|
87
87
|
elementHandle.focus()
|
|
88
|
+
await elementHandle.click({ clickCount: 3 }) // Select all existing text
|
|
89
|
+
await elementHandle.press('Backspace') // Clear the input
|
|
88
90
|
await elementHandle.type(text)
|
|
89
91
|
return elementHandle
|
|
90
92
|
}
|
|
@@ -93,16 +95,20 @@ export class PageHelper implements AsyncDisposable {
|
|
|
93
95
|
return this.typeIn(`input[name=${JSON.stringify(name)}]`, text)
|
|
94
96
|
}
|
|
95
97
|
|
|
96
|
-
async ensureTextVisibility(text: string, tag = 'p') {
|
|
98
|
+
async ensureTextVisibility(text: string, tag = 'p', timeout = 5_000) {
|
|
97
99
|
await this.page.waitForSelector(
|
|
98
100
|
`${tag}::-p-text(${JSON.stringify(text)})`,
|
|
99
101
|
{
|
|
100
102
|
visible: true,
|
|
101
|
-
timeout
|
|
103
|
+
timeout,
|
|
102
104
|
},
|
|
103
105
|
)
|
|
104
106
|
}
|
|
105
107
|
|
|
108
|
+
async ensureNotification(text: string) {
|
|
109
|
+
return this.ensureTextVisibility(text, 'div')
|
|
110
|
+
}
|
|
111
|
+
|
|
106
112
|
protected async getVisibleElement(selector: string) {
|
|
107
113
|
const elementHandle = await this.page.waitForSelector(selector, {
|
|
108
114
|
visible: true,
|
|
@@ -18,7 +18,7 @@ describe('account manager', () => {
|
|
|
18
18
|
// For debugging:
|
|
19
19
|
// headless: false,
|
|
20
20
|
// devtools: true,
|
|
21
|
-
// slowMo:
|
|
21
|
+
// slowMo: 25,
|
|
22
22
|
})
|
|
23
23
|
|
|
24
24
|
network = await TestNetworkNoAppView.create({
|
|
@@ -34,6 +34,10 @@ describe('account manager', () => {
|
|
|
34
34
|
})
|
|
35
35
|
})
|
|
36
36
|
|
|
37
|
+
afterEach(async () => {
|
|
38
|
+
await network.processAll()
|
|
39
|
+
})
|
|
40
|
+
|
|
37
41
|
afterAll(async () => {
|
|
38
42
|
await network?.close()
|
|
39
43
|
await browser?.close()
|
|
@@ -44,7 +48,7 @@ describe('account manager', () => {
|
|
|
44
48
|
|
|
45
49
|
await page.goto(new URL('/account', network.pds.url))
|
|
46
50
|
|
|
47
|
-
await page.assertTitle(`
|
|
51
|
+
await page.assertTitle(`Se connecter`)
|
|
48
52
|
|
|
49
53
|
await page.clickOnText('Créer un nouveau compte')
|
|
50
54
|
|
|
@@ -55,12 +59,14 @@ describe('account manager', () => {
|
|
|
55
59
|
await page.typeInInput('email', 'bob@test.com')
|
|
56
60
|
await page.typeInInput('password', 'bob-pass')
|
|
57
61
|
|
|
58
|
-
await page.clickOnText(
|
|
62
|
+
await page.clickOnText('Inscription')
|
|
59
63
|
|
|
60
|
-
await page.
|
|
64
|
+
await page.waitForNetworkIdle()
|
|
61
65
|
|
|
62
66
|
await page.ensureTextVisibility('bob.test', 'span')
|
|
63
67
|
await page.ensureTextVisibility('Votre compte Atmosphère est hébergé chez')
|
|
68
|
+
|
|
69
|
+
await page.assertTitle('Mon compte Atmosphère')
|
|
64
70
|
})
|
|
65
71
|
|
|
66
72
|
it('allows switching accounts', async () => {
|
|
@@ -68,7 +74,7 @@ describe('account manager', () => {
|
|
|
68
74
|
|
|
69
75
|
await page.goto(new URL('/account', network.pds.url))
|
|
70
76
|
|
|
71
|
-
await page.assertTitle(
|
|
77
|
+
await page.assertTitle('Mon compte Atmosphère')
|
|
72
78
|
|
|
73
79
|
await page.clickOnAriaLabel(`Sélecteur de compte`)
|
|
74
80
|
await page.clickOnText('Sélectionner un autre compte')
|
|
@@ -93,18 +99,13 @@ describe('account manager', () => {
|
|
|
93
99
|
|
|
94
100
|
await page.goto(new URL('/account', network.pds.url))
|
|
95
101
|
|
|
96
|
-
await page.assertTitle(
|
|
102
|
+
await page.assertTitle('Mon compte Atmosphère')
|
|
97
103
|
|
|
98
104
|
await page.ensureTextVisibility('bob.test', 'span')
|
|
99
105
|
|
|
100
|
-
await
|
|
101
|
-
(
|
|
102
|
-
|
|
103
|
-
},
|
|
104
|
-
(err) => {
|
|
105
|
-
expect(err).toBeInstanceOf(Error)
|
|
106
|
-
},
|
|
107
|
-
)
|
|
106
|
+
await expect(async () => {
|
|
107
|
+
await page.ensureTextVisibility('alice.test', 'span', 500)
|
|
108
|
+
}).rejects.toThrow('Waiting for selector')
|
|
108
109
|
})
|
|
109
110
|
|
|
110
111
|
it('allows changing the password', async () => {
|
|
@@ -112,9 +113,11 @@ describe('account manager', () => {
|
|
|
112
113
|
|
|
113
114
|
await page.goto(new URL('/account', network.pds.url))
|
|
114
115
|
|
|
115
|
-
await page.assertTitle(
|
|
116
|
+
await page.assertTitle('Mon compte Atmosphère')
|
|
116
117
|
|
|
117
|
-
await page.clickOnText('
|
|
118
|
+
await page.clickOnText('Compte utilisateur', 'a')
|
|
119
|
+
|
|
120
|
+
await page.clickOnText('Mot de passe')
|
|
118
121
|
|
|
119
122
|
using sendResetPasswordMock = jest
|
|
120
123
|
.spyOn(network.pds.ctx.mailer, 'sendResetPassword')
|
|
@@ -122,7 +125,7 @@ describe('account manager', () => {
|
|
|
122
125
|
// noop
|
|
123
126
|
})
|
|
124
127
|
|
|
125
|
-
await page.clickOnText('Envoyer le code')
|
|
128
|
+
await page.clickOnText('Envoyer le code de vérification')
|
|
126
129
|
|
|
127
130
|
await page.waitForNetworkIdle()
|
|
128
131
|
|
|
@@ -137,20 +140,23 @@ describe('account manager', () => {
|
|
|
137
140
|
await page.typeInInput('code', params.token)
|
|
138
141
|
await page.typeInInput('password', 'bob-new-pass')
|
|
139
142
|
|
|
140
|
-
await page.clickOnText('
|
|
143
|
+
await page.clickOnText('Valider')
|
|
141
144
|
|
|
142
|
-
await page.
|
|
143
|
-
'Réinitialisation du mot de passe réussie',
|
|
144
|
-
'div',
|
|
145
|
-
)
|
|
145
|
+
await page.ensureNotification('Réinitialisation du mot de passe réussie')
|
|
146
146
|
})
|
|
147
147
|
|
|
148
|
-
it('allows
|
|
148
|
+
it('allows verifying the email address', async () => {
|
|
149
149
|
await using page = await PageHelper.from(browser, { languages })
|
|
150
150
|
|
|
151
151
|
await page.goto(new URL('/account', network.pds.url))
|
|
152
152
|
|
|
153
|
-
await page.
|
|
153
|
+
await page.assertTitle('Mon compte Atmosphère')
|
|
154
|
+
|
|
155
|
+
await page.clickOnText('Compte utilisateur', 'a')
|
|
156
|
+
|
|
157
|
+
await page.ensureTextVisibility('Votre adresse email doit être vérifiée.')
|
|
158
|
+
|
|
159
|
+
await page.clickOnText('Vérifier')
|
|
154
160
|
|
|
155
161
|
using sendConfirmEmailMock = jest
|
|
156
162
|
.spyOn(network.pds.ctx.mailer, 'sendConfirmEmail')
|
|
@@ -158,7 +164,7 @@ describe('account manager', () => {
|
|
|
158
164
|
// noop
|
|
159
165
|
})
|
|
160
166
|
|
|
161
|
-
await page.clickOnText('Envoyer le code', 'button')
|
|
167
|
+
await page.clickOnText('Envoyer le code de vérification', 'button')
|
|
162
168
|
|
|
163
169
|
await page.waitForNetworkIdle()
|
|
164
170
|
|
|
@@ -171,9 +177,31 @@ describe('account manager', () => {
|
|
|
171
177
|
})
|
|
172
178
|
|
|
173
179
|
await page.typeInInput('code', params.token)
|
|
174
|
-
await page.clickOnText('
|
|
180
|
+
await page.clickOnText('Valider')
|
|
181
|
+
|
|
182
|
+
await page.ensureNotification('Adresse email vérifiée')
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
it('allows changing the username', async () => {
|
|
186
|
+
await using page = await PageHelper.from(browser, { languages })
|
|
187
|
+
|
|
188
|
+
await page.goto(new URL('/account', network.pds.url))
|
|
189
|
+
|
|
190
|
+
await page.assertTitle('Mon compte Atmosphère')
|
|
175
191
|
|
|
176
|
-
await page.
|
|
192
|
+
await page.clickOnText('Compte utilisateur', 'a')
|
|
193
|
+
|
|
194
|
+
await page.clickOnText("Nom d'utilisateur")
|
|
195
|
+
|
|
196
|
+
await page.clickOnText("Utiliser un nom d'utilisateur par défaut")
|
|
197
|
+
|
|
198
|
+
await page.typeInInput('handle', 'bob-renamed')
|
|
199
|
+
|
|
200
|
+
await page.clickOnText('Valider')
|
|
201
|
+
|
|
202
|
+
await page.waitForNetworkIdle()
|
|
203
|
+
|
|
204
|
+
await page.ensureTextVisibility('bob-renamed.test', 'span')
|
|
177
205
|
})
|
|
178
206
|
|
|
179
207
|
it('allows changing the email address', async () => {
|
|
@@ -181,7 +209,11 @@ describe('account manager', () => {
|
|
|
181
209
|
|
|
182
210
|
await page.goto(new URL('/account', network.pds.url))
|
|
183
211
|
|
|
184
|
-
await page.
|
|
212
|
+
await page.assertTitle('Mon compte Atmosphère')
|
|
213
|
+
|
|
214
|
+
await page.clickOnText('Compte utilisateur', 'a')
|
|
215
|
+
|
|
216
|
+
await page.clickOnText('Adresse email')
|
|
185
217
|
|
|
186
218
|
using sendUpdateEmailMock = jest
|
|
187
219
|
.spyOn(network.pds.ctx.mailer, 'sendUpdateEmail')
|
|
@@ -189,13 +221,11 @@ describe('account manager', () => {
|
|
|
189
221
|
// noop
|
|
190
222
|
})
|
|
191
223
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
.
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
await page.clickOnText('Envoyer le code', 'button')
|
|
224
|
+
const emailInput = await page.typeInInput(
|
|
225
|
+
'email',
|
|
226
|
+
'bob-new-email@example.com',
|
|
227
|
+
)
|
|
228
|
+
emailInput.press('Enter')
|
|
199
229
|
|
|
200
230
|
await page.waitForNetworkIdle()
|
|
201
231
|
|
|
@@ -207,26 +237,69 @@ describe('account manager', () => {
|
|
|
207
237
|
token: expect.any(String),
|
|
208
238
|
})
|
|
209
239
|
|
|
210
|
-
await page.typeInInput('code', updateParams.token)
|
|
211
|
-
|
|
212
|
-
|
|
240
|
+
const codeInput = await page.typeInInput('code', updateParams.token)
|
|
241
|
+
codeInput.press('Enter')
|
|
242
|
+
|
|
243
|
+
await page.ensureNotification("Modification de l'adresse email réussie")
|
|
213
244
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
245
|
+
// The email needs to be verified again
|
|
246
|
+
await page.ensureTextVisibility('Votre adresse email doit être vérifiée.')
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
it('allows signing out & signing back in', async () => {
|
|
250
|
+
await using page = await PageHelper.from(browser, { languages })
|
|
251
|
+
|
|
252
|
+
await page.goto(new URL('/account', network.pds.url))
|
|
253
|
+
|
|
254
|
+
await page.assertTitle('Mon compte Atmosphère')
|
|
255
|
+
|
|
256
|
+
await page.clickOnAriaLabel(`Sélecteur de compte`)
|
|
257
|
+
await page.clickOnText('Se déconnecter')
|
|
258
|
+
|
|
259
|
+
await page.assertTitle(`Se connecter`)
|
|
260
|
+
await page.clickOnText('Se connecter')
|
|
261
|
+
|
|
262
|
+
await page.clickOnText('Se souvenir de ce compte sur cet appareil', 'label')
|
|
263
|
+
await page.typeInInput('username', 'bob-new-email@example.com')
|
|
264
|
+
const input = await page.typeInInput('password', 'bob-new-pass')
|
|
265
|
+
|
|
266
|
+
input.press('Enter')
|
|
267
|
+
|
|
268
|
+
await page.ensureTextVisibility('bob-renamed.test', 'span')
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
it('does not ask for a token when changing a non-verified email', async () => {
|
|
272
|
+
await using page = await PageHelper.from(browser, { languages })
|
|
273
|
+
|
|
274
|
+
await page.goto(new URL('/account', network.pds.url))
|
|
275
|
+
|
|
276
|
+
await page.assertTitle('Mon compte Atmosphère')
|
|
277
|
+
|
|
278
|
+
await page.clickOnText('Compte utilisateur', 'a')
|
|
279
|
+
|
|
280
|
+
await page.clickOnText('Adresse email')
|
|
281
|
+
|
|
282
|
+
using sendUpdateEmailMock = jest
|
|
283
|
+
.spyOn(network.pds.ctx.mailer, 'sendUpdateEmail')
|
|
284
|
+
.mockImplementation(async () => {
|
|
285
|
+
// noop
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
const emailInput = await page.typeInInput(
|
|
289
|
+
'email',
|
|
290
|
+
'bob-new-email@example.com',
|
|
217
291
|
)
|
|
292
|
+
emailInput.press('Enter')
|
|
218
293
|
|
|
219
|
-
|
|
294
|
+
await page.waitForNetworkIdle()
|
|
220
295
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
token: expect.any(String),
|
|
225
|
-
})
|
|
296
|
+
expect(sendUpdateEmailMock).not.toHaveBeenCalled()
|
|
297
|
+
|
|
298
|
+
await page.clickOnText('Plus tard')
|
|
226
299
|
|
|
227
|
-
await page.
|
|
228
|
-
await page.clickOnText('Soumettre')
|
|
300
|
+
await page.ensureNotification("Modification de l'adresse email réussie")
|
|
229
301
|
|
|
230
|
-
|
|
302
|
+
// The email needs to be verified again
|
|
303
|
+
await page.ensureTextVisibility('Votre adresse email doit être vérifiée.')
|
|
231
304
|
})
|
|
232
305
|
})
|
package/tests/oauth.test.ts
CHANGED
|
@@ -25,7 +25,7 @@ describe('oauth', () => {
|
|
|
25
25
|
// For debugging:
|
|
26
26
|
// headless: false,
|
|
27
27
|
// devtools: true,
|
|
28
|
-
// slowMo:
|
|
28
|
+
// slowMo: 25,
|
|
29
29
|
})
|
|
30
30
|
|
|
31
31
|
network = await TestNetworkNoAppView.create({
|
|
@@ -71,7 +71,7 @@ describe('oauth', () => {
|
|
|
71
71
|
|
|
72
72
|
await page.navigationClick(`Sign up with ${new URL(network.pds.url).host}`)
|
|
73
73
|
|
|
74
|
-
await page.assertTitle(
|
|
74
|
+
await page.assertTitle('Inscription')
|
|
75
75
|
|
|
76
76
|
await page.typeInInput('handle', 'bob')
|
|
77
77
|
|
|
@@ -80,10 +80,10 @@ describe('oauth', () => {
|
|
|
80
80
|
await page.typeInInput('email', 'bob@test.com')
|
|
81
81
|
await page.typeInInput('password', 'bob-pass')
|
|
82
82
|
|
|
83
|
-
await page.clickOnText(
|
|
83
|
+
await page.clickOnText('Inscription')
|
|
84
84
|
|
|
85
85
|
await page.ensureTextVisibility(
|
|
86
|
-
`L'application demande un contrôle total sur votre identité, ce qui signifie qu'elle pourrait casser de façon permanente, ou même usurper, votre compte. N'
|
|
86
|
+
`L'application demande un contrôle total sur votre identité, ce qui signifie qu'elle pourrait casser de façon permanente, ou même usurper, votre compte. N'autorisez l'accès qu'aux applications auxquelles vous faites vraiment confiance.`,
|
|
87
87
|
)
|
|
88
88
|
|
|
89
89
|
// Make sure the new account is propagated to the PLC directory, allowing
|
|
@@ -112,7 +112,7 @@ describe('oauth', () => {
|
|
|
112
112
|
|
|
113
113
|
await page.navigationClick(`Login with ${new URL(network.pds.url).host}`)
|
|
114
114
|
|
|
115
|
-
await page.assertTitle(
|
|
115
|
+
await page.assertTitle('Se connecter')
|
|
116
116
|
|
|
117
117
|
// Cancel the OAuth flow:
|
|
118
118
|
await page.navigationClick('Annuler')
|