@atcute/xrpc-server 0.1.12 → 1.0.0

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 (37) hide show
  1. package/README.md +81 -20
  2. package/dist/auth/jwt-creator.d.ts +4 -2
  3. package/dist/auth/jwt-creator.d.ts.map +1 -1
  4. package/dist/auth/jwt-creator.js +1 -1
  5. package/dist/auth/jwt-creator.js.map +1 -1
  6. package/dist/auth/jwt-verifier.d.ts +69 -8
  7. package/dist/auth/jwt-verifier.d.ts.map +1 -1
  8. package/dist/auth/jwt-verifier.js +130 -19
  9. package/dist/auth/jwt-verifier.js.map +1 -1
  10. package/dist/auth/jwt.d.ts +7 -2
  11. package/dist/auth/jwt.d.ts.map +1 -1
  12. package/dist/auth/jwt.js +14 -7
  13. package/dist/auth/jwt.js.map +1 -1
  14. package/dist/main/router.d.ts +32 -4
  15. package/dist/main/router.d.ts.map +1 -1
  16. package/dist/main/router.js +63 -10
  17. package/dist/main/router.js.map +1 -1
  18. package/dist/main/types/operation.d.ts +8 -0
  19. package/dist/main/types/operation.d.ts.map +1 -1
  20. package/dist/main/types/websocket.d.ts +8 -0
  21. package/dist/main/types/websocket.d.ts.map +1 -1
  22. package/dist/main/utils/websocket-mock.d.ts.map +1 -1
  23. package/dist/main/utils/websocket-mock.js +3 -0
  24. package/dist/main/utils/websocket-mock.js.map +1 -1
  25. package/dist/main/xrpc-error.d.ts +55 -15
  26. package/dist/main/xrpc-error.d.ts.map +1 -1
  27. package/dist/main/xrpc-error.js +66 -26
  28. package/dist/main/xrpc-error.js.map +1 -1
  29. package/lib/auth/jwt-creator.ts +5 -3
  30. package/lib/auth/jwt-verifier.ts +205 -25
  31. package/lib/auth/jwt.ts +21 -10
  32. package/lib/main/router.ts +95 -14
  33. package/lib/main/types/operation.ts +8 -0
  34. package/lib/main/types/websocket.ts +8 -0
  35. package/lib/main/utils/websocket-mock.ts +3 -0
  36. package/lib/main/xrpc-error.ts +107 -44
  37. package/package.json +19 -15
