@atproto/pds 0.4.137 → 0.4.139
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 +25 -0
- package/dist/api/com/atproto/server/getSession.d.ts.map +1 -1
- package/dist/api/com/atproto/server/getSession.js +26 -4
- package/dist/api/com/atproto/server/getSession.js.map +1 -1
- package/dist/auth-verifier.d.ts +23 -18
- package/dist/auth-verifier.d.ts.map +1 -1
- package/dist/auth-verifier.js +14 -19
- package/dist/auth-verifier.js.map +1 -1
- package/dist/config/config.js +1 -1
- package/dist/config/config.js.map +1 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +5 -1
- package/dist/context.js.map +1 -1
- package/dist/lexicon/lexicons.d.ts +68 -6
- package/dist/lexicon/lexicons.d.ts.map +1 -1
- package/dist/lexicon/lexicons.js +35 -2
- package/dist/lexicon/lexicons.js.map +1 -1
- package/dist/lexicon/types/app/bsky/feed/defs.d.ts +6 -0
- package/dist/lexicon/types/app/bsky/feed/defs.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/feed/defs.js.map +1 -1
- package/dist/lexicon/types/app/bsky/feed/getFeedSkeleton.d.ts +2 -0
- package/dist/lexicon/types/app/bsky/feed/getFeedSkeleton.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/feed/like.d.ts +1 -0
- package/dist/lexicon/types/app/bsky/feed/like.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/feed/like.js.map +1 -1
- package/dist/lexicon/types/app/bsky/feed/repost.d.ts +1 -0
- package/dist/lexicon/types/app/bsky/feed/repost.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/feed/repost.js.map +1 -1
- package/dist/lexicon/types/app/bsky/notification/listNotifications.d.ts +2 -2
- package/dist/lexicon/types/app/bsky/notification/listNotifications.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/notification/listNotifications.js.map +1 -1
- package/dist/pipethrough.d.ts.map +1 -1
- package/dist/pipethrough.js +1 -0
- package/dist/pipethrough.js.map +1 -1
- package/package.json +8 -8
- package/src/api/com/atproto/server/getSession.ts +44 -9
- package/src/auth-verifier.ts +44 -44
- package/src/config/config.ts +1 -1
- package/src/context.ts +5 -1
- package/src/lexicon/lexicons.ts +38 -2
- package/src/lexicon/types/app/bsky/feed/defs.ts +6 -0
- package/src/lexicon/types/app/bsky/feed/getFeedSkeleton.ts +2 -0
- package/src/lexicon/types/app/bsky/feed/like.ts +1 -0
- package/src/lexicon/types/app/bsky/feed/repost.ts +1 -0
- package/src/lexicon/types/app/bsky/notification/listNotifications.ts +3 -1
- package/src/pipethrough.ts +1 -0
- package/tests/proxied/__snapshots__/views.test.ts.snap +114 -108
package/src/auth-verifier.ts
CHANGED
@@ -46,17 +46,17 @@ export enum RoleStatus {
|
|
46
46
|
Missing,
|
47
47
|
}
|
48
48
|
|
49
|
-
type NullOutput = {
|
49
|
+
export type NullOutput = {
|
50
50
|
credentials: null
|
51
51
|
}
|
52
52
|
|
53
|
-
type AdminTokenOutput = {
|
53
|
+
export type AdminTokenOutput = {
|
54
54
|
credentials: {
|
55
55
|
type: 'admin_token'
|
56
56
|
}
|
57
57
|
}
|
58
58
|
|
59
|
-
type ModServiceOutput = {
|
59
|
+
export type ModServiceOutput = {
|
60
60
|
credentials: {
|
61
61
|
type: 'mod_service'
|
62
62
|
aud: string
|
@@ -64,29 +64,35 @@ type ModServiceOutput = {
|
|
64
64
|
}
|
65
65
|
}
|
66
66
|
|
67
|
-
type AccessOutput = {
|
67
|
+
export type AccessOutput = {
|
68
68
|
credentials: {
|
69
69
|
type: 'access'
|
70
70
|
did: string
|
71
71
|
scope: AuthScope
|
72
|
-
audience: string | undefined
|
73
72
|
isPrivileged: boolean
|
74
73
|
}
|
75
|
-
artifacts: string
|
76
74
|
}
|
77
75
|
|
78
|
-
type
|
76
|
+
export type OAuthOutput = {
|
77
|
+
credentials: {
|
78
|
+
type: 'oauth'
|
79
|
+
did: string
|
80
|
+
scope: AuthScope
|
81
|
+
isPrivileged: boolean
|
82
|
+
oauthScopes: Set<string>
|
83
|
+
}
|
84
|
+
}
|
85
|
+
|
86
|
+
export type RefreshOutput = {
|
79
87
|
credentials: {
|
80
88
|
type: 'refresh'
|
81
89
|
did: string
|
82
90
|
scope: AuthScope
|
83
|
-
audience: string | undefined
|
84
91
|
tokenId: string
|
85
92
|
}
|
86
|
-
artifacts: string
|
87
93
|
}
|
88
94
|
|
89
|
-
type UserServiceAuthOutput = {
|
95
|
+
export type UserServiceAuthOutput = {
|
90
96
|
credentials: {
|
91
97
|
type: 'user_service_auth'
|
92
98
|
aud: string
|
@@ -139,7 +145,7 @@ export class AuthVerifier {
|
|
139
145
|
|
140
146
|
accessStandard =
|
141
147
|
(opts: Partial<AccessOpts> = {}) =>
|
142
|
-
async (ctx: ReqCtx): Promise<AccessOutput> => {
|
148
|
+
async (ctx: ReqCtx): Promise<AccessOutput | OAuthOutput> => {
|
143
149
|
return this.validateAccessToken(
|
144
150
|
ctx,
|
145
151
|
[
|
@@ -154,7 +160,7 @@ export class AuthVerifier {
|
|
154
160
|
|
155
161
|
accessFull =
|
156
162
|
(opts: Partial<AccessOpts> = {}) =>
|
157
|
-
(ctx: ReqCtx): Promise<AccessOutput> => {
|
163
|
+
(ctx: ReqCtx): Promise<AccessOutput | OAuthOutput> => {
|
158
164
|
return this.validateAccessToken(
|
159
165
|
ctx,
|
160
166
|
[AuthScope.Access, ...(opts.additional ?? [])],
|
@@ -164,7 +170,7 @@ export class AuthVerifier {
|
|
164
170
|
|
165
171
|
accessPrivileged =
|
166
172
|
(opts: Partial<AccessOpts> = {}) =>
|
167
|
-
(ctx: ReqCtx): Promise<AccessOutput> => {
|
173
|
+
(ctx: ReqCtx): Promise<AccessOutput | OAuthOutput> => {
|
168
174
|
return this.validateAccessToken(ctx, [
|
169
175
|
AuthScope.Access,
|
170
176
|
AuthScope.AppPassPrivileged,
|
@@ -173,34 +179,30 @@ export class AuthVerifier {
|
|
173
179
|
}
|
174
180
|
|
175
181
|
refresh = async (ctx: ReqCtx): Promise<RefreshOutput> => {
|
176
|
-
const { did, scope,
|
177
|
-
await this.validateRefreshToken(ctx)
|
182
|
+
const { did, scope, tokenId } = await this.validateRefreshToken(ctx)
|
178
183
|
|
179
184
|
return {
|
180
185
|
credentials: {
|
181
186
|
type: 'refresh',
|
182
187
|
did,
|
183
188
|
scope,
|
184
|
-
audience,
|
185
189
|
tokenId,
|
186
190
|
},
|
187
|
-
artifacts: token,
|
188
191
|
}
|
189
192
|
}
|
190
193
|
|
191
194
|
refreshExpired = async (ctx: ReqCtx): Promise<RefreshOutput> => {
|
192
|
-
const { did, scope,
|
193
|
-
|
195
|
+
const { did, scope, tokenId } = await this.validateRefreshToken(ctx, {
|
196
|
+
clockTolerance: Infinity,
|
197
|
+
})
|
194
198
|
|
195
199
|
return {
|
196
200
|
credentials: {
|
197
201
|
type: 'refresh',
|
198
202
|
did,
|
199
203
|
scope,
|
200
|
-
audience,
|
201
204
|
tokenId,
|
202
205
|
},
|
203
|
-
artifacts: token,
|
204
206
|
}
|
205
207
|
}
|
206
208
|
|
@@ -213,7 +215,7 @@ export class AuthVerifier {
|
|
213
215
|
(opts: Partial<AccessOpts> = {}) =>
|
214
216
|
async (
|
215
217
|
ctx: ReqCtx,
|
216
|
-
): Promise<AccessOutput | AdminTokenOutput | NullOutput> => {
|
218
|
+
): Promise<AccessOutput | OAuthOutput | AdminTokenOutput | NullOutput> => {
|
217
219
|
if (isAccessToken(ctx.req)) {
|
218
220
|
return await this.accessStandard(opts)(ctx)
|
219
221
|
} else if (isBasicToken(ctx.req)) {
|
@@ -258,7 +260,9 @@ export class AuthVerifier {
|
|
258
260
|
|
259
261
|
accessOrUserServiceAuth =
|
260
262
|
(opts: Partial<AccessOpts> = {}) =>
|
261
|
-
async (
|
263
|
+
async (
|
264
|
+
ctx: ReqCtx,
|
265
|
+
): Promise<UserServiceAuthOutput | AccessOutput | OAuthOutput> => {
|
262
266
|
const token = bearerTokenFromReq(ctx.req)
|
263
267
|
if (token) {
|
264
268
|
const payload = jose.decodeJwt(token)
|
@@ -411,10 +415,10 @@ export class AuthVerifier {
|
|
411
415
|
checkTakedown = false,
|
412
416
|
checkDeactivated = false,
|
413
417
|
}: { checkTakedown?: boolean; checkDeactivated?: boolean } = {},
|
414
|
-
): Promise<AccessOutput> {
|
418
|
+
): Promise<AccessOutput | OAuthOutput> {
|
415
419
|
this.setAuthHeaders(ctx)
|
416
420
|
|
417
|
-
let accessOutput: AccessOutput
|
421
|
+
let accessOutput: AccessOutput | OAuthOutput
|
418
422
|
|
419
423
|
const [type] = parseAuthorizationHeader(ctx.req.headers.authorization)
|
420
424
|
switch (type) {
|
@@ -467,7 +471,7 @@ export class AuthVerifier {
|
|
467
471
|
protected async validateDpopAccessToken(
|
468
472
|
ctx: ReqCtx,
|
469
473
|
scopes: AuthScope[],
|
470
|
-
): Promise<
|
474
|
+
): Promise<OAuthOutput> {
|
471
475
|
this.setAuthHeaders(ctx)
|
472
476
|
|
473
477
|
const { req } = ctx
|
@@ -498,16 +502,16 @@ export class AuthVerifier {
|
|
498
502
|
throw new InvalidRequestError('Malformed token', 'InvalidToken')
|
499
503
|
}
|
500
504
|
|
501
|
-
const
|
505
|
+
const oauthScopes = new Set(result.claims.scope?.split(' '))
|
502
506
|
|
503
|
-
if (!
|
507
|
+
if (!oauthScopes.has('transition:generic')) {
|
504
508
|
throw new AuthRequiredError(
|
505
509
|
'Missing required scope: transition:generic',
|
506
510
|
'InvalidToken',
|
507
511
|
)
|
508
512
|
}
|
509
513
|
|
510
|
-
const scopeEquivalent: AuthScope =
|
514
|
+
const scopeEquivalent: AuthScope = oauthScopes.has('transition:chat.bsky')
|
511
515
|
? AuthScope.AppPassPrivileged
|
512
516
|
: AuthScope.AppPass
|
513
517
|
|
@@ -530,13 +534,12 @@ export class AuthVerifier {
|
|
530
534
|
|
531
535
|
return {
|
532
536
|
credentials: {
|
533
|
-
type: '
|
537
|
+
type: 'oauth',
|
534
538
|
did: result.claims.sub,
|
535
539
|
scope: scopeEquivalent,
|
536
|
-
|
540
|
+
oauthScopes,
|
537
541
|
isPrivileged: scopeEquivalent === AuthScope.AppPassPrivileged,
|
538
542
|
},
|
539
|
-
artifacts: result.token,
|
540
543
|
}
|
541
544
|
} catch (err) {
|
542
545
|
// Make sure to include any WWW-Authenticate header in the response
|
@@ -558,24 +561,21 @@ export class AuthVerifier {
|
|
558
561
|
ctx: ReqCtx,
|
559
562
|
scopes: AuthScope[],
|
560
563
|
): Promise<AccessOutput> {
|
561
|
-
const { did, scope
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
const isPrivileged =
|
567
|
-
AuthScope.Access
|
568
|
-
|
569
|
-
].includes(scope)
|
564
|
+
const { did, scope } = await this.validateBearerToken(ctx, scopes, {
|
565
|
+
audience: this.dids.pds,
|
566
|
+
typ: 'at+jwt',
|
567
|
+
})
|
568
|
+
|
569
|
+
const isPrivileged =
|
570
|
+
scope === AuthScope.Access || scope === AuthScope.AppPassPrivileged
|
571
|
+
|
570
572
|
return {
|
571
573
|
credentials: {
|
572
574
|
type: 'access',
|
573
575
|
did,
|
574
576
|
scope,
|
575
|
-
audience,
|
576
577
|
isPrivileged,
|
577
578
|
},
|
578
|
-
artifacts: token,
|
579
579
|
}
|
580
580
|
}
|
581
581
|
|
@@ -632,7 +632,7 @@ export class AuthVerifier {
|
|
632
632
|
}
|
633
633
|
|
634
634
|
isUserOrAdmin(
|
635
|
-
auth: AccessOutput | AdminTokenOutput | NullOutput,
|
635
|
+
auth: AccessOutput | OAuthOutput | AdminTokenOutput | NullOutput,
|
636
636
|
did: string,
|
637
637
|
): boolean {
|
638
638
|
if (!auth.credentials) {
|
package/src/config/config.ts
CHANGED
@@ -272,7 +272,7 @@ export const envToCfg = (env: ServerEnvironment): ServerConfig => {
|
|
272
272
|
}
|
273
273
|
: undefined,
|
274
274
|
branding: {
|
275
|
-
name: env.serviceName ??
|
275
|
+
name: env.serviceName ?? `${hostname} PDS`,
|
276
276
|
logo: env.logoUrl,
|
277
277
|
colors: {
|
278
278
|
light: env.lightColor,
|
package/src/context.ts
CHANGED
@@ -363,7 +363,11 @@ export class AppContext {
|
|
363
363
|
safeFetch,
|
364
364
|
metadata: {
|
365
365
|
protected_resources: [new URL(cfg.oauth.issuer).origin],
|
366
|
-
scopes_supported: [
|
366
|
+
scopes_supported: [
|
367
|
+
'transition:email',
|
368
|
+
'transition:generic',
|
369
|
+
'transition:chat.bsky',
|
370
|
+
],
|
367
371
|
},
|
368
372
|
// If the PDS is both an authorization server & resource server (no
|
369
373
|
// entryway), there is no need to use JWTs as access tokens. Instead,
|
package/src/lexicon/lexicons.ts
CHANGED
@@ -6221,6 +6221,12 @@ export const schemaDict = {
|
|
6221
6221
|
'Context provided by feed generator that may be passed back alongside interactions.',
|
6222
6222
|
maxLength: 2000,
|
6223
6223
|
},
|
6224
|
+
reqId: {
|
6225
|
+
type: 'string',
|
6226
|
+
description:
|
6227
|
+
'Unique identifier per request that may be passed back alongside interactions.',
|
6228
|
+
maxLength: 100,
|
6229
|
+
},
|
6224
6230
|
},
|
6225
6231
|
},
|
6226
6232
|
replyRef: {
|
@@ -6259,6 +6265,14 @@ export const schemaDict = {
|
|
6259
6265
|
type: 'ref',
|
6260
6266
|
ref: 'lex:app.bsky.actor.defs#profileViewBasic',
|
6261
6267
|
},
|
6268
|
+
uri: {
|
6269
|
+
type: 'string',
|
6270
|
+
format: 'at-uri',
|
6271
|
+
},
|
6272
|
+
cid: {
|
6273
|
+
type: 'string',
|
6274
|
+
format: 'cid',
|
6275
|
+
},
|
6262
6276
|
indexedAt: {
|
6263
6277
|
type: 'string',
|
6264
6278
|
format: 'datetime',
|
@@ -6517,6 +6531,12 @@ export const schemaDict = {
|
|
6517
6531
|
'Context on a feed item that was originally supplied by the feed generator on getFeedSkeleton.',
|
6518
6532
|
maxLength: 2000,
|
6519
6533
|
},
|
6534
|
+
reqId: {
|
6535
|
+
type: 'string',
|
6536
|
+
description:
|
6537
|
+
'Unique identifier per request that may be passed back alongside interactions.',
|
6538
|
+
maxLength: 100,
|
6539
|
+
},
|
6520
6540
|
},
|
6521
6541
|
},
|
6522
6542
|
requestLess: {
|
@@ -7065,6 +7085,12 @@ export const schemaDict = {
|
|
7065
7085
|
ref: 'lex:app.bsky.feed.defs#skeletonFeedPost',
|
7066
7086
|
},
|
7067
7087
|
},
|
7088
|
+
reqId: {
|
7089
|
+
type: 'string',
|
7090
|
+
description:
|
7091
|
+
'Unique identifier per request that may be passed back alongside interactions.',
|
7092
|
+
maxLength: 100,
|
7093
|
+
},
|
7068
7094
|
},
|
7069
7095
|
},
|
7070
7096
|
},
|
@@ -7558,6 +7584,10 @@ export const schemaDict = {
|
|
7558
7584
|
type: 'string',
|
7559
7585
|
format: 'datetime',
|
7560
7586
|
},
|
7587
|
+
via: {
|
7588
|
+
type: 'ref',
|
7589
|
+
ref: 'lex:com.atproto.repo.strongRef',
|
7590
|
+
},
|
7561
7591
|
},
|
7562
7592
|
},
|
7563
7593
|
},
|
@@ -7772,6 +7802,10 @@ export const schemaDict = {
|
|
7772
7802
|
type: 'string',
|
7773
7803
|
format: 'datetime',
|
7774
7804
|
},
|
7805
|
+
via: {
|
7806
|
+
type: 'ref',
|
7807
|
+
ref: 'lex:com.atproto.repo.strongRef',
|
7808
|
+
},
|
7775
7809
|
},
|
7776
7810
|
},
|
7777
7811
|
},
|
@@ -7784,7 +7818,7 @@ export const schemaDict = {
|
|
7784
7818
|
main: {
|
7785
7819
|
type: 'query',
|
7786
7820
|
description:
|
7787
|
-
'Find posts matching search criteria, returning views of those posts.',
|
7821
|
+
'Find posts matching search criteria, returning views of those posts. Note that this API endpoint may require authentication (eg, not public) for some service providers and implementations.',
|
7788
7822
|
parameters: {
|
7789
7823
|
type: 'params',
|
7790
7824
|
required: ['q'],
|
@@ -9827,7 +9861,7 @@ export const schemaDict = {
|
|
9827
9861
|
reason: {
|
9828
9862
|
type: 'string',
|
9829
9863
|
description:
|
9830
|
-
|
9864
|
+
'The reason why this notification was delivered - e.g. your post was liked, or you received a new follower.',
|
9831
9865
|
knownValues: [
|
9832
9866
|
'like',
|
9833
9867
|
'repost',
|
@@ -9838,6 +9872,8 @@ export const schemaDict = {
|
|
9838
9872
|
'starterpack-joined',
|
9839
9873
|
'verified',
|
9840
9874
|
'unverified',
|
9875
|
+
'like-via-repost',
|
9876
|
+
'repost-via-repost',
|
9841
9877
|
],
|
9842
9878
|
},
|
9843
9879
|
reasonSubject: {
|
@@ -100,6 +100,8 @@ export interface FeedViewPost {
|
|
100
100
|
reason?: $Typed<ReasonRepost> | $Typed<ReasonPin> | { $type: string }
|
101
101
|
/** Context provided by feed generator that may be passed back alongside interactions. */
|
102
102
|
feedContext?: string
|
103
|
+
/** Unique identifier per request that may be passed back alongside interactions. */
|
104
|
+
reqId?: string
|
103
105
|
}
|
104
106
|
|
105
107
|
const hashFeedViewPost = 'feedViewPost'
|
@@ -140,6 +142,8 @@ export function validateReplyRef<V>(v: V) {
|
|
140
142
|
export interface ReasonRepost {
|
141
143
|
$type?: 'app.bsky.feed.defs#reasonRepost'
|
142
144
|
by: AppBskyActorDefs.ProfileViewBasic
|
145
|
+
uri?: string
|
146
|
+
cid?: string
|
143
147
|
indexedAt: string
|
144
148
|
}
|
145
149
|
|
@@ -376,6 +380,8 @@ export interface Interaction {
|
|
376
380
|
| (string & {})
|
377
381
|
/** Context on a feed item that was originally supplied by the feed generator on getFeedSkeleton. */
|
378
382
|
feedContext?: string
|
383
|
+
/** Unique identifier per request that may be passed back alongside interactions. */
|
384
|
+
reqId?: string
|
379
385
|
}
|
380
386
|
|
381
387
|
const hashInteraction = 'interaction'
|
@@ -29,6 +29,8 @@ export type InputSchema = undefined
|
|
29
29
|
export interface OutputSchema {
|
30
30
|
cursor?: string
|
31
31
|
feed: AppBskyFeedDefs.SkeletonFeedPost[]
|
32
|
+
/** Unique identifier per request that may be passed back alongside interactions. */
|
33
|
+
reqId?: string
|
32
34
|
}
|
33
35
|
|
34
36
|
export type HandlerInput = undefined
|
@@ -67,7 +67,7 @@ export interface Notification {
|
|
67
67
|
uri: string
|
68
68
|
cid: string
|
69
69
|
author: AppBskyActorDefs.ProfileView
|
70
|
-
/**
|
70
|
+
/** The reason why this notification was delivered - e.g. your post was liked, or you received a new follower. */
|
71
71
|
reason:
|
72
72
|
| 'like'
|
73
73
|
| 'repost'
|
@@ -78,6 +78,8 @@ export interface Notification {
|
|
78
78
|
| 'starterpack-joined'
|
79
79
|
| 'verified'
|
80
80
|
| 'unverified'
|
81
|
+
| 'like-via-repost'
|
82
|
+
| 'repost-via-repost'
|
81
83
|
| (string & {})
|
82
84
|
reasonSubject?: string
|
83
85
|
record: { [_ in string]: unknown }
|
package/src/pipethrough.ts
CHANGED
@@ -503,6 +503,7 @@ export const PROTECTED_METHODS = new Set<string>([
|
|
503
503
|
ids.ComAtprotoServerCreateAppPassword,
|
504
504
|
ids.ComAtprotoServerDeactivateAccount,
|
505
505
|
ids.ComAtprotoServerGetAccountInviteCodes,
|
506
|
+
ids.ComAtprotoServerGetSession,
|
506
507
|
ids.ComAtprotoServerListAppPasswords,
|
507
508
|
ids.ComAtprotoServerRequestAccountDelete,
|
508
509
|
ids.ComAtprotoServerRequestEmailConfirmation,
|