@atproto/xrpc-server 0.4.2 → 0.4.4-next.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/CHANGELOG.md +18 -0
- package/LICENSE.txt +1 -1
- package/dist/auth.d.ts +3 -2
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +124 -0
- package/dist/auth.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -53377
- package/dist/index.js.map +1 -7
- package/dist/logger.d.ts +1 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +7 -0
- package/dist/logger.js.map +1 -0
- package/dist/rate-limiter.d.ts +2 -1
- package/dist/rate-limiter.d.ts.map +1 -0
- package/dist/rate-limiter.js +166 -0
- package/dist/rate-limiter.js.map +1 -0
- package/dist/server.d.ts +6 -5
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +472 -0
- package/dist/server.js.map +1 -0
- package/dist/stream/frames.d.ts +2 -1
- package/dist/stream/frames.d.ts.map +1 -0
- package/dist/stream/frames.js +141 -0
- package/dist/stream/frames.js.map +1 -0
- package/dist/stream/index.d.ts +1 -0
- package/dist/stream/index.d.ts.map +1 -0
- package/dist/stream/index.js +22 -0
- package/dist/stream/index.js.map +1 -0
- package/dist/stream/logger.d.ts +1 -0
- package/dist/stream/logger.d.ts.map +1 -0
- package/dist/stream/logger.js +7 -0
- package/dist/stream/logger.js.map +1 -0
- package/dist/stream/server.d.ts +3 -1
- package/dist/stream/server.d.ts.map +1 -0
- package/dist/stream/server.js +70 -0
- package/dist/stream/server.js.map +1 -0
- package/dist/stream/stream.d.ts +1 -0
- package/dist/stream/stream.d.ts.map +1 -0
- package/dist/stream/stream.js +44 -0
- package/dist/stream/stream.js.map +1 -0
- package/dist/stream/subscription.d.ts +2 -0
- package/dist/stream/subscription.d.ts.map +1 -0
- package/dist/stream/subscription.js +80 -0
- package/dist/stream/subscription.js.map +1 -0
- package/dist/stream/types.d.ts +5 -4
- package/dist/stream/types.d.ts.map +1 -0
- package/dist/stream/types.js +47 -0
- package/dist/stream/types.js.map +1 -0
- package/dist/stream/websocket-keepalive.d.ts +2 -0
- package/dist/stream/websocket-keepalive.d.ts.map +1 -0
- package/dist/stream/websocket-keepalive.js +160 -0
- package/dist/stream/websocket-keepalive.js.map +1 -0
- package/dist/types.d.ts +54 -34
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +163 -0
- package/dist/types.js.map +1 -0
- package/dist/util.d.ts +3 -2
- package/dist/util.d.ts.map +1 -0
- package/dist/util.js +263 -0
- package/dist/util.js.map +1 -0
- package/jest.config.js +4 -3
- package/package.json +10 -11
- package/src/rate-limiter.ts +3 -0
- package/src/server.ts +53 -14
- package/src/stream/frames.ts +1 -1
- package/src/stream/websocket-keepalive.ts +2 -1
- package/src/types.ts +22 -10
- package/src/util.ts +3 -3
- package/tests/bodies.test.ts +4 -4
- package/tests/errors.test.ts +1 -1
- package/tsconfig.build.json +6 -2
- package/tsconfig.json +3 -11
- package/tsconfig.tests.json +7 -0
- package/babel.config.js +0 -1
- package/build.js +0 -14
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @atproto/xrpc-server
|
|
2
2
|
|
|
3
|
+
## 0.4.4-next.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Overhaul of package builds, no longer bundling deps.
|
|
8
|
+
|
|
9
|
+
- Updated dependencies []:
|
|
10
|
+
- @atproto/lexicon@0.3.3-next.0
|
|
11
|
+
- @atproto/common@0.3.4-next.0
|
|
12
|
+
- @atproto/crypto@0.3.1-next.0
|
|
13
|
+
|
|
14
|
+
## 0.4.3
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- Updated dependencies []:
|
|
19
|
+
- @atproto/lexicon@0.3.2
|
|
20
|
+
|
|
3
21
|
## 0.4.2
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
package/LICENSE.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Dual MIT/Apache-2.0 License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2022-
|
|
3
|
+
Copyright (c) 2022-2024 Bluesky PBC, and Contributors
|
|
4
4
|
|
|
5
5
|
Except as otherwise noted in individual files, this software is licensed under the MIT license (<http://opensource.org/licenses/MIT>), or the Apache License, Version 2.0 (<http://www.apache.org/licenses/LICENSE-2.0>).
|
|
6
6
|
|
package/dist/auth.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as crypto from '@atproto/crypto';
|
|
2
|
-
|
|
2
|
+
type ServiceJwtPayload = {
|
|
3
3
|
iss: string;
|
|
4
4
|
aud: string;
|
|
5
5
|
exp?: number;
|
|
6
6
|
};
|
|
7
|
-
|
|
7
|
+
type ServiceJwtParams = ServiceJwtPayload & {
|
|
8
8
|
keypair: crypto.Keypair;
|
|
9
9
|
};
|
|
10
10
|
export declare const createServiceJwt: (params: ServiceJwtParams) => Promise<string>;
|
|
@@ -15,3 +15,4 @@ export declare const createServiceAuthHeaders: (params: ServiceJwtParams) => Pro
|
|
|
15
15
|
}>;
|
|
16
16
|
export declare const verifyJwt: (jwtStr: string, ownDid: string | null, getSigningKey: (did: string, forceRefresh: boolean) => Promise<string>) => Promise<ServiceJwtPayload>;
|
|
17
17
|
export {};
|
|
18
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,MAAM,iBAAiB,CAAA;AAIzC,KAAK,iBAAiB,GAAG;IACvB,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,CAAC,EAAE,MAAM,CAAA;CACb,CAAA;AAED,KAAK,gBAAgB,GAAG,iBAAiB,GAAG;IAC1C,OAAO,EAAE,MAAM,CAAC,OAAO,CAAA;CACxB,CAAA;AAED,eAAO,MAAM,gBAAgB,WACnB,gBAAgB,KACvB,QAAQ,MAAM,CAgBhB,CAAA;AAED,eAAO,MAAM,wBAAwB,WAAkB,gBAAgB;;;;EAKtE,CAAA;AAMD,eAAO,MAAM,SAAS,WACZ,MAAM,UACN,MAAM,GAAG,IAAI,uBACA,MAAM,gBAAgB,OAAO,KAAK,QAAQ,MAAM,CAAC,KACrE,QAAQ,iBAAiB,CA8D3B,CAAA"}
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.verifyJwt = exports.createServiceAuthHeaders = exports.createServiceJwt = void 0;
|
|
27
|
+
const common = __importStar(require("@atproto/common"));
|
|
28
|
+
const common_1 = require("@atproto/common");
|
|
29
|
+
const crypto = __importStar(require("@atproto/crypto"));
|
|
30
|
+
const ui8 = __importStar(require("uint8arrays"));
|
|
31
|
+
const types_1 = require("./types");
|
|
32
|
+
const createServiceJwt = async (params) => {
|
|
33
|
+
const { iss, aud, keypair } = params;
|
|
34
|
+
const exp = params.exp ?? Math.floor((Date.now() + common_1.MINUTE) / 1000);
|
|
35
|
+
const header = {
|
|
36
|
+
typ: 'JWT',
|
|
37
|
+
alg: keypair.jwtAlg,
|
|
38
|
+
};
|
|
39
|
+
const payload = {
|
|
40
|
+
iss,
|
|
41
|
+
aud,
|
|
42
|
+
exp,
|
|
43
|
+
};
|
|
44
|
+
const toSignStr = `${jsonToB64Url(header)}.${jsonToB64Url(payload)}`;
|
|
45
|
+
const toSign = ui8.fromString(toSignStr, 'utf8');
|
|
46
|
+
const sig = await keypair.sign(toSign);
|
|
47
|
+
return `${toSignStr}.${ui8.toString(sig, 'base64url')}`;
|
|
48
|
+
};
|
|
49
|
+
exports.createServiceJwt = createServiceJwt;
|
|
50
|
+
const createServiceAuthHeaders = async (params) => {
|
|
51
|
+
const jwt = await (0, exports.createServiceJwt)(params);
|
|
52
|
+
return {
|
|
53
|
+
headers: { authorization: `Bearer ${jwt}` },
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
exports.createServiceAuthHeaders = createServiceAuthHeaders;
|
|
57
|
+
const jsonToB64Url = (json) => {
|
|
58
|
+
return common.utf8ToB64Url(JSON.stringify(json));
|
|
59
|
+
};
|
|
60
|
+
const verifyJwt = async (jwtStr, ownDid, // null indicates to skip the audience check
|
|
61
|
+
getSigningKey) => {
|
|
62
|
+
const parts = jwtStr.split('.');
|
|
63
|
+
if (parts.length !== 3) {
|
|
64
|
+
throw new types_1.AuthRequiredError('poorly formatted jwt', 'BadJwt');
|
|
65
|
+
}
|
|
66
|
+
const payload = parsePayload(parts[1]);
|
|
67
|
+
const sig = parts[2];
|
|
68
|
+
if (Date.now() / 1000 > payload.exp) {
|
|
69
|
+
throw new types_1.AuthRequiredError('jwt expired', 'JwtExpired');
|
|
70
|
+
}
|
|
71
|
+
if (ownDid !== null && payload.aud !== ownDid) {
|
|
72
|
+
throw new types_1.AuthRequiredError('jwt audience does not match service did', 'BadJwtAudience');
|
|
73
|
+
}
|
|
74
|
+
const msgBytes = ui8.fromString(parts.slice(0, 2).join('.'), 'utf8');
|
|
75
|
+
const sigBytes = ui8.fromString(sig, 'base64url');
|
|
76
|
+
const verifySignatureWithKey = (key) => {
|
|
77
|
+
return crypto.verifySignature(key, msgBytes, sigBytes, {
|
|
78
|
+
allowMalleableSig: true,
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
const signingKey = await getSigningKey(payload.iss, false);
|
|
82
|
+
let validSig;
|
|
83
|
+
try {
|
|
84
|
+
validSig = await verifySignatureWithKey(signingKey);
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
throw new types_1.AuthRequiredError('could not verify jwt signature', 'BadJwtSignature');
|
|
88
|
+
}
|
|
89
|
+
if (!validSig) {
|
|
90
|
+
// get fresh signing key in case it failed due to a recent rotation
|
|
91
|
+
const freshSigningKey = await getSigningKey(payload.iss, true);
|
|
92
|
+
try {
|
|
93
|
+
validSig =
|
|
94
|
+
freshSigningKey !== signingKey
|
|
95
|
+
? await verifySignatureWithKey(freshSigningKey)
|
|
96
|
+
: false;
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
throw new types_1.AuthRequiredError('could not verify jwt signature', 'BadJwtSignature');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (!validSig) {
|
|
103
|
+
throw new types_1.AuthRequiredError('jwt signature does not match jwt issuer', 'BadJwtSignature');
|
|
104
|
+
}
|
|
105
|
+
return payload;
|
|
106
|
+
};
|
|
107
|
+
exports.verifyJwt = verifyJwt;
|
|
108
|
+
const parseB64UrlToJson = (b64) => {
|
|
109
|
+
return JSON.parse(common.b64UrlToUtf8(b64));
|
|
110
|
+
};
|
|
111
|
+
const parsePayload = (b64) => {
|
|
112
|
+
const payload = parseB64UrlToJson(b64);
|
|
113
|
+
if (!payload || typeof payload !== 'object') {
|
|
114
|
+
throw new types_1.AuthRequiredError('poorly formatted jwt', 'BadJwt');
|
|
115
|
+
}
|
|
116
|
+
else if (typeof payload.exp !== 'number') {
|
|
117
|
+
throw new types_1.AuthRequiredError('poorly formatted jwt', 'BadJwt');
|
|
118
|
+
}
|
|
119
|
+
else if (typeof payload.iss !== 'string') {
|
|
120
|
+
throw new types_1.AuthRequiredError('poorly formatted jwt', 'BadJwt');
|
|
121
|
+
}
|
|
122
|
+
return payload;
|
|
123
|
+
};
|
|
124
|
+
//# sourceMappingURL=auth.js.map
|
package/dist/auth.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAA;AACvB,cAAc,QAAQ,CAAA;AACtB,cAAc,UAAU,CAAA;AACxB,cAAc,UAAU,CAAA;AACxB,cAAc,gBAAgB,CAAA;AAE9B,YAAY,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAA;AAC1C,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAA"}
|