@atproto/xrpc-server 0.3.2 → 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 CHANGED
@@ -1,5 +1,28 @@
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
+
18
+ ## 0.3.3
19
+
20
+ ### Patch Changes
21
+
22
+ - Updated dependencies []:
23
+ - @atproto/common@0.3.2
24
+ - @atproto/lexicon@0.2.3
25
+
3
26
  ## 0.3.2
4
27
 
5
28
  ### 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
- // create xrpc server
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 ServiceJwtParams = {
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<string>;
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
@@ -44909,6 +44909,25 @@ var validateLanguage = (langTag) => {
44909
44909
  };
44910
44910
  var bcp47Regexp = /^((?<grandfathered>(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)|(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang))|((?<language>([A-Za-z]{2,3}(-(?<extlang>[A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-(?<script>[A-Za-z]{4}))?(-(?<region>[A-Za-z]{2}|[0-9]{3}))?(-(?<variant>[A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-(?<extension>[0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(?<privateUseA>x(-[A-Za-z0-9]{1,8})+))?)|(?<privateUseB>x(-[A-Za-z0-9]{1,8})+))$/;
44911
44911
 
44912
+ // ../common-web/src/did-doc.ts
44913
+ var verificationMethod = z.object({
44914
+ id: z.string(),
44915
+ type: z.string(),
44916
+ controller: z.string(),
44917
+ publicKeyMultibase: z.string().optional()
44918
+ });
44919
+ var service = z.object({
44920
+ id: z.string(),
44921
+ type: z.string(),
44922
+ serviceEndpoint: z.union([z.string(), z.record(z.unknown())])
44923
+ });
44924
+ var didDocument = z.object({
44925
+ id: z.string(),
44926
+ alsoKnownAs: z.array(z.string()).optional(),
44927
+ verificationMethod: z.array(verificationMethod).optional(),
44928
+ service: z.array(service).optional()
44929
+ });
44930
+
44912
44931
  // ../lexicon/src/validators/formats.ts
44913
44932
  var import_iso_datestring_validator = __toESM(require_dist());
44914
44933
  function datetime(path, value) {
@@ -45754,15 +45773,6 @@ var discriminatedObject = z.object({ $type: z.string() });
45754
45773
  function isDiscriminatedObject(value) {
45755
45774
  return discriminatedObject.safeParse(value).success;
45756
45775
  }
45757
- var LexiconDocMalformedError = class extends Error {
45758
- constructor(message, schemaDef, issues) {
45759
- super(message);
45760
- this.schemaDef = schemaDef;
45761
- this.issues = issues;
45762
- this.schemaDef = schemaDef;
45763
- this.issues = issues;
45764
- }
45765
- };
45766
45776
  var ValidationError = class extends Error {
45767
45777
  };
45768
45778
  var InvalidLexiconError = class extends Error {
@@ -45843,23 +45853,13 @@ var Lexicons = class {
45843
45853
  }
45844
45854
  }
45845
45855
  add(doc) {
45846
- try {
45847
- lexiconDoc.parse(doc);
45848
- } catch (e) {
45849
- if (e instanceof ZodError) {
45850
- throw new LexiconDocMalformedError(`Failed to parse schema definition ${doc.id}`, doc, e.issues);
45851
- } else {
45852
- throw e;
45853
- }
45854
- }
45855
- const validatedDoc = doc;
45856
- const uri2 = toLexUri(validatedDoc.id);
45856
+ const uri2 = toLexUri(doc.id);
45857
45857
  if (this.docs.has(uri2)) {
45858
45858
  throw new Error(`${uri2} has already been registered`);
45859
45859
  }
45860
- resolveRefUris(validatedDoc, uri2);
45861
- this.docs.set(uri2, validatedDoc);
45862
- for (const [defUri, def2] of iterDefs(validatedDoc)) {
45860
+ resolveRefUris(doc, uri2);
45861
+ this.docs.set(uri2, doc);
45862
+ for (const [defUri, def2] of iterDefs(doc)) {
45863
45863
  this.defs.set(defUri, def2);
45864
45864
  }
45865
45865
  }
@@ -51917,16 +51917,18 @@ var decompressPubkey2 = (compressed) => {
51917
51917
  };
51918
51918
 
51919
51919
  // ../crypto/src/p256/operations.ts
51920
- var verifyDidSig = async (did2, data, sig) => {
51920
+ var verifyDidSig = async (did2, data, sig, opts) => {
51921
51921
  const { jwtAlg, keyBytes } = parseDidKey(did2);
51922
51922
  if (jwtAlg !== P256_JWT_ALG) {
51923
51923
  throw new Error(`Not a P-256 did:key: ${did2}`);
51924
51924
  }
51925
- return verifySig(keyBytes, data, sig);
51925
+ return verifySig(keyBytes, data, sig, opts);
51926
51926
  };
51927
- var verifySig = async (publicKey, data, sig) => {
51927
+ var verifySig = async (publicKey, data, sig, opts) => {
51928
51928
  const msgHash = await sha2562(data);
51929
- return p256.verify(sig, msgHash, publicKey, { lowS: true });
51929
+ return p256.verify(sig, msgHash, publicKey, {
51930
+ lowS: opts?.lowS ?? true
51931
+ });
51930
51932
  };
51931
51933
 
51932
51934
  // ../crypto/src/p256/plugin.ts
@@ -51938,16 +51940,18 @@ var p256Plugin = {
51938
51940
  var plugin_default = p256Plugin;
51939
51941
 
51940
51942
  // ../crypto/src/secp256k1/operations.ts
51941
- var verifyDidSig2 = async (did2, data, sig) => {
51943
+ var verifyDidSig2 = async (did2, data, sig, opts) => {
51942
51944
  const { jwtAlg, keyBytes } = parseDidKey(did2);
51943
51945
  if (jwtAlg !== SECP256K1_JWT_ALG) {
51944
51946
  throw new Error(`Not a secp256k1 did:key: ${did2}`);
51945
51947
  }
51946
- return verifySig2(keyBytes, data, sig);
51948
+ return verifySig2(keyBytes, data, sig, opts);
51947
51949
  };
51948
- var verifySig2 = async (publicKey, data, sig) => {
51950
+ var verifySig2 = async (publicKey, data, sig, opts) => {
51949
51951
  const msgHash = await sha2562(data);
51950
- return secp256k1.verify(sig, msgHash, publicKey, { lowS: true });
51952
+ return secp256k1.verify(sig, msgHash, publicKey, {
51953
+ lowS: opts?.lowS ?? true
51954
+ });
51951
51955
  };
51952
51956
 
51953
51957
  // ../crypto/src/secp256k1/plugin.ts
@@ -51994,13 +51998,13 @@ var hasPrefix = (bytes3, prefix) => {
51994
51998
  };
51995
51999
 
51996
52000
  // ../crypto/src/verify.ts
51997
- var verifySignature = (didKey, data, sig) => {
52001
+ var verifySignature = (didKey, data, sig, opts) => {
51998
52002
  const parsed = parseDidKey(didKey);
51999
52003
  const plugin = plugins_default.find((p) => p.jwtAlg === parsed.jwtAlg);
52000
52004
  if (!plugin) {
52001
- throw new Error(`Unsupported signature alg: :${parsed.jwtAlg}`);
52005
+ throw new Error(`Unsupported signature alg: ${parsed.jwtAlg}`);
52002
52006
  }
52003
- return plugin.verifySignature(didKey, data, sig);
52007
+ return plugin.verifySignature(didKey, data, sig, opts);
52004
52008
  };
52005
52009
 
52006
52010
  // src/auth.ts
@@ -52045,17 +52049,30 @@ var verifyJwt = async (jwtStr, ownDid, getSigningKey) => {
52045
52049
  }
52046
52050
  const msgBytes = fromString2(parts.slice(0, 2).join("."), "utf8");
52047
52051
  const sigBytes = fromString2(sig, "base64url");
52048
- const signingKey = await getSigningKey(payload.iss);
52052
+ const verifySignatureWithKey = (key) => {
52053
+ return verifySignature(key, msgBytes, sigBytes, {
52054
+ lowS: false
52055
+ });
52056
+ };
52057
+ const signingKey = await getSigningKey(payload.iss, false);
52049
52058
  let validSig;
52050
52059
  try {
52051
- validSig = await verifySignature(signingKey, msgBytes, sigBytes);
52060
+ validSig = await verifySignatureWithKey(signingKey);
52052
52061
  } catch (err) {
52053
52062
  throw new AuthRequiredError("could not verify jwt signature", "BadJwtSignature");
52054
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
+ }
52055
52072
  if (!validSig) {
52056
52073
  throw new AuthRequiredError("jwt signature does not match jwt issuer", "BadJwtSignature");
52057
52074
  }
52058
- return payload.iss;
52075
+ return payload;
52059
52076
  };
52060
52077
  var parseB64UrlToJson = (b64) => {
52061
52078
  return JSON.parse(b64UrlToUtf8(b64));