@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.
Files changed (47) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/api/com/atproto/server/getSession.d.ts.map +1 -1
  3. package/dist/api/com/atproto/server/getSession.js +26 -4
  4. package/dist/api/com/atproto/server/getSession.js.map +1 -1
  5. package/dist/auth-verifier.d.ts +23 -18
  6. package/dist/auth-verifier.d.ts.map +1 -1
  7. package/dist/auth-verifier.js +14 -19
  8. package/dist/auth-verifier.js.map +1 -1
  9. package/dist/config/config.js +1 -1
  10. package/dist/config/config.js.map +1 -1
  11. package/dist/context.d.ts.map +1 -1
  12. package/dist/context.js +5 -1
  13. package/dist/context.js.map +1 -1
  14. package/dist/lexicon/lexicons.d.ts +68 -6
  15. package/dist/lexicon/lexicons.d.ts.map +1 -1
  16. package/dist/lexicon/lexicons.js +35 -2
  17. package/dist/lexicon/lexicons.js.map +1 -1
  18. package/dist/lexicon/types/app/bsky/feed/defs.d.ts +6 -0
  19. package/dist/lexicon/types/app/bsky/feed/defs.d.ts.map +1 -1
  20. package/dist/lexicon/types/app/bsky/feed/defs.js.map +1 -1
  21. package/dist/lexicon/types/app/bsky/feed/getFeedSkeleton.d.ts +2 -0
  22. package/dist/lexicon/types/app/bsky/feed/getFeedSkeleton.d.ts.map +1 -1
  23. package/dist/lexicon/types/app/bsky/feed/like.d.ts +1 -0
  24. package/dist/lexicon/types/app/bsky/feed/like.d.ts.map +1 -1
  25. package/dist/lexicon/types/app/bsky/feed/like.js.map +1 -1
  26. package/dist/lexicon/types/app/bsky/feed/repost.d.ts +1 -0
  27. package/dist/lexicon/types/app/bsky/feed/repost.d.ts.map +1 -1
  28. package/dist/lexicon/types/app/bsky/feed/repost.js.map +1 -1
  29. package/dist/lexicon/types/app/bsky/notification/listNotifications.d.ts +2 -2
  30. package/dist/lexicon/types/app/bsky/notification/listNotifications.d.ts.map +1 -1
  31. package/dist/lexicon/types/app/bsky/notification/listNotifications.js.map +1 -1
  32. package/dist/pipethrough.d.ts.map +1 -1
  33. package/dist/pipethrough.js +1 -0
  34. package/dist/pipethrough.js.map +1 -1
  35. package/package.json +8 -8
  36. package/src/api/com/atproto/server/getSession.ts +44 -9
  37. package/src/auth-verifier.ts +44 -44
  38. package/src/config/config.ts +1 -1
  39. package/src/context.ts +5 -1
  40. package/src/lexicon/lexicons.ts +38 -2
  41. package/src/lexicon/types/app/bsky/feed/defs.ts +6 -0
  42. package/src/lexicon/types/app/bsky/feed/getFeedSkeleton.ts +2 -0
  43. package/src/lexicon/types/app/bsky/feed/like.ts +1 -0
  44. package/src/lexicon/types/app/bsky/feed/repost.ts +1 -0
  45. package/src/lexicon/types/app/bsky/notification/listNotifications.ts +3 -1
  46. package/src/pipethrough.ts +1 -0
  47. package/tests/proxied/__snapshots__/views.test.ts.snap +114 -108
@@ -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 RefreshOutput = {
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, token, tokenId, audience } =
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, token, tokenId, audience } =
193
- await this.validateRefreshToken(ctx, { clockTolerance: Infinity })
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 (ctx: ReqCtx): Promise<UserServiceAuthOutput | AccessOutput> => {
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<AccessOutput> {
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 tokenScopes = new Set(result.claims.scope?.split(' '))
505
+ const oauthScopes = new Set(result.claims.scope?.split(' '))
502
506
 
503
- if (!tokenScopes.has('transition:generic')) {
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 = tokenScopes.has('transition:chat.bsky')
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: 'access',
537
+ type: 'oauth',
534
538
  did: result.claims.sub,
535
539
  scope: scopeEquivalent,
536
- audience: this.dids.pds,
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, token, audience } = await this.validateBearerToken(
562
- ctx,
563
- scopes,
564
- { audience: this.dids.pds, typ: 'at+jwt' },
565
- )
566
- const isPrivileged = [
567
- AuthScope.Access,
568
- AuthScope.AppPassPrivileged,
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) {
@@ -272,7 +272,7 @@ export const envToCfg = (env: ServerEnvironment): ServerConfig => {
272
272
  }
273
273
  : undefined,
274
274
  branding: {
275
- name: env.serviceName ?? 'Personal PDS',
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: ['transition:generic', 'transition:chat.bsky'],
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,
@@ -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
- "Expected values are 'like', 'repost', 'follow', 'mention', 'reply', 'quote', 'starterpack-joined', 'verified', and 'unverified'.",
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
@@ -19,6 +19,7 @@ export interface Record {
19
19
  $type: 'app.bsky.feed.like'
20
20
  subject: ComAtprotoRepoStrongRef.Main
21
21
  createdAt: string
22
+ via?: ComAtprotoRepoStrongRef.Main
22
23
  [k: string]: unknown
23
24
  }
24
25
 
@@ -19,6 +19,7 @@ export interface Record {
19
19
  $type: 'app.bsky.feed.repost'
20
20
  subject: ComAtprotoRepoStrongRef.Main
21
21
  createdAt: string
22
+ via?: ComAtprotoRepoStrongRef.Main
22
23
  [k: string]: unknown
23
24
  }
24
25
 
@@ -67,7 +67,7 @@ export interface Notification {
67
67
  uri: string
68
68
  cid: string
69
69
  author: AppBskyActorDefs.ProfileView
70
- /** Expected values are 'like', 'repost', 'follow', 'mention', 'reply', 'quote', 'starterpack-joined', 'verified', and 'unverified'. */
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 }
@@ -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,