@atproto-labs/identity-resolver 0.1.19 → 0.2.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,24 @@
1
1
  # @atproto-labs/identity-resolver
2
2
 
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#3977](https://github.com/bluesky-social/atproto/pull/3977) [`9dac8b0c6`](https://github.com/bluesky-social/atproto/commit/9dac8b0c600520ecb0066ac104787b27668dea47) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Return the whole did document when resolving an identity
8
+
9
+ - [#3977](https://github.com/bluesky-social/atproto/pull/3977) [`9dac8b0c6`](https://github.com/bluesky-social/atproto/commit/9dac8b0c600520ecb0066ac104787b27668dea47) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Reject did documents containing invalid `alsoKnownAs` ATProto handles
10
+
11
+ - [#3977](https://github.com/bluesky-social/atproto/pull/3977) [`9dac8b0c6`](https://github.com/bluesky-social/atproto/commit/9dac8b0c600520ecb0066ac104787b27668dea47) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Use `IdentityResolverError` instances instead of `TypeError`
12
+
13
+ ### Patch Changes
14
+
15
+ - [#3977](https://github.com/bluesky-social/atproto/pull/3977) [`9dac8b0c6`](https://github.com/bluesky-social/atproto/commit/9dac8b0c600520ecb0066ac104787b27668dea47) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Perform a bi-directional check when resolving identity from did
16
+
17
+ - [#3977](https://github.com/bluesky-social/atproto/pull/3977) [`9dac8b0c6`](https://github.com/bluesky-social/atproto/commit/9dac8b0c600520ecb0066ac104787b27668dea47) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Allow non-normalized handles in did documents
18
+
19
+ - Updated dependencies [[`9dac8b0c6`](https://github.com/bluesky-social/atproto/commit/9dac8b0c600520ecb0066ac104787b27668dea47), [`9dac8b0c6`](https://github.com/bluesky-social/atproto/commit/9dac8b0c600520ecb0066ac104787b27668dea47)]:
20
+ - @atproto-labs/handle-resolver@0.3.0
21
+
3
22
  ## 0.1.19
4
23
 
5
24
  ### Patch Changes
@@ -0,0 +1,4 @@
1
+ export declare class IdentityResolverError extends Error {
2
+ name: string;
3
+ }
4
+ //# sourceMappingURL=identity-resolver-error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity-resolver-error.d.ts","sourceRoot":"","sources":["../src/identity-resolver-error.ts"],"names":[],"mappings":"AAAA,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,IAAI,SAA0B;CAC/B"}
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.IdentityResolverError = void 0;
4
+ class IdentityResolverError extends Error {
5
+ constructor() {
6
+ super(...arguments);
7
+ Object.defineProperty(this, "name", {
8
+ enumerable: true,
9
+ configurable: true,
10
+ writable: true,
11
+ value: 'IdentityResolverError'
12
+ });
13
+ }
14
+ }
15
+ exports.IdentityResolverError = IdentityResolverError;
16
+ //# sourceMappingURL=identity-resolver-error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity-resolver-error.js","sourceRoot":"","sources":["../src/identity-resolver-error.ts"],"names":[],"mappings":";;;AAAA,MAAa,qBAAsB,SAAQ,KAAK;IAAhD;;QACE;;;;mBAAO,uBAAuB;WAAA;IAChC,CAAC;CAAA;AAFD,sDAEC"}
@@ -1,8 +1,11 @@
1
- import { DidDocument, DidResolver, ResolveDidOptions } from '@atproto-labs/did-resolver';
2
- import { AtprotoIdentityDidMethods, HandleResolver, ResolveHandleOptions, ResolvedHandle } from '@atproto-labs/handle-resolver';
1
+ import { AtprotoDid, AtprotoIdentityDidMethods, DidDocument, DidResolver, ResolveDidOptions } from '@atproto-labs/did-resolver';
2
+ import { HandleResolver, ResolveHandleOptions } from '@atproto-labs/handle-resolver';
3
3
  export type ResolvedIdentity = {
4
- did: NonNullable<ResolvedHandle>;
5
- pds: URL;
4
+ document: DidDocument<AtprotoIdentityDidMethods>;
5
+ /**
6
+ * Will be defined if the document contains a handle alias that resolves
7
+ * to the document's DID.
8
+ */
6
9
  handle?: string;
7
10
  };
8
11
  export type ResolveIdentityOptions = ResolveDidOptions & ResolveHandleOptions;
@@ -11,7 +14,9 @@ export declare class IdentityResolver {
11
14
  readonly handleResolver: HandleResolver;
12
15
  constructor(didResolver: DidResolver<AtprotoIdentityDidMethods>, handleResolver: HandleResolver);
13
16
  resolve(input: string, options?: ResolveIdentityOptions): Promise<ResolvedIdentity>;
14
- getDocumentFromDid(did: NonNullable<ResolvedHandle>, options?: ResolveDidOptions): Promise<DidDocument<AtprotoIdentityDidMethods>>;
17
+ resolveFromDid(did: AtprotoDid, options?: ResolveDidOptions): Promise<ResolvedIdentity>;
18
+ resolveFromHandle(handle: string, options?: ResolveHandleOptions): Promise<ResolvedIdentity>;
19
+ getDocumentFromDid(did: AtprotoDid, options?: ResolveDidOptions): Promise<DidDocument<AtprotoIdentityDidMethods>>;
15
20
  getDocumentFromHandle(input: string, options?: ResolveHandleOptions): Promise<DidDocument<AtprotoIdentityDidMethods>>;
16
21
  }
17
22
  //# sourceMappingURL=identity-resolver.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"identity-resolver.d.ts","sourceRoot":"","sources":["../src/identity-resolver.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,WAAW,EACX,WAAW,EAEX,iBAAiB,EAClB,MAAM,4BAA4B,CAAA;AACnC,OAAO,EACL,yBAAyB,EACzB,cAAc,EACd,oBAAoB,EACpB,cAAc,EAEf,MAAM,+BAA+B,CAAA;AAEtC,MAAM,MAAM,gBAAgB,GAAG;IAC7B,GAAG,EAAE,WAAW,CAAC,cAAc,CAAC,CAAA;IAChC,GAAG,EAAE,GAAG,CAAA;IACR,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,sBAAsB,GAAG,iBAAiB,GAAG,oBAAoB,CAAA;AAE7E,qBAAa,gBAAgB;IAEzB,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,yBAAyB,CAAC;IAC5D,QAAQ,CAAC,cAAc,EAAE,cAAc;gBAD9B,WAAW,EAAE,WAAW,CAAC,yBAAyB,CAAC,EACnD,cAAc,EAAE,cAAc;IAG5B,OAAO,CAClB,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,gBAAgB,CAAC;IAuBf,kBAAkB,CAC7B,GAAG,EAAE,WAAW,CAAC,cAAc,CAAC,EAChC,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,WAAW,CAAC,yBAAyB,CAAC,CAAC;IAIrC,qBAAqB,CAChC,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,WAAW,CAAC,yBAAyB,CAAC,CAAC;CAyBnD"}
1
+ {"version":3,"file":"identity-resolver.d.ts","sourceRoot":"","sources":["../src/identity-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,yBAAyB,EACzB,WAAW,EACX,WAAW,EACX,iBAAiB,EAElB,MAAM,4BAA4B,CAAA;AACnC,OAAO,EACL,cAAc,EACd,oBAAoB,EACrB,MAAM,+BAA+B,CAAA;AAItC,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,WAAW,CAAC,yBAAyB,CAAC,CAAA;IAEhD;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,sBAAsB,GAAG,iBAAiB,GAAG,oBAAoB,CAAA;AAE7E,qBAAa,gBAAgB;IAEzB,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,yBAAyB,CAAC;IAC5D,QAAQ,CAAC,cAAc,EAAE,cAAc;gBAD9B,WAAW,EAAE,WAAW,CAAC,yBAAyB,CAAC,EACnD,cAAc,EAAE,cAAc;IAG5B,OAAO,CAClB,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,gBAAgB,CAAC;IAMf,cAAc,CACzB,GAAG,EAAE,UAAU,EACf,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,gBAAgB,CAAC;IAoBf,iBAAiB,CAC5B,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,gBAAgB,CAAC;IAWf,kBAAkB,CAC7B,GAAG,EAAE,UAAU,EACf,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,WAAW,CAAC,yBAAyB,CAAC,CAAC;IAIrC,qBAAqB,CAChC,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,WAAW,CAAC,yBAAyB,CAAC,CAAC;CA8BnD"}
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.IdentityResolver = void 0;
4
- const syntax_1 = require("@atproto/syntax");
5
- const handle_resolver_1 = require("@atproto-labs/handle-resolver");
4
+ const did_resolver_1 = require("@atproto-labs/did-resolver");
5
+ const identity_resolver_error_1 = require("./identity-resolver-error");
6
+ const util_1 = require("./util");
6
7
  class IdentityResolver {
7
8
  constructor(didResolver, handleResolver) {
8
9
  Object.defineProperty(this, "didResolver", {
@@ -19,57 +20,56 @@ class IdentityResolver {
19
20
  });
20
21
  }
21
22
  async resolve(input, options) {
22
- const document = (0, handle_resolver_1.isResolvedHandle)(input)
23
- ? await this.getDocumentFromDid(input, options)
24
- : await this.getDocumentFromHandle(input, options);
25
- const service = document.service?.find((isAtprotoPersonalDataServerService), document);
26
- if (!service) {
27
- throw new TypeError(`No valid "AtprotoPersonalDataServer" service found in "${document.id}" DID document`);
28
- }
23
+ return (0, did_resolver_1.isAtprotoDid)(input)
24
+ ? this.resolveFromDid(input, options)
25
+ : this.resolveFromHandle(input, options);
26
+ }
27
+ async resolveFromDid(did, options) {
28
+ const document = await this.getDocumentFromDid(did, options);
29
+ options?.signal?.throwIfAborted();
30
+ // We will only return the document's handle alias if it resolves to the
31
+ // same DID as the input.
32
+ const handle = (0, util_1.extractNormalizedHandle)(document);
33
+ const resolvedDid = handle
34
+ ? await this.handleResolver
35
+ .resolve(handle, options)
36
+ .catch(() => undefined) // Ignore errors (temporarily unavailable)
37
+ : undefined;
38
+ return {
39
+ document,
40
+ handle: resolvedDid === did ? handle : undefined,
41
+ };
42
+ }
43
+ async resolveFromHandle(handle, options) {
44
+ const document = await this.getDocumentFromHandle(handle, options);
45
+ // @NOTE bi-directional resolution enforced in getDocumentFromHandle()
29
46
  return {
30
- did: document.id,
31
- pds: new URL(service.serviceEndpoint),
32
- handle: extractHandle(document),
47
+ document,
48
+ handle: (0, util_1.extractNormalizedHandle)(document),
33
49
  };
34
50
  }
35
51
  async getDocumentFromDid(did, options) {
36
52
  return this.didResolver.resolve(did, options);
37
53
  }
38
54
  async getDocumentFromHandle(input, options) {
39
- const handle = (0, syntax_1.normalizeAndEnsureValidHandle)(input);
55
+ const handle = (0, util_1.asNormalizedHandle)(input);
56
+ if (!handle) {
57
+ throw new identity_resolver_error_1.IdentityResolverError(`Invalid handle "${input}" provided.`);
58
+ }
40
59
  const did = await this.handleResolver.resolve(handle, options);
41
60
  if (!did) {
42
- throw new TypeError(`Handle "${handle}" does not resolve to a DID`);
61
+ throw new identity_resolver_error_1.IdentityResolverError(`Handle "${handle}" does not resolve to a DID`);
43
62
  }
44
63
  options?.signal?.throwIfAborted();
45
64
  // Note: Not using "return this.resolveDid(did, options)" to make the extra
46
65
  // check for the handle in the DID document:
47
66
  const document = await this.didResolver.resolve(did, options);
48
- // Ensure that the handle is included in the document
49
- if (!document.alsoKnownAs?.includes(`at://${handle}`)) {
50
- throw new TypeError(`Did document for "${did}" does not include the handle "${handle}"`);
67
+ // Enforce bi-directional resolution
68
+ if (handle !== (0, util_1.extractNormalizedHandle)(document)) {
69
+ throw new identity_resolver_error_1.IdentityResolverError(`Did document for "${did}" does not include the handle "${handle}"`);
51
70
  }
52
71
  return document;
53
72
  }
54
73
  }
55
74
  exports.IdentityResolver = IdentityResolver;
56
- function extractHandle(document) {
57
- if (document.alsoKnownAs) {
58
- for (const h of document.alsoKnownAs) {
59
- if (h.startsWith('at://')) {
60
- const handle = h.slice(5);
61
- if ((0, syntax_1.isValidHandle)(handle))
62
- return handle;
63
- }
64
- }
65
- }
66
- return undefined;
67
- }
68
- function isAtprotoPersonalDataServerService(s) {
69
- return (typeof s.serviceEndpoint === 'string' &&
70
- s.type === 'AtprotoPersonalDataServer' &&
71
- (s.id.startsWith('#')
72
- ? s.id === '#atproto_pds'
73
- : s.id === `${this.id}#atproto_pds`));
74
- }
75
75
  //# sourceMappingURL=identity-resolver.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"identity-resolver.js","sourceRoot":"","sources":["../src/identity-resolver.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"identity-resolver.js","sourceRoot":"","sources":["../src/identity-resolver.ts"],"names":[],"mappings":";;;AAAA,6DAOmC;AAKnC,uEAAiE;AACjE,iCAAoE;AAcpE,MAAa,gBAAgB;IAC3B,YACW,WAAmD,EACnD,cAA8B;QADvC;;;;mBAAS,WAAW;WAAwC;QAC5D;;;;mBAAS,cAAc;WAAgB;IACtC,CAAC;IAEG,KAAK,CAAC,OAAO,CAClB,KAAa,EACb,OAAgC;QAEhC,OAAO,IAAA,2BAAY,EAAC,KAAK,CAAC;YACxB,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,OAAO,CAAC;YACrC,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;IAC5C,CAAC;IAEM,KAAK,CAAC,cAAc,CACzB,GAAe,EACf,OAA2B;QAE3B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;QAE5D,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,CAAA;QAEjC,wEAAwE;QACxE,yBAAyB;QACzB,MAAM,MAAM,GAAG,IAAA,8BAAuB,EAAC,QAAQ,CAAC,CAAA;QAChD,MAAM,WAAW,GAAG,MAAM;YACxB,CAAC,CAAC,MAAM,IAAI,CAAC,cAAc;iBACtB,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC;iBACxB,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,0CAA0C;YACtE,CAAC,CAAC,SAAS,CAAA;QAEb,OAAO;YACL,QAAQ;YACR,MAAM,EAAE,WAAW,KAAK,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;SACjD,CAAA;IACH,CAAC;IAEM,KAAK,CAAC,iBAAiB,CAC5B,MAAc,EACd,OAA8B;QAE9B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,qBAAqB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;QAElE,sEAAsE;QAEtE,OAAO;YACL,QAAQ;YACR,MAAM,EAAE,IAAA,8BAAuB,EAAC,QAAQ,CAAC;SAC1C,CAAA;IACH,CAAC;IAEM,KAAK,CAAC,kBAAkB,CAC7B,GAAe,EACf,OAA2B;QAE3B,OAAO,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;IAC/C,CAAC;IAEM,KAAK,CAAC,qBAAqB,CAChC,KAAa,EACb,OAA8B;QAE9B,MAAM,MAAM,GAAG,IAAA,yBAAkB,EAAC,KAAK,CAAC,CAAA;QACxC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,+CAAqB,CAAC,mBAAmB,KAAK,aAAa,CAAC,CAAA;QACxE,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;QAE9D,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,MAAM,IAAI,+CAAqB,CAC7B,WAAW,MAAM,6BAA6B,CAC/C,CAAA;QACH,CAAC;QAED,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,CAAA;QAEjC,2EAA2E;QAC3E,4CAA4C;QAE5C,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;QAE7D,oCAAoC;QACpC,IAAI,MAAM,KAAK,IAAA,8BAAuB,EAAC,QAAQ,CAAC,EAAE,CAAC;YACjD,MAAM,IAAI,+CAAqB,CAC7B,qBAAqB,GAAG,kCAAkC,MAAM,GAAG,CACpE,CAAA;QACH,CAAC;QAED,OAAO,QAAQ,CAAA;IACjB,CAAC;CACF;AA5FD,4CA4FC"}
package/dist/index.d.ts CHANGED
@@ -1,2 +1,4 @@
1
+ export * from './identity-resolver-error.js';
1
2
  export * from './identity-resolver.js';
3
+ export * from './util.js';
2
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAA;AAC5C,cAAc,wBAAwB,CAAA;AACtC,cAAc,WAAW,CAAA"}
package/dist/index.js CHANGED
@@ -14,5 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./identity-resolver-error.js"), exports);
17
18
  __exportStar(require("./identity-resolver.js"), exports);
19
+ __exportStar(require("./util.js"), exports);
18
20
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,yDAAsC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,+DAA4C;AAC5C,yDAAsC;AACtC,4CAAyB"}
package/dist/util.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ import { AtprotoIdentityDidMethods, DidDocument } from '@atproto-labs/did-resolver';
2
+ /**
3
+ * Extract the raw, un-validated, Atproto handle from a DID document.
4
+ */
5
+ export declare function extractAtprotoHandle(document: DidDocument<AtprotoIdentityDidMethods>): string | undefined;
6
+ /**
7
+ * Extracts a validated, normalized Atproto handle from a DID document.
8
+ */
9
+ export declare function extractNormalizedHandle(document: DidDocument<AtprotoIdentityDidMethods>): string | undefined;
10
+ export declare function asNormalizedHandle(input: string): string | undefined;
11
+ export declare function normalizeHandle(handle: string): string;
12
+ export declare function isValidHandle(handle: string): boolean;
13
+ //# sourceMappingURL=util.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,yBAAyB,EACzB,WAAW,EACZ,MAAM,4BAA4B,CAAA;AAEnC;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,WAAW,CAAC,yBAAyB,CAAC,GAC/C,MAAM,GAAG,SAAS,CAUpB;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,WAAW,CAAC,yBAAyB,CAAC,GAC/C,MAAM,GAAG,SAAS,CAIpB;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGpE;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAQrD"}
package/dist/util.js ADDED
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.extractAtprotoHandle = extractAtprotoHandle;
4
+ exports.extractNormalizedHandle = extractNormalizedHandle;
5
+ exports.asNormalizedHandle = asNormalizedHandle;
6
+ exports.normalizeHandle = normalizeHandle;
7
+ exports.isValidHandle = isValidHandle;
8
+ /**
9
+ * Extract the raw, un-validated, Atproto handle from a DID document.
10
+ */
11
+ function extractAtprotoHandle(document) {
12
+ if (document.alsoKnownAs) {
13
+ for (const h of document.alsoKnownAs) {
14
+ if (h.startsWith('at://')) {
15
+ // strip off "at://" prefix
16
+ return h.slice(5);
17
+ }
18
+ }
19
+ }
20
+ return undefined;
21
+ }
22
+ /**
23
+ * Extracts a validated, normalized Atproto handle from a DID document.
24
+ */
25
+ function extractNormalizedHandle(document) {
26
+ const handle = extractAtprotoHandle(document);
27
+ if (!handle)
28
+ return undefined;
29
+ return asNormalizedHandle(handle);
30
+ }
31
+ function asNormalizedHandle(input) {
32
+ const handle = normalizeHandle(input);
33
+ return isValidHandle(handle) ? handle : undefined;
34
+ }
35
+ function normalizeHandle(handle) {
36
+ return handle.toLowerCase();
37
+ }
38
+ function isValidHandle(handle) {
39
+ return (handle.length > 0 &&
40
+ handle.length < 254 &&
41
+ /^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$/.test(handle));
42
+ }
43
+ //# sourceMappingURL=util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":";;AAQA,oDAYC;AAKD,0DAMC;AAED,gDAGC;AAED,0CAEC;AAED,sCAQC;AA7CD;;GAEG;AACH,SAAgB,oBAAoB,CAClC,QAAgD;IAEhD,IAAI,QAAQ,CAAC,WAAW,EAAE,CAAC;QACzB,KAAK,MAAM,CAAC,IAAI,QAAQ,CAAC,WAAW,EAAE,CAAC;YACrC,IAAI,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC1B,2BAA2B;gBAC3B,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;YACnB,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED;;GAEG;AACH,SAAgB,uBAAuB,CACrC,QAAgD;IAEhD,MAAM,MAAM,GAAG,oBAAoB,CAAC,QAAQ,CAAC,CAAA;IAC7C,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAA;IAC7B,OAAO,kBAAkB,CAAC,MAAM,CAAC,CAAA;AACnC,CAAC;AAED,SAAgB,kBAAkB,CAAC,KAAa;IAC9C,MAAM,MAAM,GAAG,eAAe,CAAC,KAAK,CAAC,CAAA;IACrC,OAAO,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAA;AACnD,CAAC;AAED,SAAgB,eAAe,CAAC,MAAc;IAC5C,OAAO,MAAM,CAAC,WAAW,EAAE,CAAA;AAC7B,CAAC;AAED,SAAgB,aAAa,CAAC,MAAc;IAC1C,OAAO,CACL,MAAM,CAAC,MAAM,GAAG,CAAC;QACjB,MAAM,CAAC,MAAM,GAAG,GAAG;QACnB,4FAA4F,CAAC,IAAI,CAC/F,MAAM,CACP,CACF,CAAA;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto-labs/identity-resolver",
3
- "version": "0.1.19",
3
+ "version": "0.2.0",
4
4
  "license": "MIT",
5
5
  "description": "A library resolving ATPROTO identities",
6
6
  "keywords": [
@@ -26,8 +26,7 @@
26
26
  },
27
27
  "dependencies": {
28
28
  "@atproto-labs/did-resolver": "0.2.0",
29
- "@atproto-labs/handle-resolver": "0.2.0",
30
- "@atproto/syntax": "0.4.0"
29
+ "@atproto-labs/handle-resolver": "0.3.0"
31
30
  },
32
31
  "devDependencies": {
33
32
  "typescript": "^5.6.3"
@@ -0,0 +1,3 @@
1
+ export class IdentityResolverError extends Error {
2
+ name = 'IdentityResolverError'
3
+ }
@@ -1,22 +1,25 @@
1
- import { isValidHandle, normalizeAndEnsureValidHandle } from '@atproto/syntax'
2
1
  import {
3
- Did,
2
+ AtprotoDid,
3
+ AtprotoIdentityDidMethods,
4
4
  DidDocument,
5
5
  DidResolver,
6
- DidService,
7
6
  ResolveDidOptions,
7
+ isAtprotoDid,
8
8
  } from '@atproto-labs/did-resolver'
9
9
  import {
10
- AtprotoIdentityDidMethods,
11
10
  HandleResolver,
12
11
  ResolveHandleOptions,
13
- ResolvedHandle,
14
- isResolvedHandle,
15
12
  } from '@atproto-labs/handle-resolver'
13
+ import { IdentityResolverError } from './identity-resolver-error'
14
+ import { asNormalizedHandle, extractNormalizedHandle } from './util'
16
15
 
17
16
  export type ResolvedIdentity = {
18
- did: NonNullable<ResolvedHandle>
19
- pds: URL
17
+ document: DidDocument<AtprotoIdentityDidMethods>
18
+
19
+ /**
20
+ * Will be defined if the document contains a handle alias that resolves
21
+ * to the document's DID.
22
+ */
20
23
  handle?: string
21
24
  }
22
25
 
@@ -32,30 +35,50 @@ export class IdentityResolver {
32
35
  input: string,
33
36
  options?: ResolveIdentityOptions,
34
37
  ): Promise<ResolvedIdentity> {
35
- const document = isResolvedHandle(input)
36
- ? await this.getDocumentFromDid(input, options)
37
- : await this.getDocumentFromHandle(input, options)
38
+ return isAtprotoDid(input)
39
+ ? this.resolveFromDid(input, options)
40
+ : this.resolveFromHandle(input, options)
41
+ }
38
42
 
39
- const service = document.service?.find(
40
- isAtprotoPersonalDataServerService<AtprotoIdentityDidMethods>,
41
- document,
42
- )
43
+ public async resolveFromDid(
44
+ did: AtprotoDid,
45
+ options?: ResolveDidOptions,
46
+ ): Promise<ResolvedIdentity> {
47
+ const document = await this.getDocumentFromDid(did, options)
43
48
 
44
- if (!service) {
45
- throw new TypeError(
46
- `No valid "AtprotoPersonalDataServer" service found in "${document.id}" DID document`,
47
- )
49
+ options?.signal?.throwIfAborted()
50
+
51
+ // We will only return the document's handle alias if it resolves to the
52
+ // same DID as the input.
53
+ const handle = extractNormalizedHandle(document)
54
+ const resolvedDid = handle
55
+ ? await this.handleResolver
56
+ .resolve(handle, options)
57
+ .catch(() => undefined) // Ignore errors (temporarily unavailable)
58
+ : undefined
59
+
60
+ return {
61
+ document,
62
+ handle: resolvedDid === did ? handle : undefined,
48
63
  }
64
+ }
65
+
66
+ public async resolveFromHandle(
67
+ handle: string,
68
+ options?: ResolveHandleOptions,
69
+ ): Promise<ResolvedIdentity> {
70
+ const document = await this.getDocumentFromHandle(handle, options)
71
+
72
+ // @NOTE bi-directional resolution enforced in getDocumentFromHandle()
49
73
 
50
74
  return {
51
- did: document.id,
52
- pds: new URL(service.serviceEndpoint),
53
- handle: extractHandle(document),
75
+ document,
76
+ handle: extractNormalizedHandle(document),
54
77
  }
55
78
  }
56
79
 
57
80
  public async getDocumentFromDid(
58
- did: NonNullable<ResolvedHandle>,
81
+ did: AtprotoDid,
59
82
  options?: ResolveDidOptions,
60
83
  ): Promise<DidDocument<AtprotoIdentityDidMethods>> {
61
84
  return this.didResolver.resolve(did, options)
@@ -65,12 +88,17 @@ export class IdentityResolver {
65
88
  input: string,
66
89
  options?: ResolveHandleOptions,
67
90
  ): Promise<DidDocument<AtprotoIdentityDidMethods>> {
68
- const handle = normalizeAndEnsureValidHandle(input)
91
+ const handle = asNormalizedHandle(input)
92
+ if (!handle) {
93
+ throw new IdentityResolverError(`Invalid handle "${input}" provided.`)
94
+ }
69
95
 
70
96
  const did = await this.handleResolver.resolve(handle, options)
71
97
 
72
98
  if (!did) {
73
- throw new TypeError(`Handle "${handle}" does not resolve to a DID`)
99
+ throw new IdentityResolverError(
100
+ `Handle "${handle}" does not resolve to a DID`,
101
+ )
74
102
  }
75
103
 
76
104
  options?.signal?.throwIfAborted()
@@ -80,9 +108,9 @@ export class IdentityResolver {
80
108
 
81
109
  const document = await this.didResolver.resolve(did, options)
82
110
 
83
- // Ensure that the handle is included in the document
84
- if (!document.alsoKnownAs?.includes(`at://${handle}`)) {
85
- throw new TypeError(
111
+ // Enforce bi-directional resolution
112
+ if (handle !== extractNormalizedHandle(document)) {
113
+ throw new IdentityResolverError(
86
114
  `Did document for "${did}" does not include the handle "${handle}"`,
87
115
  )
88
116
  }
@@ -90,35 +118,3 @@ export class IdentityResolver {
90
118
  return document
91
119
  }
92
120
  }
93
-
94
- function extractHandle(
95
- document: DidDocument<AtprotoIdentityDidMethods>,
96
- ): string | undefined {
97
- if (document.alsoKnownAs) {
98
- for (const h of document.alsoKnownAs) {
99
- if (h.startsWith('at://')) {
100
- const handle = h.slice(5)
101
- if (isValidHandle(handle)) return handle
102
- }
103
- }
104
- }
105
-
106
- return undefined
107
- }
108
-
109
- function isAtprotoPersonalDataServerService<M extends string>(
110
- this: DidDocument<M>,
111
- s: DidService,
112
- ): s is {
113
- id: '#atproto_pds' | `${Did<M>}#atproto_pds`
114
- type: 'AtprotoPersonalDataServer'
115
- serviceEndpoint: string
116
- } {
117
- return (
118
- typeof s.serviceEndpoint === 'string' &&
119
- s.type === 'AtprotoPersonalDataServer' &&
120
- (s.id.startsWith('#')
121
- ? s.id === '#atproto_pds'
122
- : s.id === `${this.id}#atproto_pds`)
123
- )
124
- }
package/src/index.ts CHANGED
@@ -1 +1,3 @@
1
+ export * from './identity-resolver-error.js'
1
2
  export * from './identity-resolver.js'
3
+ export * from './util.js'
package/src/util.ts ADDED
@@ -0,0 +1,51 @@
1
+ import {
2
+ AtprotoIdentityDidMethods,
3
+ DidDocument,
4
+ } from '@atproto-labs/did-resolver'
5
+
6
+ /**
7
+ * Extract the raw, un-validated, Atproto handle from a DID document.
8
+ */
9
+ export function extractAtprotoHandle(
10
+ document: DidDocument<AtprotoIdentityDidMethods>,
11
+ ): string | undefined {
12
+ if (document.alsoKnownAs) {
13
+ for (const h of document.alsoKnownAs) {
14
+ if (h.startsWith('at://')) {
15
+ // strip off "at://" prefix
16
+ return h.slice(5)
17
+ }
18
+ }
19
+ }
20
+ return undefined
21
+ }
22
+
23
+ /**
24
+ * Extracts a validated, normalized Atproto handle from a DID document.
25
+ */
26
+ export function extractNormalizedHandle(
27
+ document: DidDocument<AtprotoIdentityDidMethods>,
28
+ ): string | undefined {
29
+ const handle = extractAtprotoHandle(document)
30
+ if (!handle) return undefined
31
+ return asNormalizedHandle(handle)
32
+ }
33
+
34
+ export function asNormalizedHandle(input: string): string | undefined {
35
+ const handle = normalizeHandle(input)
36
+ return isValidHandle(handle) ? handle : undefined
37
+ }
38
+
39
+ export function normalizeHandle(handle: string): string {
40
+ return handle.toLowerCase()
41
+ }
42
+
43
+ export function isValidHandle(handle: string): boolean {
44
+ return (
45
+ handle.length > 0 &&
46
+ handle.length < 254 &&
47
+ /^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$/.test(
48
+ handle,
49
+ )
50
+ )
51
+ }
@@ -1 +1 @@
1
- {"root":["./src/identity-resolver.ts","./src/index.ts"],"version":"5.8.2"}
1
+ {"root":["./src/identity-resolver-error.ts","./src/identity-resolver.ts","./src/index.ts","./src/util.ts"],"version":"5.8.2"}