@atproto/xrpc-server 0.3.3 → 0.4.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 +15 -0
- package/LICENSE.txt +7 -0
- package/README.md +12 -4
- package/dist/auth.d.ts +4 -2
- package/dist/index.js +35 -37
- package/dist/index.js.map +2 -2
- package/dist/server.d.ts +5 -5
- package/package.json +8 -6
- package/src/auth.ts +31 -6
- package/src/server.ts +5 -4
- package/tests/auth.test.ts +109 -2
- package/tests/bodies.test.ts +2 -1
- package/tests/errors.test.ts +2 -1
- package/tests/ipld.test.ts +2 -1
- package/tests/parameters.test.ts +2 -1
- package/tests/procedures.test.ts +2 -1
- package/tests/queries.test.ts +2 -1
- package/tests/rate-limiter.test.ts +2 -1
- package/tests/responses.test.ts +2 -1
- package/tests/subscriptions.test.ts +2 -1
- package/LICENSE +0 -21
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# @atproto/xrpc-server
|
|
2
2
|
|
|
3
|
+
## 0.4.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#1801](https://github.com/bluesky-social/atproto/pull/1801) [`ce49743d`](https://github.com/bluesky-social/atproto/commit/ce49743d7f8800d33116b88001d7b512553c2c89) Thanks [@gaearon](https://github.com/gaearon)! - Methods that accepts lexicons now take `LexiconDoc` type instead of `unknown`
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- [#1788](https://github.com/bluesky-social/atproto/pull/1788) [`84e2d4d2`](https://github.com/bluesky-social/atproto/commit/84e2d4d2b6694f344d80c18672c78b650189d423) Thanks [@bnewbold](https://github.com/bnewbold)! - update license to "MIT or Apache2"
|
|
12
|
+
|
|
13
|
+
- Updated dependencies [[`ce49743d`](https://github.com/bluesky-social/atproto/commit/ce49743d7f8800d33116b88001d7b512553c2c89), [`84e2d4d2`](https://github.com/bluesky-social/atproto/commit/84e2d4d2b6694f344d80c18672c78b650189d423)]:
|
|
14
|
+
- @atproto/lexicon@0.3.0
|
|
15
|
+
- @atproto/common@0.3.3
|
|
16
|
+
- @atproto/crypto@0.2.3
|
|
17
|
+
|
|
3
18
|
## 0.3.3
|
|
4
19
|
|
|
5
20
|
### Patch Changes
|
package/LICENSE.txt
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Dual MIT/Apache-2.0 License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022-2023 Bluesky PBC, and Contributors
|
|
4
|
+
|
|
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
|
+
|
|
7
|
+
Downstream projects and end users may chose either license individually, or both together, at their discretion. The motivation for this dual-licensing is the additional software patent assurance provided by Apache 2.0.
|
package/README.md
CHANGED
|
@@ -8,11 +8,11 @@ TypeScript library for implementing [atproto](https://atproto.com) HTTP API serv
|
|
|
8
8
|
## Usage
|
|
9
9
|
|
|
10
10
|
```typescript
|
|
11
|
+
import { LexiconDoc } from '@atproto/lexicon'
|
|
11
12
|
import * as xrpc from '@atproto/xrpc-server'
|
|
12
13
|
import express from 'express'
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
const server = xrpc.createServer([
|
|
15
|
+
const lexicons: LexiconDoc[] = [
|
|
16
16
|
{
|
|
17
17
|
lexicon: 1,
|
|
18
18
|
id: 'io.example.ping',
|
|
@@ -29,7 +29,10 @@ const server = xrpc.createServer([
|
|
|
29
29
|
},
|
|
30
30
|
},
|
|
31
31
|
},
|
|
32
|
-
]
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
// create xrpc server
|
|
35
|
+
const server = xrpc.createServer(lexicons)
|
|
33
36
|
|
|
34
37
|
function ping(ctx: {
|
|
35
38
|
auth: xrpc.HandlerAuth | undefined
|
|
@@ -51,4 +54,9 @@ app.listen(8080)
|
|
|
51
54
|
|
|
52
55
|
## License
|
|
53
56
|
|
|
54
|
-
MIT
|
|
57
|
+
This project is dual-licensed under MIT and Apache 2.0 terms:
|
|
58
|
+
|
|
59
|
+
- MIT license ([LICENSE-MIT.txt](https://github.com/bluesky-social/atproto/blob/main/LICENSE-MIT.txt) or http://opensource.org/licenses/MIT)
|
|
60
|
+
- Apache License, Version 2.0, ([LICENSE-APACHE.txt](https://github.com/bluesky-social/atproto/blob/main/LICENSE-APACHE.txt) or http://www.apache.org/licenses/LICENSE-2.0)
|
|
61
|
+
|
|
62
|
+
Downstream projects and end users may chose either license individually, or both together, at their discretion. The motivation for this dual-licensing is the additional software patent assurance provided by Apache 2.0.
|
package/dist/auth.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import * as crypto from '@atproto/crypto';
|
|
2
|
-
declare type
|
|
2
|
+
declare type ServiceJwtPayload = {
|
|
3
3
|
iss: string;
|
|
4
4
|
aud: string;
|
|
5
5
|
exp?: number;
|
|
6
|
+
};
|
|
7
|
+
declare type ServiceJwtParams = ServiceJwtPayload & {
|
|
6
8
|
keypair: crypto.Keypair;
|
|
7
9
|
};
|
|
8
10
|
export declare const createServiceJwt: (params: ServiceJwtParams) => Promise<string>;
|
|
@@ -11,5 +13,5 @@ export declare const createServiceAuthHeaders: (params: ServiceJwtParams) => Pro
|
|
|
11
13
|
authorization: string;
|
|
12
14
|
};
|
|
13
15
|
}>;
|
|
14
|
-
export declare const verifyJwt: (jwtStr: string, ownDid: string | null, getSigningKey: (did: string) => Promise<string>) => Promise<
|
|
16
|
+
export declare const verifyJwt: (jwtStr: string, ownDid: string | null, getSigningKey: (did: string, forceRefresh: boolean) => Promise<string>) => Promise<ServiceJwtPayload>;
|
|
15
17
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -45773,15 +45773,6 @@ var discriminatedObject = z.object({ $type: z.string() });
|
|
|
45773
45773
|
function isDiscriminatedObject(value) {
|
|
45774
45774
|
return discriminatedObject.safeParse(value).success;
|
|
45775
45775
|
}
|
|
45776
|
-
var LexiconDocMalformedError = class extends Error {
|
|
45777
|
-
constructor(message, schemaDef, issues) {
|
|
45778
|
-
super(message);
|
|
45779
|
-
this.schemaDef = schemaDef;
|
|
45780
|
-
this.issues = issues;
|
|
45781
|
-
this.schemaDef = schemaDef;
|
|
45782
|
-
this.issues = issues;
|
|
45783
|
-
}
|
|
45784
|
-
};
|
|
45785
45776
|
var ValidationError = class extends Error {
|
|
45786
45777
|
};
|
|
45787
45778
|
var InvalidLexiconError = class extends Error {
|
|
@@ -45862,23 +45853,13 @@ var Lexicons = class {
|
|
|
45862
45853
|
}
|
|
45863
45854
|
}
|
|
45864
45855
|
add(doc) {
|
|
45865
|
-
|
|
45866
|
-
lexiconDoc.parse(doc);
|
|
45867
|
-
} catch (e) {
|
|
45868
|
-
if (e instanceof ZodError) {
|
|
45869
|
-
throw new LexiconDocMalformedError(`Failed to parse schema definition ${doc.id}`, doc, e.issues);
|
|
45870
|
-
} else {
|
|
45871
|
-
throw e;
|
|
45872
|
-
}
|
|
45873
|
-
}
|
|
45874
|
-
const validatedDoc = doc;
|
|
45875
|
-
const uri2 = toLexUri(validatedDoc.id);
|
|
45856
|
+
const uri2 = toLexUri(doc.id);
|
|
45876
45857
|
if (this.docs.has(uri2)) {
|
|
45877
45858
|
throw new Error(`${uri2} has already been registered`);
|
|
45878
45859
|
}
|
|
45879
|
-
resolveRefUris(
|
|
45880
|
-
this.docs.set(uri2,
|
|
45881
|
-
for (const [defUri, def2] of iterDefs(
|
|
45860
|
+
resolveRefUris(doc, uri2);
|
|
45861
|
+
this.docs.set(uri2, doc);
|
|
45862
|
+
for (const [defUri, def2] of iterDefs(doc)) {
|
|
45882
45863
|
this.defs.set(defUri, def2);
|
|
45883
45864
|
}
|
|
45884
45865
|
}
|
|
@@ -51936,16 +51917,18 @@ var decompressPubkey2 = (compressed) => {
|
|
|
51936
51917
|
};
|
|
51937
51918
|
|
|
51938
51919
|
// ../crypto/src/p256/operations.ts
|
|
51939
|
-
var verifyDidSig = async (did2, data, sig) => {
|
|
51920
|
+
var verifyDidSig = async (did2, data, sig, opts) => {
|
|
51940
51921
|
const { jwtAlg, keyBytes } = parseDidKey(did2);
|
|
51941
51922
|
if (jwtAlg !== P256_JWT_ALG) {
|
|
51942
51923
|
throw new Error(`Not a P-256 did:key: ${did2}`);
|
|
51943
51924
|
}
|
|
51944
|
-
return verifySig(keyBytes, data, sig);
|
|
51925
|
+
return verifySig(keyBytes, data, sig, opts);
|
|
51945
51926
|
};
|
|
51946
|
-
var verifySig = async (publicKey, data, sig) => {
|
|
51927
|
+
var verifySig = async (publicKey, data, sig, opts) => {
|
|
51947
51928
|
const msgHash = await sha2562(data);
|
|
51948
|
-
return p256.verify(sig, msgHash, publicKey, {
|
|
51929
|
+
return p256.verify(sig, msgHash, publicKey, {
|
|
51930
|
+
lowS: opts?.lowS ?? true
|
|
51931
|
+
});
|
|
51949
51932
|
};
|
|
51950
51933
|
|
|
51951
51934
|
// ../crypto/src/p256/plugin.ts
|
|
@@ -51957,16 +51940,18 @@ var p256Plugin = {
|
|
|
51957
51940
|
var plugin_default = p256Plugin;
|
|
51958
51941
|
|
|
51959
51942
|
// ../crypto/src/secp256k1/operations.ts
|
|
51960
|
-
var verifyDidSig2 = async (did2, data, sig) => {
|
|
51943
|
+
var verifyDidSig2 = async (did2, data, sig, opts) => {
|
|
51961
51944
|
const { jwtAlg, keyBytes } = parseDidKey(did2);
|
|
51962
51945
|
if (jwtAlg !== SECP256K1_JWT_ALG) {
|
|
51963
51946
|
throw new Error(`Not a secp256k1 did:key: ${did2}`);
|
|
51964
51947
|
}
|
|
51965
|
-
return verifySig2(keyBytes, data, sig);
|
|
51948
|
+
return verifySig2(keyBytes, data, sig, opts);
|
|
51966
51949
|
};
|
|
51967
|
-
var verifySig2 = async (publicKey, data, sig) => {
|
|
51950
|
+
var verifySig2 = async (publicKey, data, sig, opts) => {
|
|
51968
51951
|
const msgHash = await sha2562(data);
|
|
51969
|
-
return secp256k1.verify(sig, msgHash, publicKey, {
|
|
51952
|
+
return secp256k1.verify(sig, msgHash, publicKey, {
|
|
51953
|
+
lowS: opts?.lowS ?? true
|
|
51954
|
+
});
|
|
51970
51955
|
};
|
|
51971
51956
|
|
|
51972
51957
|
// ../crypto/src/secp256k1/plugin.ts
|
|
@@ -52013,13 +51998,13 @@ var hasPrefix = (bytes3, prefix) => {
|
|
|
52013
51998
|
};
|
|
52014
51999
|
|
|
52015
52000
|
// ../crypto/src/verify.ts
|
|
52016
|
-
var verifySignature = (didKey, data, sig) => {
|
|
52001
|
+
var verifySignature = (didKey, data, sig, opts) => {
|
|
52017
52002
|
const parsed = parseDidKey(didKey);
|
|
52018
52003
|
const plugin = plugins_default.find((p) => p.jwtAlg === parsed.jwtAlg);
|
|
52019
52004
|
if (!plugin) {
|
|
52020
|
-
throw new Error(`Unsupported signature alg:
|
|
52005
|
+
throw new Error(`Unsupported signature alg: ${parsed.jwtAlg}`);
|
|
52021
52006
|
}
|
|
52022
|
-
return plugin.verifySignature(didKey, data, sig);
|
|
52007
|
+
return plugin.verifySignature(didKey, data, sig, opts);
|
|
52023
52008
|
};
|
|
52024
52009
|
|
|
52025
52010
|
// src/auth.ts
|
|
@@ -52064,17 +52049,30 @@ var verifyJwt = async (jwtStr, ownDid, getSigningKey) => {
|
|
|
52064
52049
|
}
|
|
52065
52050
|
const msgBytes = fromString2(parts.slice(0, 2).join("."), "utf8");
|
|
52066
52051
|
const sigBytes = fromString2(sig, "base64url");
|
|
52067
|
-
const
|
|
52052
|
+
const verifySignatureWithKey = (key) => {
|
|
52053
|
+
return verifySignature(key, msgBytes, sigBytes, {
|
|
52054
|
+
lowS: false
|
|
52055
|
+
});
|
|
52056
|
+
};
|
|
52057
|
+
const signingKey = await getSigningKey(payload.iss, false);
|
|
52068
52058
|
let validSig;
|
|
52069
52059
|
try {
|
|
52070
|
-
validSig = await
|
|
52060
|
+
validSig = await verifySignatureWithKey(signingKey);
|
|
52071
52061
|
} catch (err) {
|
|
52072
52062
|
throw new AuthRequiredError("could not verify jwt signature", "BadJwtSignature");
|
|
52073
52063
|
}
|
|
52064
|
+
if (!validSig) {
|
|
52065
|
+
const freshSigningKey = await getSigningKey(payload.iss, true);
|
|
52066
|
+
try {
|
|
52067
|
+
validSig = freshSigningKey !== signingKey ? await verifySignatureWithKey(freshSigningKey) : false;
|
|
52068
|
+
} catch (err) {
|
|
52069
|
+
throw new AuthRequiredError("could not verify jwt signature", "BadJwtSignature");
|
|
52070
|
+
}
|
|
52071
|
+
}
|
|
52074
52072
|
if (!validSig) {
|
|
52075
52073
|
throw new AuthRequiredError("jwt signature does not match jwt issuer", "BadJwtSignature");
|
|
52076
52074
|
}
|
|
52077
|
-
return payload
|
|
52075
|
+
return payload;
|
|
52078
52076
|
};
|
|
52079
52077
|
var parseB64UrlToJson = (b64) => {
|
|
52080
52078
|
return JSON.parse(b64UrlToUtf8(b64));
|