@atcute/xrpc-server 0.1.12 → 2.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.
- package/README.md +81 -20
- package/dist/auth/jwt-creator.d.ts +4 -2
- package/dist/auth/jwt-creator.d.ts.map +1 -1
- package/dist/auth/jwt-creator.js +1 -1
- package/dist/auth/jwt-creator.js.map +1 -1
- package/dist/auth/jwt-verifier.d.ts +69 -8
- package/dist/auth/jwt-verifier.d.ts.map +1 -1
- package/dist/auth/jwt-verifier.js +130 -19
- package/dist/auth/jwt-verifier.js.map +1 -1
- package/dist/auth/jwt.d.ts +7 -2
- package/dist/auth/jwt.d.ts.map +1 -1
- package/dist/auth/jwt.js +25 -25
- package/dist/auth/jwt.js.map +1 -1
- package/dist/main/router.d.ts +32 -7
- package/dist/main/router.d.ts.map +1 -1
- package/dist/main/router.js +63 -21
- package/dist/main/router.js.map +1 -1
- package/dist/main/types/operation.d.ts +8 -0
- package/dist/main/types/operation.d.ts.map +1 -1
- package/dist/main/types/websocket.d.ts +8 -0
- package/dist/main/types/websocket.d.ts.map +1 -1
- package/dist/main/utils/websocket-mock.d.ts.map +1 -1
- package/dist/main/utils/websocket-mock.js +3 -0
- package/dist/main/utils/websocket-mock.js.map +1 -1
- package/dist/main/xrpc-error.d.ts +55 -15
- package/dist/main/xrpc-error.d.ts.map +1 -1
- package/dist/main/xrpc-error.js +66 -26
- package/dist/main/xrpc-error.js.map +1 -1
- package/lib/auth/jwt-creator.ts +5 -3
- package/lib/auth/jwt-verifier.ts +205 -25
- package/lib/auth/jwt.ts +42 -29
- package/lib/main/router.ts +95 -42
- package/lib/main/types/operation.ts +8 -0
- package/lib/main/types/websocket.ts +8 -0
- package/lib/main/utils/websocket-mock.ts +3 -0
- package/lib/main/xrpc-error.ts +107 -44
- package/package.json +20 -16
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',
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
|
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:
|
|
7
|
-
|
|
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;
|
|
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"}
|
package/dist/auth/jwt-creator.js
CHANGED
|
@@ -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
|
|
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":"
|
|
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 {
|
|
4
|
-
|
|
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
|
-
|
|
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[]
|
|
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:
|
|
60
|
+
audience: Did | AtprotoAudience;
|
|
61
|
+
lxm: Nsid;
|
|
16
62
|
}
|
|
17
63
|
export declare class ServiceJwtVerifier {
|
|
18
64
|
#private;
|
|
19
65
|
didDocResolver: DidDocumentResolver;
|
|
20
|
-
|
|
66
|
+
acceptAudiences: (Did | AtprotoAudience)[] | null;
|
|
67
|
+
maxAge: number;
|
|
68
|
+
clockLeeway: number;
|
|
69
|
+
replayStore?: ReplayStore;
|
|
21
70
|
constructor(options: ServiceJwtVerifierOptions);
|
|
22
|
-
|
|
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;
|
|
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 {
|
|
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
|
-
|
|
12
|
+
acceptAudiences;
|
|
13
|
+
maxAge;
|
|
14
|
+
clockLeeway;
|
|
15
|
+
replayStore;
|
|
9
16
|
constructor(options) {
|
|
10
17
|
this.didDocResolver = options.resolver;
|
|
11
|
-
this.
|
|
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
|
-
|
|
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: '
|
|
64
|
+
error: 'DidResolutionFailed',
|
|
24
65
|
description: `failed to retrieve did document for ${issuer}`,
|
|
25
66
|
},
|
|
26
67
|
};
|
|
27
68
|
}
|
|
28
|
-
const controller =
|
|
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
|
|
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
|
|
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
|
|
70
|
-
const parsed = parseJwt(
|
|
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
|
-
|
|
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
|
-
|
|
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: '
|
|
102
|
-
description: `jwt
|
|
168
|
+
error: 'JwtTooOld',
|
|
169
|
+
description: `jwt exceeds maximum age (${this.maxAge}s)`,
|
|
103
170
|
},
|
|
104
171
|
};
|
|
105
172
|
}
|
|
106
|
-
if (
|
|
107
|
-
|
|
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
|
-
|
|
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":"AAAA,OAAO,EAAE,6BAA6B,EAAE,SAAS,EAAuB,MAAM,gBAAgB,CAAC;AAC/F,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"jwt-verifier.js","sourceRoot":"","sources":["../../lib/auth/jwt-verifier.ts"],"names":[],"mappings":"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"}
|
package/dist/auth/jwt.d.ts
CHANGED
|
@@ -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
|
-
|
|
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 {
|
package/dist/auth/jwt.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../../lib/auth/jwt.ts"],"names":[],"mappings":"
|
|
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;AAW5C,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;AAyBD,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"}
|