package/README.md CHANGED
@@ -135,7 +135,7 @@ router.addQuery(ComExampleGetPost, {
135
135
 
136
136
  const post = await db.getPost(params.uri);
137
137
  if (!post) {
138
- throw new XRPCError({ status: 400, error: 'InvalidRequest', description: `post not found` });
138
+ throw new XRPCError({ status: 400, error: 'InvalidRequest', message: `post not found` });
139
139
  }
140
140
 
141
141
  return json(post);
@@ -147,6 +147,50 @@ convenience subclasses are also available: `InvalidRequestError`, `AuthRequiredE
147
147
  `ForbiddenError`, `RateLimitExceededError`, `InternalServerError`, `UpstreamFailureError`,
148
148
  `NotEnoughResourcesError`, `UpstreamTimeoutError`.
149
149
 
150
+ `AuthRequiredError` accepts a `wwwAuthenticate` option that auto-formats an RFC 7235
151
+ `WWW-Authenticate` header on the response (and appends `access-control-expose-headers` so browsers
152
+ can read it from CORS responses):
153
+
154
+ ```ts
155
+ import { AuthRequiredError } from '@atcute/xrpc-server';
156
+
157
+ throw new AuthRequiredError({
158
+ message: 'invalid token',
159
+ wwwAuthenticate: { scheme: 'Bearer', params: { error: 'BadJwtSignature' } },
160
+ });
161
+ ```
162
+
163
+ ### observing errors
164
+
165
+ for logs or metrics, use the `onError` / `onSocketError` router options. these are fire-and-forget
166
+ hooks invoked alongside response generation, and they are NOT called for client-induced errors
167
+ (aborted requests, `XRPCError`, `XRPCSubscriptionError`) — only for bugs worth reporting:
168
+
169
+ ```ts
170
+ const router = new XRPCRouter({
171
+ onError({ error, request }) {
172
+ reportToSentry(error, { url: request.url });
173
+ },
174
+ onSocketError({ error, request }) {
175
+ reportToSentry(error, { url: request.url, subscription: true });
176
+ },
177
+ });
178
+ ```
179
+
180
+ ### health check
181
+
182
+ the router can optionally answer `/xrpc/_health` if you pass `handleHealthCheck`. this endpoint is
183
+ non-standard — consumers decide the response shape and status.
184
+
185
+ ```ts
186
+ const router = new XRPCRouter({
187
+ async handleHealthCheck() {
188
+ const healthy = await pingDatabase();
189
+ return Response.json({ status: healthy ? 'ok' : 'degraded' }, { status: healthy ? 200 : 503 });
190
+ },
191
+ });
192
+ ```
193
+
150
194
  ### subscriptions
151
195
 
152
196
  subscriptions provide real-time streaming over WebSocket. they require a runtime-specific adapter:
@@ -201,7 +245,7 @@ router.addSubscription(ComExampleSubscribe, {
201
245
  if (params.cursor && isCursorTooOld(params.cursor)) {
202
246
  throw new XRPCSubscriptionError({
203
247
  error: 'FutureCursor',
204
- description: `cursor is too old`,
248
+ message: `cursor is too old`,
205
249
  });
206
250
  }
207
251
 
@@ -210,6 +254,11 @@ router.addSubscription(ComExampleSubscribe, {
210
254
  });
211
255
  ```
212
256
 
257
+ backpressure is handled per adapter: each adapter's `create*WebSocket()` factory accepts
258
+ `highWaterMark` / `lowWaterMark` options (default 250 KB / 50 KB) that throttle the send loop when
259
+ the outgoing buffer grows. the Cloudflare Workers adapter does not apply backpressure — the runtime
260
+ does not expose the outgoing WebSocket buffer.
261
+
213
262
  ### service authentication
214
263
 
215
264
  the `@atcute/xrpc-server/auth` subpackage provides utilities for service-to-service authentication
@@ -218,8 +267,7 @@ using JWTs.
218
267
  verifying incoming JWTs:
219
268
 
220
269
  ```ts
221
- import { AuthRequiredError } from '@atcute/xrpc-server';
222
- import { ServiceJwtVerifier, type VerifiedJwt } from '@atcute/xrpc-server/auth';
270
+ import { ServiceJwtVerifier } from '@atcute/xrpc-server/auth';
223
271
  import {
224
272
  CompositeDidDocumentResolver,
225
273
  PlcDidDocumentResolver,
@@ -227,7 +275,10 @@ import {
227
275
  } from '@atcute/identity-resolver';
228
276
 
229
277
  const jwtVerifier = new ServiceJwtVerifier({
230
- serviceDid: 'did:web:my-service.example.com',
278
+ // list of audience values this service accepts. during the transition to proposal 0014
279
+ // (atproto service auth audience), configure both the bare DID and the DID-with-service-ref
280
+ // form so that tokens from older issuers keep working alongside new ones.
281
+ acceptAudiences: ['did:web:my-service.example.com', 'did:web:my-service.example.com#atproto_pds'],
231
282
  resolver: new CompositeDidDocumentResolver({
232
283
  methods: {
233
284
  plc: new PlcDidDocumentResolver(),
@@ -236,28 +287,38 @@ const jwtVerifier = new ServiceJwtVerifier({
236
287
  }),
237
288
  });
238
289
 
239
- const verifyServiceAuth = async (request: Request, lxm: string): Promise<VerifiedJwt> => {
240
- const authHeader = request.headers.get('authorization');
241
- if (!authHeader?.startsWith('Bearer ')) {
242
- throw new AuthRequiredError({ description: `missing or invalid authorization header` });
243
- }
244
-
245
- const result = await jwtVerifier.verify(authHeader.slice(7), { lxm });
246
- if (!result.ok) {
247
- throw new AuthRequiredError({ description: result.error.description });
248
- }
249
-
250
- return result.value;
251
- };
252
-
253
290
  router.addQuery(ComExampleProtectedEndpoint, {
254
291
  async handler({ request }) {
255
- const auth = await verifyServiceAuth(request, 'com.example.protectedEndpoint');
292
+ const auth = await jwtVerifier.verifyRequest(request, { lxm: 'com.example.protectedEndpoint' });
256
293
  return json({ caller: auth.issuer });
257
294
  },
258
295
  });
259
296
  ```
260
297
 
298
+ `verifyRequest` parses the `Authorization: Bearer` header, verifies the token, and throws an
299
+ `AuthRequiredError` with a populated `WWW-Authenticate: Bearer error="…"` challenge on every failure
300
+ path. it forwards `request.signal` into DID resolution so aborted requests don't keep network calls
301
+ alive.
302
+
303
+ additional options tune verification:
304
+
305
+ ```ts
306
+ const jwtVerifier = new ServiceJwtVerifier({
307
+ acceptAudiences: [...],
308
+ resolver: ...,
309
+ maxAge: 300, // max token lifetime window in seconds (default 300)
310
+ clockLeeway: 5, // leeway applied to nbf/exp comparisons (default 5)
311
+ replayStore: { // optional replay protection; requires jti in tokens
312
+ async check({ iss, jti }, ttlSeconds) {
313
+ const key = `${iss}:${jti}`;
314
+ const isNew = await redis.setnx(key, '1');
315
+ if (isNew === 1) await redis.expire(key, ttlSeconds);
316
+ return isNew === 1;
317
+ },
318
+ },
319
+ });
320
+ ```
321
+
261
322
  creating outgoing JWTs:
262
323
 
263
324
  ```ts
@@ -1,10 +1,12 @@
1
1
  import type { PrivateKey } from '@atcute/crypto';
2
2
  import type { Did, Nsid } from '@atcute/lexicons';
3
+ import type { AtprotoAudience } from '@atcute/lexicons/syntax';
3
4
  export interface CreateServiceJwtOptions {
4
5
  keypair: PrivateKey;
5
6
  issuer: Did;
6
- audience: Did;
7
- lxm: Nsid | null;
7
+ /** audience is either a bare DID or a DID with service fragment (e.g. `did:web:x.example#svc`) */
8
+ audience: Did | AtprotoAudience;
9
+ lxm: Nsid;
8
10
  issuedAt?: number;
9
11
  expiresIn?: number;
10
12
  }
@@ -1 +1 @@
1
- {"version":3,"file":"jwt-creator.d.ts","sourceRoot":"","sources":["../../lib/auth/jwt-creator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAQlD,MAAM,WAAW,uBAAuB;IACvC,OAAO,EAAE,UAAU,CAAC;IACpB,MAAM,EAAE,GAAG,CAAC;IACZ,QAAQ,EAAE,GAAG,CAAC;IACd,GAAG,EAAE,IAAI,GAAG,IAAI,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,gBAAgB,YAAmB,uBAAuB,KAAG,OAAO,CAAC,MAAM,CA4BvF,CAAC"}
1
+ {"version":3,"file":"jwt-creator.d.ts","sourceRoot":"","sources":["../../lib/auth/jwt-creator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAQ/D,MAAM,WAAW,uBAAuB;IACvC,OAAO,EAAE,UAAU,CAAC;IACpB,MAAM,EAAE,GAAG,CAAC;IACZ,kGAAkG;IAClG,QAAQ,EAAE,GAAG,GAAG,eAAe,CAAC;IAChC,GAAG,EAAE,IAAI,CAAC;IACV,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,gBAAgB,YAAmB,uBAAuB,KAAG,OAAO,CAAC,MAAM,CA4BvF,CAAC"}
@@ -15,7 +15,7 @@ export const createServiceJwt = async (options) => {
15
15
  iat: issuedAt,
16
16
  iss: options.issuer,
17
17
  jti: nanoid(24),
18
- lxm: options.lxm ?? undefined,
18
+ lxm: options.lxm,
19
19
  };
20
20
  const headerB64 = encodeJwtPortion(header);
21
21
  const payloadB64 = encodeJwtPortion(payload);
@@ -1 +1 @@
1
- {"version":3,"file":"jwt-creator.js","sourceRoot":"","sources":["../../lib/auth/jwt-creator.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEhD,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAahC,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,EAAE,OAAgC,EAAmB,EAAE;IAC3F,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAEhC,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC;IACpE,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;IAEtD,MAAM,MAAM,GAAc;QACzB,GAAG,EAAE,KAAK;QACV,GAAG,EAAE,OAAO,CAAC,MAAM;KACnB,CAAC;IAEF,MAAM,OAAO,GAAe;QAC3B,GAAG,EAAE,OAAO,CAAC,QAAQ;QACrB,GAAG,EAAE,QAAQ,GAAG,SAAS;QACzB,GAAG,EAAE,QAAQ;QACb,GAAG,EAAE,OAAO,CAAC,MAAM;QACnB,GAAG,EAAE,MAAM,CAAC,EAAE,CAAC;QACf,GAAG,EAAE,OAAO,CAAC,GAAG,IAAI,SAAS;KAC7B,CAAC;IAEF,MAAM,SAAS,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAC3C,MAAM,UAAU,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,GAAG,SAAS,IAAI,UAAU,EAAE,CAAC;IAE7C,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;IAC1D,MAAM,YAAY,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC;IAE5C,OAAO,GAAG,OAAO,IAAI,YAAY,EAAE,CAAC;AACrC,CAAC,CAAC;AAEF,MAAM,gBAAgB,GAAG,CAAC,IAAa,EAAU,EAAE;IAClD,OAAO,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACtD,CAAC,CAAC"}
1
+ {"version":3,"file":"jwt-creator.js","sourceRoot":"","sources":["../../lib/auth/jwt-creator.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEhD,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAchC,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,EAAE,OAAgC,EAAmB,EAAE;IAC3F,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAEhC,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC;IACpE,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;IAEtD,MAAM,MAAM,GAAc;QACzB,GAAG,EAAE,KAAK;QACV,GAAG,EAAE,OAAO,CAAC,MAAM;KACnB,CAAC;IAEF,MAAM,OAAO,GAAe;QAC3B,GAAG,EAAE,OAAO,CAAC,QAAQ;QACrB,GAAG,EAAE,QAAQ,GAAG,SAAS;QACzB,GAAG,EAAE,QAAQ;QACb,GAAG,EAAE,OAAO,CAAC,MAAM;QACnB,GAAG,EAAE,MAAM,CAAC,EAAE,CAAC;QACf,GAAG,EAAE,OAAO,CAAC,GAAG;KAChB,CAAC;IAEF,MAAM,SAAS,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAC3C,MAAM,UAAU,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,GAAG,SAAS,IAAI,UAAU,EAAE,CAAC;IAE7C,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;IAC1D,MAAM,YAAY,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC;IAE5C,OAAO,GAAG,OAAO,IAAI,YAAY,EAAE,CAAC;AACrC,CAAC,CAAC;AAEF,MAAM,gBAAgB,GAAG,CAAC,IAAa,EAAU,EAAE;IAClD,OAAO,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACtD,CAAC,CAAC"}
@@ -1,24 +1,85 @@
1
1
  import { type DidDocumentResolver } from '@atcute/identity-resolver';
2
2
  import type { Did, Nsid } from '@atcute/lexicons';
3
- import type { Result } from '../types/misc.ts';
4
- import type { AuthError } from './types.ts';
3
+ import type { AtprotoAudience } from '@atcute/lexicons/syntax';
4
+ /**
5
+ * replay-protection store for service JWTs. when configured on a verifier,
6
+ * tokens must carry a `jti` claim and the verifier consults this store to
7
+ * reject duplicates.
8
+ */
9
+ export interface ReplayStore {
10
+ /**
11
+ * record a `(iss, jti)` pair seen now.
12
+ *
13
+ * @param key issuer + token identifier; implementations decide how to
14
+ * encode this into a storage key.
15
+ * @param ttlSeconds how long the entry must be retained. implementations
16
+ * are free to retain it for longer.
17
+ * @returns `true` if the pair was previously unseen (token is unique),
18
+ * `false` if the pair has been recorded before (replay).
19
+ */
20
+ check(key: {
21
+ iss: Did;
22
+ jti: string;
23
+ }, ttlSeconds: number): Promise<boolean>;
24
+ }
5
25
  export interface ServiceJwtVerifierOptions {
6
- serviceDid: Did | null;
26
+ /**
27
+ * list of `aud` values accepted by this service; each entry is a bare DID or a DID with
28
+ * service fragment (e.g. `did:web:x.example#svc`), and incoming tokens must exact-match any entry.
29
+ *
30
+ * pass `null` to skip audience validation (accept any audience). an empty array rejects every
31
+ * audience, which is useful when a service wants to fail closed until configured.
32
+ */
33
+ acceptAudiences: (Did | AtprotoAudience)[] | null;
7
34
  resolver: DidDocumentResolver;
35
+ /**
36
+ * maximum token lifetime window in seconds. rejects tokens whose `exp` is
37
+ * more than this far in the future or whose `iat` is more than this far in
38
+ * the past. defaults to 300 (5 minutes), matching atproto convention.
39
+ */
40
+ maxAge?: number;
41
+ /**
42
+ * clock-skew leeway in seconds applied to `exp` and `nbf` comparisons.
43
+ * defaults to 5 seconds.
44
+ */
45
+ clockLeeway?: number;
46
+ /**
47
+ * optional replay-protection store. when provided, tokens must carry a
48
+ * `jti` claim and the verifier rejects any `(iss, jti)` the store reports
49
+ * as previously seen.
50
+ */
51
+ replayStore?: ReplayStore;
8
52
  }
9
53
  export interface VerifyJwtOptions {
10
- lxm: Nsid | Nsid[] | null;
54
+ lxm: Nsid | Nsid[];
55
+ /** abort signal forwarded to DID resolution; falls back to `request.signal` in `verifyRequest`. */
56
+ signal?: AbortSignal;
11
57
  }
12
58
  export interface VerifiedJwt {
13
59
  issuer: Did;
14
- audience: Did;
15
- lxm: string | undefined;
60
+ audience: Did | AtprotoAudience;
61
+ lxm: Nsid;
16
62
  }
17
63
  export declare class ServiceJwtVerifier {
18
64
  #private;
19
65
  didDocResolver: DidDocumentResolver;
20
- serviceDid: Did | null;
66
+ acceptAudiences: (Did | AtprotoAudience)[] | null;
67
+ maxAge: number;
68
+ clockLeeway: number;
69
+ replayStore?: ReplayStore;
21
70
  constructor(options: ServiceJwtVerifierOptions);
22
- verify(jwtString: string, options?: VerifyJwtOptions): Promise<Result<VerifiedJwt, AuthError>>;
71
+ /**
72
+ * parse the Authorization header, verify the bearer token, and return the
73
+ * validated claims. throws {@link AuthRequiredError} with a populated
74
+ * `WWW-Authenticate: Bearer` challenge on every failure path.
75
+ *
76
+ * @param request incoming request; `request.signal` is forwarded to DID
77
+ * resolution unless `options.signal` overrides it.
78
+ * @param options verification options; `lxm` restricts which lexicon
79
+ * methods the token is allowed to invoke.
80
+ * @throws {AuthRequiredError} on missing header, malformed token,
81
+ * signature mismatch, audience/lxm rejection, replay, or expiry.
82
+ */
83
+ verifyRequest(request: Request, options: VerifyJwtOptions): Promise<VerifiedJwt>;
23
84
  }
24
85
  //# sourceMappingURL=jwt-verifier.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"jwt-verifier.d.ts","sourceRoot":"","sources":["../../lib/auth/jwt-verifier.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAGlD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAG/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,MAAM,WAAW,yBAAyB;IACzC,UAAU,EAAE,GAAG,GAAG,IAAI,CAAC;IACvB,QAAQ,EAAE,mBAAmB,CAAC;CAC9B;AAED,MAAM,WAAW,gBAAgB;IAChC,GAAG,EAAE,IAAI,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,WAAW;IAC3B,MAAM,EAAE,GAAG,CAAC;IACZ,QAAQ,EAAE,GAAG,CAAC;IACd,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;CACxB;AAED,qBAAa,kBAAkB;;IAC9B,cAAc,EAAE,mBAAmB,CAAC;IACpC,UAAU,EAAE,GAAG,GAAG,IAAI,CAAC;IAEvB,YAAY,OAAO,EAAE,yBAAyB,EAG7C;IA6DK,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC,CAuHnG;CACD"}
1
+ {"version":3,"file":"jwt-verifier.d.ts","sourceRoot":"","sources":["../../lib/auth/jwt-verifier.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAa/D;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC3B;;;;;;;;;OASG;IACH,KAAK,CAAC,GAAG,EAAE;QAAE,GAAG,EAAE,GAAG,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC5E;AAED,MAAM,WAAW,yBAAyB;IACzC;;;;;;OAMG;IACH,eAAe,EAAE,CAAC,GAAG,GAAG,eAAe,CAAC,EAAE,GAAG,IAAI,CAAC;IAClD,QAAQ,EAAE,mBAAmB,CAAC;IAC9B;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAChC,GAAG,EAAE,IAAI,GAAG,IAAI,EAAE,CAAC;IACnB,mGAAmG;IACnG,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC3B,MAAM,EAAE,GAAG,CAAC;IACZ,QAAQ,EAAE,GAAG,GAAG,eAAe,CAAC;IAChC,GAAG,EAAE,IAAI,CAAC;CACV;AAID,qBAAa,kBAAkB;;IAC9B,cAAc,EAAE,mBAAmB,CAAC;IACpC,eAAe,EAAE,CAAC,GAAG,GAAG,eAAe,CAAC,EAAE,GAAG,IAAI,CAAC;IAClD,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,WAAW,CAAC;IAE1B,YAAY,OAAO,EAAE,yBAAyB,EAM7C;IAED;;;;;;;;;;;OAWG;IACG,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,WAAW,CAAC,CAsBrF;CAmQD"}
@@ -1,37 +1,78 @@
1
1
  import { getPublicKeyFromDidController, verifySig } from '@atcute/crypto';
2
- import { getAtprotoVerificationMaterial } from '@atcute/identity';
2
+ import { getVerificationMaterial } from '@atcute/identity';
3
3
  import {} from '@atcute/identity-resolver';
4
4
  import * as uint8arrays from '@atcute/uint8array';
5
+ import { AuthRequiredError } from '../main/xrpc-error.js';
5
6
  import { parseJwt } from './jwt.js';
7
+ /** only `#atproto` is accepted as a signing key identifier for now */
8
+ const DEFAULT_KID = '#atproto';
9
+ const BEARER_PREFIX = 'Bearer ';
6
10
  export class ServiceJwtVerifier {
7
11
  didDocResolver;
8
- serviceDid;
12
+ acceptAudiences;
13
+ maxAge;
14
+ clockLeeway;
15
+ replayStore;
9
16
  constructor(options) {
10
17
  this.didDocResolver = options.resolver;
11
- this.serviceDid = options.serviceDid;
18
+ this.acceptAudiences = options.acceptAudiences;
19
+ this.maxAge = options.maxAge ?? 5 * 60;
20
+ this.clockLeeway = options.clockLeeway ?? 5;
21
+ this.replayStore = options.replayStore;
12
22
  }
13
- async #getSigningKey(issuer, noCache) {
23
+ /**
24
+ * parse the Authorization header, verify the bearer token, and return the
25
+ * validated claims. throws {@link AuthRequiredError} with a populated
26
+ * `WWW-Authenticate: Bearer` challenge on every failure path.
27
+ *
28
+ * @param request incoming request; `request.signal` is forwarded to DID
29
+ * resolution unless `options.signal` overrides it.
30
+ * @param options verification options; `lxm` restricts which lexicon
31
+ * methods the token is allowed to invoke.
32
+ * @throws {AuthRequiredError} on missing header, malformed token,
33
+ * signature mismatch, audience/lxm rejection, replay, or expiry.
34
+ */
35
+ async verifyRequest(request, options) {
36
+ const authorization = request.headers.get('authorization');
37
+ if (authorization === null) {
38
+ throw new AuthRequiredError({
39
+ message: 'authorization header required',
40
+ wwwAuthenticate: { scheme: 'Bearer' },
41
+ });
42
+ }
43
+ if (!authorization.startsWith(BEARER_PREFIX)) {
44
+ throw authError({ error: 'MissingBearer', description: 'expected a bearer token' });
45
+ }
46
+ const token = authorization.slice(BEARER_PREFIX.length).trim();
47
+ const signal = options.signal ?? request.signal;
48
+ const result = await this.#verifyToken(token, { lxm: options.lxm, signal });
49
+ if (!result.ok) {
50
+ throw authError(result.error);
51
+ }
52
+ return result.value;
53
+ }
54
+ async #getSigningKey(issuer, kid, noCache, signal) {
14
55
  let didDocument;
15
56
  let key;
16
57
  try {
17
- didDocument = await this.didDocResolver.resolve(issuer, { noCache });
58
+ didDocument = await this.didDocResolver.resolve(issuer, { noCache, signal });
18
59
  }
19
60
  catch {
20
61
  return {
21
62
  ok: false,
22
63
  error: {
23
- error: 'UnresolvedDidDocument',
64
+ error: 'DidResolutionFailed',
24
65
  description: `failed to retrieve did document for ${issuer}`,
25
66
  },
26
67
  };
27
68
  }
28
- const controller = getAtprotoVerificationMaterial(didDocument);
69
+ const controller = getVerificationMaterial(didDocument, kid);
29
70
  if (!controller) {
30
71
  return {
31
72
  ok: false,
32
73
  error: {
33
74
  error: 'BadJwtIssuer',
34
- description: `${issuer} does not have an atproto verification material`,
75
+ description: `${issuer} does not have a ${kid} verification material`,
35
76
  },
36
77
  };
37
78
  }
@@ -43,7 +84,7 @@ export class ServiceJwtVerifier {
43
84
  ok: false,
44
85
  error: {
45
86
  error: 'BadJwtIssuer',
46
- description: `${issuer} has invalid atproto verification material`,
87
+ description: `${issuer} has invalid ${kid} verification material`,
47
88
  },
48
89
  };
49
90
  }
@@ -66,8 +107,8 @@ export class ServiceJwtVerifier {
66
107
  };
67
108
  }
68
109
  }
69
- async verify(jwtString, options) {
70
- const parsed = parseJwt(jwtString);
110
+ async #verifyToken(token, options) {
111
+ const parsed = parseJwt(token);
71
112
  if (!parsed.ok) {
72
113
  return parsed;
73
114
  }
@@ -85,7 +126,30 @@ export class ServiceJwtVerifier {
85
126
  };
86
127
  }
87
128
  }
88
- if (Date.now() / 1_000 > payload.exp) {
129
+ // resolve the `kid` header (defaulting to `#atproto`) and restrict to the set of
130
+ // identifiers this verifier knows how to look up in the issuer's DID document.
131
+ // matches proposal 0014's "safe default" for SDKs.
132
+ const kid = header.kid ?? DEFAULT_KID;
133
+ if (kid !== DEFAULT_KID) {
134
+ return {
135
+ ok: false,
136
+ error: {
137
+ error: 'BadJwtIssuer',
138
+ description: `unsupported signing key identifier (${kid})`,
139
+ },
140
+ };
141
+ }
142
+ const now = Math.floor(Date.now() / 1_000);
143
+ if (payload.nbf !== undefined && now < payload.nbf - this.clockLeeway) {
144
+ return {
145
+ ok: false,
146
+ error: {
147
+ error: 'JwtNotYetValid',
148
+ description: `jwt is not yet valid`,
149
+ },
150
+ };
151
+ }
152
+ if (now > payload.exp + this.clockLeeway) {
89
153
  return {
90
154
  ok: false,
91
155
  error: {
@@ -94,17 +158,30 @@ export class ServiceJwtVerifier {
94
158
  },
95
159
  };
96
160
  }
97
- if (this.serviceDid !== null && this.serviceDid !== payload.aud) {
161
+ // prevent issuers from minting very long-lived tokens: the configured max-age
162
+ // window bounds how far `exp` can be in the future and how far `iat` can be in
163
+ // the past.
164
+ if (payload.exp - now > this.maxAge || (payload.iat !== undefined && now - payload.iat > this.maxAge)) {
98
165
  return {
99
166
  ok: false,
100
167
  error: {
101
- error: 'BadJwtAudience',
102
- description: `jwt audience does not match (expected ${this.serviceDid})`,
168
+ error: 'JwtTooOld',
169
+ description: `jwt exceeds maximum age (${this.maxAge}s)`,
103
170
  },
104
171
  };
105
172
  }
106
- if (options?.lxm != null &&
107
- (typeof options.lxm === 'string' ? options.lxm !== payload.lxm : !options.lxm.includes(payload.lxm))) {
173
+ if (this.acceptAudiences !== null && !this.acceptAudiences.includes(payload.aud)) {
174
+ return {
175
+ ok: false,
176
+ error: {
177
+ error: 'InvalidAudience',
178
+ description: this.acceptAudiences.length === 0
179
+ ? `jwt audience does not match (no audiences accepted)`
180
+ : `jwt audience does not match (expected one of: ${this.acceptAudiences.join(', ')})`,
181
+ },
182
+ };
183
+ }
184
+ if (typeof options.lxm === 'string' ? options.lxm !== payload.lxm : !options.lxm.includes(payload.lxm)) {
108
185
  return {
109
186
  ok: false,
110
187
  error: {
@@ -113,7 +190,20 @@ export class ServiceJwtVerifier {
113
190
  },
114
191
  };
115
192
  }
116
- const key = await this.#getSigningKey(payload.iss, false);
193
+ let jti;
194
+ if (this.replayStore !== undefined) {
195
+ if (payload.jti === undefined) {
196
+ return {
197
+ ok: false,
198
+ error: {
199
+ error: 'BadJwt',
200
+ description: `jwt is missing the jti claim (required for replay protection)`,
201
+ },
202
+ };
203
+ }
204
+ jti = payload.jti;
205
+ }
206
+ const key = await this.#getSigningKey(payload.iss, kid, false, options.signal);
117
207
  if (!key.ok) {
118
208
  return key;
119
209
  }
@@ -127,7 +217,7 @@ export class ServiceJwtVerifier {
127
217
  }
128
218
  if (!isValid) {
129
219
  // try again, uncached
130
- const freshKey = await this.#getSigningKey(payload.iss, true);
220
+ const freshKey = await this.#getSigningKey(payload.iss, kid, true, options.signal);
131
221
  if (!freshKey.ok) {
132
222
  return freshKey;
133
223
  }
@@ -160,6 +250,21 @@ export class ServiceJwtVerifier {
160
250
  },
161
251
  };
162
252
  }
253
+ // replay-store check runs after signature verification so forged tokens
254
+ // can't burn entries (memory dos) or evict legitimate `(iss, jti)` pairs
255
+ // before the real request lands.
256
+ if (this.replayStore !== undefined && jti !== undefined) {
257
+ const unique = await this.replayStore.check({ iss: payload.iss, jti }, this.maxAge);
258
+ if (!unique) {
259
+ return {
260
+ ok: false,
261
+ error: {
262
+ error: 'NonceNotUnique',
263
+ description: `jwt has been used before`,
264
+ },
265
+ };
266
+ }
267
+ }
163
268
  return {
164
269
  ok: true,
165
270
  value: {
@@ -170,4 +275,10 @@ export class ServiceJwtVerifier {
170
275
  };
171
276
  }
172
277
  }
278
+ const authError = (err) => {
279
+ return new AuthRequiredError({
280
+ message: err.description,
281
+ wwwAuthenticate: { scheme: 'Bearer', params: { error: err.error } },
282
+ });
283
+ };
173
284
  //# sourceMappingURL=jwt-verifier.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"jwt-verifier.js","sourceRoot":"","sources":["../../lib/auth/jwt-verifier.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"jwt-verifier.js","sourceRoot":"","sources":["../../lib/auth/jwt-verifier.ts"],"names":[],"mappings":"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"}
@@ -1,16 +1,21 @@
1
1
  import type { Did, Nsid } from '@atcute/lexicons';
2
+ import { type AtprotoAudience } from '@atcute/lexicons/syntax';
2
3
  import type { Result } from '../types/misc.ts';
3
4
  import type { AuthError } from './types.ts';
4
5
  export interface JwtHeader {
5
6
  typ?: string;
6
7
  alg: string;
8
+ /** signing key identifier; a DID fragment, defaults to `#atproto` when absent */
9
+ kid?: string;
7
10
  }
8
11
  export interface JwtPayload {
9
12
  iss: Did;
10
- aud: Did;
13
+ aud: Did | AtprotoAudience;
11
14
  exp: number;
12
15
  iat?: number;
13
- lxm?: Nsid;
16
+ /** not-before time; token is invalid before this unix timestamp */
17
+ nbf?: number;
18
+ lxm: Nsid;
14
19
  jti?: string;
15
20
  }
16
21
  export interface ParsedJwt {
@@ -1 +1 @@
1
- {"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../../lib/auth/jwt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAOlD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAO5C,MAAM,WAAW,SAAS;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACZ;AAOD,MAAM,WAAW,UAAU;IAC1B,GAAG,EAAE,GAAG,CAAC;IACT,GAAG,EAAE,GAAG,CAAC;IACT,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,IAAI,CAAC;IACX,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAsBD,MAAM,WAAW,SAAS;IACzB,MAAM,EAAE,SAAS,CAAC;IAClB,OAAO,EAAE,UAAU,CAAC;IACpB,OAAO,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC;IACjC,SAAS,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC;CACnC;AAoCD,eAAO,MAAM,QAAQ,cAAe,MAAM,KAAG,MAAM,CAAC,SAAS,EAAE,SAAS,CAsCvE,CAAC"}
1
+ {"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../../lib/auth/jwt.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAiB,KAAK,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAM9E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAU5C,MAAM,WAAW,SAAS;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,iFAAiF;IACjF,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAQD,MAAM,WAAW,UAAU;IAC1B,GAAG,EAAE,GAAG,CAAC;IACT,GAAG,EAAE,GAAG,GAAG,eAAe,CAAC;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,mEAAmE;IACnE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,IAAI,CAAC;IACV,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAwBD,MAAM,WAAW,SAAS;IACzB,MAAM,EAAE,SAAS,CAAC;IAClB,OAAO,EAAE,UAAU,CAAC;IACpB,OAAO,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC;IACjC,SAAS,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC;CACnC;AAoCD,eAAO,MAAM,QAAQ,cAAe,MAAM,KAAG,MAAM,CAAC,SAAS,EAAE,SAAS,CAsCvE,CAAC"}