@atproto/oauth-client 0.7.10 → 0.8.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,42 @@
1
1
  # @atproto/oauth-client
2
2
 
3
+ ## 0.8.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#5266](https://github.com/bluesky-social/atproto/pull/5266) [`f136380`](https://github.com/bluesky-social/atproto/commit/f136380964a8ea52ef031a9732d3d1f1f1ab0c4a) Thanks [@matthieusieben](https://github.com/matthieusieben)! - **BREAKING:** Rename `SessionHooks` into `SessionGetterOptions`. `onUpdate` and `onDelete` hooks are now named `onSessionUpdated` and `onSessionDeleted`, respectively.
8
+
9
+ ### Patch Changes
10
+
11
+ - [#5266](https://github.com/bluesky-social/atproto/pull/5266) [`f136380`](https://github.com/bluesky-social/atproto/commit/f136380964a8ea52ef031a9732d3d1f1f1ab0c4a) Thanks [@matthieusieben](https://github.com/matthieusieben)! - `SessionGetter`'s private properties are now really private.
12
+
13
+ - [#5266](https://github.com/bluesky-social/atproto/pull/5266) [`f136380`](https://github.com/bluesky-social/atproto/commit/f136380964a8ea52ef031a9732d3d1f1f1ab0c4a) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Remove implicit `any` types in internal utilities (`runtime`, `oauth-response-error`, `fetch-dpop`) so the package type-checks under `noImplicitAny`. No runtime behavior change.
14
+
15
+ - [#5266](https://github.com/bluesky-social/atproto/pull/5266) [`f136380`](https://github.com/bluesky-social/atproto/commit/f136380964a8ea52ef031a9732d3d1f1f1ab0c4a) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Add `AtprotoDid` as part of exported types
16
+
17
+ - [#5266](https://github.com/bluesky-social/atproto/pull/5266) [`f136380`](https://github.com/bluesky-social/atproto/commit/f136380964a8ea52ef031a9732d3d1f1f1ab0c4a) Thanks [@matthieusieben](https://github.com/matthieusieben)! - The `SessionGetter` now propagates the `AbortSignal` when fetching the Authorization Server metadata, allowing it to abort the request earlier.
18
+
19
+ - [#5266](https://github.com/bluesky-social/atproto/pull/5266) [`f136380`](https://github.com/bluesky-social/atproto/commit/f136380964a8ea52ef031a9732d3d1f1f1ab0c4a) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Throw an `AggregateError` containing every error that occurred while saving session data from the `SessionGetter` to the `SessionStore`.
20
+
21
+ - [#5266](https://github.com/bluesky-social/atproto/pull/5266) [`f136380`](https://github.com/bluesky-social/atproto/commit/f136380964a8ea52ef031a9732d3d1f1f1ab0c4a) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Errors thrown by the metadata caches (authorization server & protected resource) are now logged (previously they were silently ignored on read) while continuing to degrade to a cache miss and a refetch. A new `onCacheError` config option on both metadata resolvers allows overriding how these errors are handled.
22
+
23
+ - Updated dependencies [[`f136380`](https://github.com/bluesky-social/atproto/commit/f136380964a8ea52ef031a9732d3d1f1f1ab0c4a), [`f136380`](https://github.com/bluesky-social/atproto/commit/f136380964a8ea52ef031a9732d3d1f1f1ab0c4a), [`f136380`](https://github.com/bluesky-social/atproto/commit/f136380964a8ea52ef031a9732d3d1f1f1ab0c4a), [`f136380`](https://github.com/bluesky-social/atproto/commit/f136380964a8ea52ef031a9732d3d1f1f1ab0c4a), [`f136380`](https://github.com/bluesky-social/atproto/commit/f136380964a8ea52ef031a9732d3d1f1f1ab0c4a), [`f136380`](https://github.com/bluesky-social/atproto/commit/f136380964a8ea52ef031a9732d3d1f1f1ab0c4a), [`f136380`](https://github.com/bluesky-social/atproto/commit/f136380964a8ea52ef031a9732d3d1f1f1ab0c4a)]:
24
+ - @atproto-labs/handle-resolver@0.4.7
25
+ - @atproto-labs/simple-store@0.5.0
26
+ - @atproto-labs/fetch@0.3.5
27
+ - @atproto-labs/did-resolver@0.3.6
28
+ - @atproto-labs/identity-resolver@0.4.6
29
+ - @atproto-labs/simple-store-memory@0.2.5
30
+ - @atproto/xrpc@0.8.7
31
+
32
+ ## 0.7.11
33
+
34
+ ### Patch Changes
35
+
36
+ - Updated dependencies [[`ce386ee`](https://github.com/bluesky-social/atproto/commit/ce386eed324f813601db57f873fbb3b987492001)]:
37
+ - @atproto-labs/handle-resolver@0.4.6
38
+ - @atproto-labs/identity-resolver@0.4.5
39
+
3
40
  ## 0.7.10
4
41
 
5
42
  ### Patch Changes
@@ -140,7 +140,7 @@ async function isUseDpopNonceError(response, isAuthServer) {
140
140
  if (response.status === 400) {
141
141
  try {
142
142
  const json = await peekJson(response, 10 * 1024);
143
- return typeof json === 'object' && json?.['error'] === 'use_dpop_nonce';
143
+ return json?.['error'] === 'use_dpop_nonce';
144
144
  }
145
145
  catch {
146
146
  // Response too big (to be "use_dpop_nonce" error) or invalid JSON
@@ -1 +1 @@
1
- {"version":3,"file":"fetch-dpop.js","sourceRoot":"","sources":["../src/fetch-dpop.ts"],"names":[],"mappings":"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","sourcesContent":["import { base64url } from 'multiformats/bases/base64'\nimport type { Key } from '@atproto/jwk'\nimport {\n type Fetch,\n type FetchContext,\n cancelBody,\n peekJson,\n} from '@atproto-labs/fetch'\nimport type { SimpleStore } from '@atproto-labs/simple-store'\n\n// \"undefined\" in non https environments or environments without crypto\nconst subtle = globalThis.crypto?.subtle as SubtleCrypto | undefined\n\nconst ReadableStream = globalThis.ReadableStream as\n | typeof globalThis.ReadableStream\n | undefined\n\nexport type DpopFetchWrapperOptions<C = FetchContext> = {\n key: Key\n nonces: SimpleStore<string, string>\n supportedAlgs?: string[]\n sha256?: (input: string) => Promise<string>\n\n /**\n * Is the intended server an authorization server (true) or a resource server\n * (false)? Setting this may allow to avoid parsing the response body to\n * determine the dpop-nonce.\n *\n * @default undefined\n */\n isAuthServer?: boolean\n fetch?: Fetch<C>\n}\n\nexport function dpopFetchWrapper<C = FetchContext>({\n key,\n // @TODO we should provide a default based on specs\n supportedAlgs,\n nonces,\n sha256 = typeof subtle !== 'undefined' ? subtleSha256 : undefined,\n isAuthServer,\n fetch = globalThis.fetch,\n}: DpopFetchWrapperOptions<C>): Fetch<C> {\n if (!sha256) {\n throw new TypeError(\n `crypto.subtle is not available in this environment. Please provide a sha256 function.`,\n )\n }\n\n // Throws if negotiation fails\n const alg = negotiateAlg(key, supportedAlgs)\n\n return async function (this: C, input, init) {\n const request: Request =\n init == null && input instanceof Request\n ? input\n : new Request(input, init)\n\n const authorizationHeader = request.headers.get('Authorization')\n const ath = authorizationHeader?.startsWith('DPoP ')\n ? await sha256(authorizationHeader.slice(5))\n : undefined\n\n const { origin } = new URL(request.url)\n\n const htm = request.method\n const htu = buildHtu(request.url)\n\n let initNonce: string | undefined\n try {\n initNonce = await nonces.get(origin)\n } catch {\n // Ignore get errors, we will just not send a nonce\n }\n\n const initProof = await buildProof(key, alg, htm, htu, initNonce, ath)\n request.headers.set('DPoP', initProof)\n\n const initResponse = await fetch.call(this, request)\n\n // Make sure the response body is consumed. Either by the caller (when the\n // response is returned), of if an error is thrown (catch block).\n\n const nextNonce = initResponse.headers.get('DPoP-Nonce')\n if (!nextNonce || nextNonce === initNonce) {\n // No nonce was returned or it is the same as the one we sent. No need to\n // update the nonce store, or retry the request.\n return initResponse\n }\n\n // Store the fresh nonce for future requests\n try {\n await nonces.set(origin, nextNonce)\n } catch {\n // Ignore set errors\n }\n\n const shouldRetry = await isUseDpopNonceError(initResponse, isAuthServer)\n if (!shouldRetry) {\n // Not a \"use_dpop_nonce\" error, so there is no need to retry\n return initResponse\n }\n\n // If the input stream was already consumed, we cannot retry the request. A\n // solution would be to clone() the request but that would bufferize the\n // entire stream in memory which can lead to memory starvation. Instead, we\n // will return the original response and let the calling code handle retries.\n\n if (input === request) {\n // The input request body was consumed. We cannot retry the request.\n return initResponse\n }\n\n if (ReadableStream && init?.body instanceof ReadableStream) {\n // The init body was consumed. We cannot retry the request.\n return initResponse\n }\n\n // We will now retry the request with the fresh nonce.\n\n // The initial response body must be consumed (see cancelBody's doc).\n await cancelBody(initResponse, 'log')\n\n const nextProof = await buildProof(key, alg, htm, htu, nextNonce, ath)\n const nextRequest = new Request(input, init)\n nextRequest.headers.set('DPoP', nextProof)\n\n const retryRequest = await fetch.call(this, nextRequest)\n const retryNonce = retryRequest.headers.get('DPoP-Nonce')\n if (!retryNonce || retryNonce === initNonce) {\n // No nonce was returned or it is the same as the one we sent. No need to\n // update the nonce store, or retry the request.\n return retryRequest\n }\n\n // Store the fresh nonce for future requests\n try {\n await nonces.set(origin, retryNonce)\n } catch {\n // Ignore set errors\n }\n\n return retryRequest\n }\n}\n\n/**\n * Strip query and fragment\n *\n * @see {@link https://www.rfc-editor.org/rfc/rfc9449.html#section-4.2-4.6}\n */\nfunction buildHtu(url: string): string {\n const fragmentIndex = url.indexOf('#')\n const queryIndex = url.indexOf('?')\n\n const end =\n fragmentIndex === -1\n ? queryIndex\n : queryIndex === -1\n ? fragmentIndex\n : Math.min(fragmentIndex, queryIndex)\n\n return end === -1 ? url : url.slice(0, end)\n}\n\nasync function buildProof(\n key: Key,\n alg: string,\n htm: string,\n htu: string,\n nonce?: string,\n ath?: string,\n) {\n const jwk = key.bareJwk\n if (!jwk) {\n throw new Error('Only asymmetric keys can be used as DPoP proofs')\n }\n\n const now = Math.floor(Date.now() / 1e3)\n\n return key.createJwt(\n // https://datatracker.ietf.org/doc/html/rfc9449#section-4.2\n {\n alg,\n typ: 'dpop+jwt',\n jwk,\n },\n {\n iat: now,\n // Any collision will cause the request to be rejected by the server. no biggie.\n jti: Math.random().toString(36).slice(2),\n htm,\n htu,\n nonce,\n ath,\n },\n )\n}\n\nasync function isUseDpopNonceError(\n response: Response,\n isAuthServer?: boolean,\n): Promise<boolean> {\n // https://datatracker.ietf.org/doc/html/rfc6750#section-3\n // https://datatracker.ietf.org/doc/html/rfc9449#name-resource-server-provided-no\n if (isAuthServer === undefined || isAuthServer === false) {\n if (response.status === 401) {\n const wwwAuth = response.headers.get('WWW-Authenticate')\n if (wwwAuth?.startsWith('DPoP')) {\n return wwwAuth.includes('error=\"use_dpop_nonce\"')\n }\n }\n }\n\n // https://datatracker.ietf.org/doc/html/rfc9449#name-authorization-server-provid\n if (isAuthServer === undefined || isAuthServer === true) {\n if (response.status === 400) {\n try {\n const json = await peekJson(response, 10 * 1024)\n return typeof json === 'object' && json?.['error'] === 'use_dpop_nonce'\n } catch {\n // Response too big (to be \"use_dpop_nonce\" error) or invalid JSON\n return false\n }\n }\n }\n\n return false\n}\n\nfunction negotiateAlg(key: Key, supportedAlgs: string[] | undefined): string {\n if (supportedAlgs) {\n // Use order of supportedAlgs as preference\n const alg = supportedAlgs.find((a) => key.algorithms.includes(a))\n if (alg) return alg\n } else {\n const [alg] = key.algorithms\n if (alg) return alg\n }\n\n throw new Error('Key does not match any alg supported by the server')\n}\n\nasync function subtleSha256(input: string): Promise<string> {\n if (subtle == null) {\n throw new Error(\n `crypto.subtle is not available in this environment. Please provide a sha256 function.`,\n )\n }\n\n const bytes = new TextEncoder().encode(input)\n const digest = await subtle.digest('SHA-256', bytes)\n const digestBytes = new Uint8Array(digest)\n return base64url.baseEncode(digestBytes)\n}\n"]}
1
+ {"version":3,"file":"fetch-dpop.js","sourceRoot":"","sources":["../src/fetch-dpop.ts"],"names":[],"mappings":"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","sourcesContent":["import { base64url } from 'multiformats/bases/base64'\nimport type { Key } from '@atproto/jwk'\nimport {\n type Fetch,\n type FetchContext,\n cancelBody,\n peekJson,\n} from '@atproto-labs/fetch'\nimport type { SimpleStore } from '@atproto-labs/simple-store'\n\n// \"undefined\" in non https environments or environments without crypto\nconst subtle = globalThis.crypto?.subtle as SubtleCrypto | undefined\n\nconst ReadableStream = globalThis.ReadableStream as\n | typeof globalThis.ReadableStream\n | undefined\n\nexport type DpopFetchWrapperOptions<C = FetchContext> = {\n key: Key\n nonces: SimpleStore<string, string>\n supportedAlgs?: string[]\n sha256?: (input: string) => Promise<string>\n\n /**\n * Is the intended server an authorization server (true) or a resource server\n * (false)? Setting this may allow to avoid parsing the response body to\n * determine the dpop-nonce.\n *\n * @default undefined\n */\n isAuthServer?: boolean\n fetch?: Fetch<C>\n}\n\nexport function dpopFetchWrapper<C = FetchContext>({\n key,\n // @TODO we should provide a default based on specs\n supportedAlgs,\n nonces,\n sha256 = typeof subtle !== 'undefined' ? subtleSha256 : undefined,\n isAuthServer,\n fetch = globalThis.fetch,\n}: DpopFetchWrapperOptions<C>): Fetch<C> {\n if (!sha256) {\n throw new TypeError(\n `crypto.subtle is not available in this environment. Please provide a sha256 function.`,\n )\n }\n\n // Throws if negotiation fails\n const alg = negotiateAlg(key, supportedAlgs)\n\n return async function (this: C, input, init) {\n const request: Request =\n init == null && input instanceof Request\n ? input\n : new Request(input, init)\n\n const authorizationHeader = request.headers.get('Authorization')\n const ath = authorizationHeader?.startsWith('DPoP ')\n ? await sha256(authorizationHeader.slice(5))\n : undefined\n\n const { origin } = new URL(request.url)\n\n const htm = request.method\n const htu = buildHtu(request.url)\n\n let initNonce: string | undefined\n try {\n initNonce = await nonces.get(origin)\n } catch {\n // Ignore get errors, we will just not send a nonce\n }\n\n const initProof = await buildProof(key, alg, htm, htu, initNonce, ath)\n request.headers.set('DPoP', initProof)\n\n const initResponse = await fetch.call(this, request)\n\n // Make sure the response body is consumed. Either by the caller (when the\n // response is returned), of if an error is thrown (catch block).\n\n const nextNonce = initResponse.headers.get('DPoP-Nonce')\n if (!nextNonce || nextNonce === initNonce) {\n // No nonce was returned or it is the same as the one we sent. No need to\n // update the nonce store, or retry the request.\n return initResponse\n }\n\n // Store the fresh nonce for future requests\n try {\n await nonces.set(origin, nextNonce)\n } catch {\n // Ignore set errors\n }\n\n const shouldRetry = await isUseDpopNonceError(initResponse, isAuthServer)\n if (!shouldRetry) {\n // Not a \"use_dpop_nonce\" error, so there is no need to retry\n return initResponse\n }\n\n // If the input stream was already consumed, we cannot retry the request. A\n // solution would be to clone() the request but that would bufferize the\n // entire stream in memory which can lead to memory starvation. Instead, we\n // will return the original response and let the calling code handle retries.\n\n if (input === request) {\n // The input request body was consumed. We cannot retry the request.\n return initResponse\n }\n\n if (ReadableStream && init?.body instanceof ReadableStream) {\n // The init body was consumed. We cannot retry the request.\n return initResponse\n }\n\n // We will now retry the request with the fresh nonce.\n\n // The initial response body must be consumed (see cancelBody's doc).\n await cancelBody(initResponse, 'log')\n\n const nextProof = await buildProof(key, alg, htm, htu, nextNonce, ath)\n const nextRequest = new Request(input, init)\n nextRequest.headers.set('DPoP', nextProof)\n\n const retryRequest = await fetch.call(this, nextRequest)\n const retryNonce = retryRequest.headers.get('DPoP-Nonce')\n if (!retryNonce || retryNonce === initNonce) {\n // No nonce was returned or it is the same as the one we sent. No need to\n // update the nonce store, or retry the request.\n return retryRequest\n }\n\n // Store the fresh nonce for future requests\n try {\n await nonces.set(origin, retryNonce)\n } catch {\n // Ignore set errors\n }\n\n return retryRequest\n }\n}\n\n/**\n * Strip query and fragment\n *\n * @see {@link https://www.rfc-editor.org/rfc/rfc9449.html#section-4.2-4.6}\n */\nfunction buildHtu(url: string): string {\n const fragmentIndex = url.indexOf('#')\n const queryIndex = url.indexOf('?')\n\n const end =\n fragmentIndex === -1\n ? queryIndex\n : queryIndex === -1\n ? fragmentIndex\n : Math.min(fragmentIndex, queryIndex)\n\n return end === -1 ? url : url.slice(0, end)\n}\n\nasync function buildProof(\n key: Key,\n alg: string,\n htm: string,\n htu: string,\n nonce?: string,\n ath?: string,\n) {\n const jwk = key.bareJwk\n if (!jwk) {\n throw new Error('Only asymmetric keys can be used as DPoP proofs')\n }\n\n const now = Math.floor(Date.now() / 1e3)\n\n return key.createJwt(\n // https://datatracker.ietf.org/doc/html/rfc9449#section-4.2\n {\n alg,\n typ: 'dpop+jwt',\n jwk,\n },\n {\n iat: now,\n // Any collision will cause the request to be rejected by the server. no biggie.\n jti: Math.random().toString(36).slice(2),\n htm,\n htu,\n nonce,\n ath,\n },\n )\n}\n\nasync function isUseDpopNonceError(\n response: Response,\n isAuthServer?: boolean,\n): Promise<boolean> {\n // https://datatracker.ietf.org/doc/html/rfc6750#section-3\n // https://datatracker.ietf.org/doc/html/rfc9449#name-resource-server-provided-no\n if (isAuthServer === undefined || isAuthServer === false) {\n if (response.status === 401) {\n const wwwAuth = response.headers.get('WWW-Authenticate')\n if (wwwAuth?.startsWith('DPoP')) {\n return wwwAuth.includes('error=\"use_dpop_nonce\"')\n }\n }\n }\n\n // https://datatracker.ietf.org/doc/html/rfc9449#name-authorization-server-provid\n if (isAuthServer === undefined || isAuthServer === true) {\n if (response.status === 400) {\n try {\n const json = await peekJson(response, 10 * 1024)\n return (json as any)?.['error'] === 'use_dpop_nonce'\n } catch {\n // Response too big (to be \"use_dpop_nonce\" error) or invalid JSON\n return false\n }\n }\n }\n\n return false\n}\n\nfunction negotiateAlg(key: Key, supportedAlgs: string[] | undefined): string {\n if (supportedAlgs) {\n // Use order of supportedAlgs as preference\n const alg = supportedAlgs.find((a) => key.algorithms.includes(a))\n if (alg) return alg\n } else {\n const [alg] = key.algorithms\n if (alg) return alg\n }\n\n throw new Error('Key does not match any alg supported by the server')\n}\n\nasync function subtleSha256(input: string): Promise<string> {\n if (subtle == null) {\n throw new Error(\n `crypto.subtle is not available in this environment. Please provide a sha256 function.`,\n )\n }\n\n const bytes = new TextEncoder().encode(input)\n const digest = await subtle.digest('SHA-256', bytes)\n const digestBytes = new Uint8Array(digest)\n return base64url.baseEncode(digestBytes)\n}\n"]}
@@ -1,10 +1,16 @@
1
1
  import { type OAuthAuthorizationServerMetadata } from '@atproto/oauth-types';
2
2
  import { type Fetch } from '@atproto-labs/fetch';
3
- import { CachedGetter, type GetCachedOptions, type SimpleStore } from '@atproto-labs/simple-store';
3
+ import { CachedGetter, type GetCachedOptions, type SimpleStore, type StoreErrorHandler } from '@atproto-labs/simple-store';
4
4
  export type { GetCachedOptions, OAuthAuthorizationServerMetadata };
5
5
  export type AuthorizationServerMetadataCache = SimpleStore<string, OAuthAuthorizationServerMetadata>;
6
6
  export type OAuthAuthorizationServerMetadataResolverConfig = {
7
7
  allowHttpIssuer?: boolean;
8
+ /**
9
+ * Called when the metadata cache throws. The cache is best-effort: failures
10
+ * are logged (by default) and degrade to a cache miss + refetch, rather than
11
+ * being propagated.
12
+ */
13
+ onCacheError?: StoreErrorHandler<string>;
8
14
  };
9
15
  /**
10
16
  * @see {@link https://datatracker.ietf.org/doc/html/rfc8414}
@@ -1 +1 @@
1
- {"version":3,"file":"oauth-authorization-server-metadata-resolver.d.ts","sourceRoot":"","sources":["../src/oauth-authorization-server-metadata-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,gCAAgC,EAGtC,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EACL,KAAK,KAAK,EAIX,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACL,YAAY,EACZ,KAAK,gBAAgB,EACrB,KAAK,WAAW,EACjB,MAAM,4BAA4B,CAAA;AAGnC,YAAY,EAAE,gBAAgB,EAAE,gCAAgC,EAAE,CAAA;AAElE,MAAM,MAAM,gCAAgC,GAAG,WAAW,CACxD,MAAM,EACN,gCAAgC,CACjC,CAAA;AAED,MAAM,MAAM,8CAA8C,GAAG;IAC3D,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B,CAAA;AAED;;GAEG;AACH,qBAAa,wCAAyC,SAAQ,YAAY,CACxE,MAAM,EACN,gCAAgC,CACjC;IACC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgB;IACtC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IAEzC,YACE,KAAK,EAAE,gCAAgC,EACvC,KAAK,CAAC,EAAE,KAAK,EACb,MAAM,CAAC,EAAE,8CAA8C,EAMxD;IAEK,GAAG,CACP,KAAK,EAAE,GAAG,GAAG,MAAM,EACnB,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,gCAAgC,CAAC,CAQ3C;YAEa,aAAa;CAwD5B"}
1
+ {"version":3,"file":"oauth-authorization-server-metadata-resolver.d.ts","sourceRoot":"","sources":["../src/oauth-authorization-server-metadata-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,gCAAgC,EAGtC,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EACL,KAAK,KAAK,EAIX,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACL,YAAY,EACZ,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAChB,KAAK,iBAAiB,EAEvB,MAAM,4BAA4B,CAAA;AAGnC,YAAY,EAAE,gBAAgB,EAAE,gCAAgC,EAAE,CAAA;AAElE,MAAM,MAAM,gCAAgC,GAAG,WAAW,CACxD,MAAM,EACN,gCAAgC,CACjC,CAAA;AAED,MAAM,MAAM,8CAA8C,GAAG;IAC3D,eAAe,CAAC,EAAE,OAAO,CAAA;IAEzB;;;;OAIG;IACH,YAAY,CAAC,EAAE,iBAAiB,CAAC,MAAM,CAAC,CAAA;CACzC,CAAA;AAED;;GAEG;AACH,qBAAa,wCAAyC,SAAQ,YAAY,CACxE,MAAM,EACN,gCAAgC,CACjC;IACC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgB;IACtC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IAEzC,YACE,KAAK,EAAE,gCAAgC,EACvC,KAAK,CAAC,EAAE,KAAK,EACb,MAAM,CAAC,EAAE,8CAA8C,EASxD;IAEK,GAAG,CACP,KAAK,EAAE,GAAG,GAAG,MAAM,EACnB,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,gCAAgC,CAAC,CAQ3C;YAEa,aAAa;CAwD5B"}
@@ -1,13 +1,13 @@
1
1
  import { oauthAuthorizationServerMetadataValidator, oauthIssuerIdentifierSchema, } from '@atproto/oauth-types';
2
2
  import { FetchResponseError, bindFetch, cancelBody, } from '@atproto-labs/fetch';
3
- import { CachedGetter, } from '@atproto-labs/simple-store';
3
+ import { CachedGetter, swallowStoreErrors, } from '@atproto-labs/simple-store';
4
4
  import { contentMime } from './util.js';
5
5
  /**
6
6
  * @see {@link https://datatracker.ietf.org/doc/html/rfc8414}
7
7
  */
8
8
  export class OAuthAuthorizationServerMetadataResolver extends CachedGetter {
9
9
  constructor(cache, fetch, config) {
10
- super(async (issuer, options) => this.fetchMetadata(issuer, options), cache);
10
+ super(async (issuer, options) => this.fetchMetadata(issuer, options), swallowStoreErrors(cache, config?.onCacheError));
11
11
  this.fetch = bindFetch(fetch);
12
12
  this.allowHttpIssuer = config?.allowHttpIssuer === true;
13
13
  }
@@ -1 +1 @@
1
- {"version":3,"file":"oauth-authorization-server-metadata-resolver.js","sourceRoot":"","sources":["../src/oauth-authorization-server-metadata-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type OAuthAuthorizationServerMetadata,\n oauthAuthorizationServerMetadataValidator,\n oauthIssuerIdentifierSchema,\n} from '@atproto/oauth-types'\nimport {\n type Fetch,\n FetchResponseError,\n bindFetch,\n cancelBody,\n} from '@atproto-labs/fetch'\nimport {\n CachedGetter,\n type GetCachedOptions,\n type SimpleStore,\n} from '@atproto-labs/simple-store'\nimport { contentMime } from './util.js'\n\nexport type { GetCachedOptions, OAuthAuthorizationServerMetadata }\n\nexport type AuthorizationServerMetadataCache = SimpleStore<\n string,\n OAuthAuthorizationServerMetadata\n>\n\nexport type OAuthAuthorizationServerMetadataResolverConfig = {\n allowHttpIssuer?: boolean\n}\n\n/**\n * @see {@link https://datatracker.ietf.org/doc/html/rfc8414}\n */\nexport class OAuthAuthorizationServerMetadataResolver extends CachedGetter<\n string,\n OAuthAuthorizationServerMetadata\n> {\n private readonly fetch: Fetch<unknown>\n private readonly allowHttpIssuer: boolean\n\n constructor(\n cache: AuthorizationServerMetadataCache,\n fetch?: Fetch,\n config?: OAuthAuthorizationServerMetadataResolverConfig,\n ) {\n super(async (issuer, options) => this.fetchMetadata(issuer, options), cache)\n\n this.fetch = bindFetch(fetch)\n this.allowHttpIssuer = config?.allowHttpIssuer === true\n }\n\n async get(\n input: URL | string,\n options?: GetCachedOptions,\n ): Promise<OAuthAuthorizationServerMetadata> {\n const issuer = oauthIssuerIdentifierSchema.parse(String(input))\n if (!this.allowHttpIssuer && issuer.startsWith('http:')) {\n throw new TypeError(\n 'Unsecure issuer URL protocol only allowed in development and test environments',\n )\n }\n return super.get(issuer, options)\n }\n\n private async fetchMetadata(\n issuer: string,\n options?: GetCachedOptions,\n ): Promise<OAuthAuthorizationServerMetadata> {\n const url = new URL(`/.well-known/oauth-authorization-server`, issuer)\n const request = new Request(url, {\n headers: { accept: 'application/json' },\n cache: options?.noCache ? 'no-cache' : undefined,\n signal: options?.signal,\n redirect: 'manual', // response must be 200 OK\n })\n\n const response = await this.fetch(request)\n\n // https://datatracker.ietf.org/doc/html/rfc8414#section-3.2\n if (response.status !== 200) {\n await cancelBody(response, 'log')\n throw await FetchResponseError.from(\n response,\n `Unexpected status code ${response.status} for \"${url}\"`,\n undefined,\n { cause: request },\n )\n }\n\n if (contentMime(response.headers) !== 'application/json') {\n await cancelBody(response, 'log')\n throw await FetchResponseError.from(\n response,\n `Unexpected content type for \"${url}\"`,\n undefined,\n { cause: request },\n )\n }\n\n const metadata = oauthAuthorizationServerMetadataValidator.parse(\n await response.json(),\n )\n\n // Validate the issuer (MIX-UP attacks)\n // https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#name-mix-up-attacks\n // https://datatracker.ietf.org/doc/html/rfc8414#section-2\n if (metadata.issuer !== issuer) {\n throw new TypeError(`Invalid issuer ${metadata.issuer}`)\n }\n\n // ATPROTO requires client_id_metadata_document\n // https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/\n if (metadata.client_id_metadata_document_supported !== true) {\n throw new TypeError(\n `Authorization server \"${issuer}\" does not support client_id_metadata_document`,\n )\n }\n\n return metadata\n }\n}\n"]}
1
+ {"version":3,"file":"oauth-authorization-server-metadata-resolver.js","sourceRoot":"","sources":["../src/oauth-authorization-server-metadata-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,yCAAyC,EACzC,2BAA2B,GAC5B,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAEL,kBAAkB,EAClB,SAAS,EACT,UAAU,GACX,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACL,YAAY,EAIZ,kBAAkB,GACnB,MAAM,4BAA4B,CAAA;AACnC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAoBvC;;GAEG;AACH,MAAM,OAAO,wCAAyC,SAAQ,YAG7D;IAIC,YACE,KAAuC,EACvC,KAAa,EACb,MAAuD;QAEvD,KAAK,CACH,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9D,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,CAAC,CAChD,CAAA;QAED,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,CAAA;QAC7B,IAAI,CAAC,eAAe,GAAG,MAAM,EAAE,eAAe,KAAK,IAAI,CAAA;IACzD,CAAC;IAED,KAAK,CAAC,GAAG,CACP,KAAmB,EACnB,OAA0B;QAE1B,MAAM,MAAM,GAAG,2BAA2B,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;QAC/D,IAAI,CAAC,IAAI,CAAC,eAAe,IAAI,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YACxD,MAAM,IAAI,SAAS,CACjB,gFAAgF,CACjF,CAAA;QACH,CAAC;QACD,OAAO,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACnC,CAAC;IAEO,KAAK,CAAC,aAAa,CACzB,MAAc,EACd,OAA0B;QAE1B,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,yCAAyC,EAAE,MAAM,CAAC,CAAA;QACtE,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE;YAC/B,OAAO,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE;YACvC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS;YAChD,MAAM,EAAE,OAAO,EAAE,MAAM;YACvB,QAAQ,EAAE,QAAQ,EAAE,0BAA0B;SAC/C,CAAC,CAAA;QAEF,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;QAE1C,4DAA4D;QAC5D,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC5B,MAAM,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;YACjC,MAAM,MAAM,kBAAkB,CAAC,IAAI,CACjC,QAAQ,EACR,0BAA0B,QAAQ,CAAC,MAAM,SAAS,GAAG,GAAG,EACxD,SAAS,EACT,EAAE,KAAK,EAAE,OAAO,EAAE,CACnB,CAAA;QACH,CAAC;QAED,IAAI,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,kBAAkB,EAAE,CAAC;YACzD,MAAM,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;YACjC,MAAM,MAAM,kBAAkB,CAAC,IAAI,CACjC,QAAQ,EACR,gCAAgC,GAAG,GAAG,EACtC,SAAS,EACT,EAAE,KAAK,EAAE,OAAO,EAAE,CACnB,CAAA;QACH,CAAC;QAED,MAAM,QAAQ,GAAG,yCAAyC,CAAC,KAAK,CAC9D,MAAM,QAAQ,CAAC,IAAI,EAAE,CACtB,CAAA;QAED,uCAAuC;QACvC,6FAA6F;QAC7F,0DAA0D;QAC1D,IAAI,QAAQ,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;YAC/B,MAAM,IAAI,SAAS,CAAC,kBAAkB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAA;QAC1D,CAAC;QAED,+CAA+C;QAC/C,iFAAiF;QACjF,IAAI,QAAQ,CAAC,qCAAqC,KAAK,IAAI,EAAE,CAAC;YAC5D,MAAM,IAAI,SAAS,CACjB,yBAAyB,MAAM,gDAAgD,CAChF,CAAA;QACH,CAAC;QAED,OAAO,QAAQ,CAAA;IACjB,CAAC;CACF","sourcesContent":["import {\n type OAuthAuthorizationServerMetadata,\n oauthAuthorizationServerMetadataValidator,\n oauthIssuerIdentifierSchema,\n} from '@atproto/oauth-types'\nimport {\n type Fetch,\n FetchResponseError,\n bindFetch,\n cancelBody,\n} from '@atproto-labs/fetch'\nimport {\n CachedGetter,\n type GetCachedOptions,\n type SimpleStore,\n type StoreErrorHandler,\n swallowStoreErrors,\n} from '@atproto-labs/simple-store'\nimport { contentMime } from './util.js'\n\nexport type { GetCachedOptions, OAuthAuthorizationServerMetadata }\n\nexport type AuthorizationServerMetadataCache = SimpleStore<\n string,\n OAuthAuthorizationServerMetadata\n>\n\nexport type OAuthAuthorizationServerMetadataResolverConfig = {\n allowHttpIssuer?: boolean\n\n /**\n * Called when the metadata cache throws. The cache is best-effort: failures\n * are logged (by default) and degrade to a cache miss + refetch, rather than\n * being propagated.\n */\n onCacheError?: StoreErrorHandler<string>\n}\n\n/**\n * @see {@link https://datatracker.ietf.org/doc/html/rfc8414}\n */\nexport class OAuthAuthorizationServerMetadataResolver extends CachedGetter<\n string,\n OAuthAuthorizationServerMetadata\n> {\n private readonly fetch: Fetch<unknown>\n private readonly allowHttpIssuer: boolean\n\n constructor(\n cache: AuthorizationServerMetadataCache,\n fetch?: Fetch,\n config?: OAuthAuthorizationServerMetadataResolverConfig,\n ) {\n super(\n async (issuer, options) => this.fetchMetadata(issuer, options),\n swallowStoreErrors(cache, config?.onCacheError),\n )\n\n this.fetch = bindFetch(fetch)\n this.allowHttpIssuer = config?.allowHttpIssuer === true\n }\n\n async get(\n input: URL | string,\n options?: GetCachedOptions,\n ): Promise<OAuthAuthorizationServerMetadata> {\n const issuer = oauthIssuerIdentifierSchema.parse(String(input))\n if (!this.allowHttpIssuer && issuer.startsWith('http:')) {\n throw new TypeError(\n 'Unsecure issuer URL protocol only allowed in development and test environments',\n )\n }\n return super.get(issuer, options)\n }\n\n private async fetchMetadata(\n issuer: string,\n options?: GetCachedOptions,\n ): Promise<OAuthAuthorizationServerMetadata> {\n const url = new URL(`/.well-known/oauth-authorization-server`, issuer)\n const request = new Request(url, {\n headers: { accept: 'application/json' },\n cache: options?.noCache ? 'no-cache' : undefined,\n signal: options?.signal,\n redirect: 'manual', // response must be 200 OK\n })\n\n const response = await this.fetch(request)\n\n // https://datatracker.ietf.org/doc/html/rfc8414#section-3.2\n if (response.status !== 200) {\n await cancelBody(response, 'log')\n throw await FetchResponseError.from(\n response,\n `Unexpected status code ${response.status} for \"${url}\"`,\n undefined,\n { cause: request },\n )\n }\n\n if (contentMime(response.headers) !== 'application/json') {\n await cancelBody(response, 'log')\n throw await FetchResponseError.from(\n response,\n `Unexpected content type for \"${url}\"`,\n undefined,\n { cause: request },\n )\n }\n\n const metadata = oauthAuthorizationServerMetadataValidator.parse(\n await response.json(),\n )\n\n // Validate the issuer (MIX-UP attacks)\n // https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#name-mix-up-attacks\n // https://datatracker.ietf.org/doc/html/rfc8414#section-2\n if (metadata.issuer !== issuer) {\n throw new TypeError(`Invalid issuer ${metadata.issuer}`)\n }\n\n // ATPROTO requires client_id_metadata_document\n // https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/\n if (metadata.client_id_metadata_document_supported !== true) {\n throw new TypeError(\n `Authorization server \"${issuer}\" does not support client_id_metadata_document`,\n )\n }\n\n return metadata\n }\n}\n"]}
@@ -12,10 +12,10 @@ import { OAuthServerFactory } from './oauth-server-factory.js';
12
12
  import { OAuthSession } from './oauth-session.js';
13
13
  import type { RuntimeImplementation } from './runtime-implementation.js';
14
14
  import { Runtime } from './runtime.js';
15
- import { SessionGetter, type SessionHooks, type SessionStore } from './session-getter.js';
15
+ import { SessionGetter, type SessionGetterOptions, type SessionStore } from './session-getter.js';
16
16
  import type { InternalStateData, StateStore } from './state-store.js';
17
17
  import type { AuthorizeOptions, CallbackOptions, ClientMetadata } from './types.js';
18
- export type { AuthorizationServerMetadataCache, CreateIdentityResolverOptions, DidCache, DpopNonceCache, Fetch, HandleCache, HandleResolver, InternalStateData, OAuthClientMetadata, OAuthClientMetadataInput, OAuthResponseMode, ProtectedResourceMetadataCache, RuntimeImplementation, SessionHooks, SessionStore, StateStore, };
18
+ export type { AuthorizationServerMetadataCache, CreateIdentityResolverOptions, DidCache, DpopNonceCache, Fetch, HandleCache, HandleResolver, InternalStateData, OAuthClientMetadata, OAuthClientMetadataInput, OAuthResponseMode, ProtectedResourceMetadataCache, RuntimeImplementation, SessionGetterOptions, SessionStore, StateStore, };
19
19
  export { Key, Keyset };
20
20
  export type OAuthClientOptions = {
21
21
  responseMode: OAuthResponseMode;
@@ -43,7 +43,7 @@ export type OAuthClientOptions = {
43
43
  dpopNonceCache?: DpopNonceCache;
44
44
  runtimeImplementation: RuntimeImplementation;
45
45
  fetch?: Fetch;
46
- } & CreateIdentityResolverOptions & SessionHooks;
46
+ } & CreateIdentityResolverOptions & SessionGetterOptions;
47
47
  export type OAuthClientFetchMetadataOptions = {
48
48
  clientId: OAuthClientIdDiscoverable;
49
49
  fetch?: Fetch;
@@ -1 +1 @@
1
- {"version":3,"file":"oauth-client.d.ts","sourceRoot":"","sources":["../src/oauth-client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"oauth-client.d.ts","sourceRoot":"","sources":["../src/oauth-client.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"oauth-client.js","sourceRoot":"","sources":["../src/oauth-client.ts"],"names":[],"mappings":"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","sourcesContent":["import { Key, Keyset } from '@atproto/jwk'\nimport {\n type OAuthAuthorizationRequestParameters,\n type OAuthClientIdDiscoverable,\n type OAuthClientMetadata,\n type OAuthClientMetadataInput,\n type OAuthResponseMode,\n oauthClientMetadataSchema,\n} from '@atproto/oauth-types'\nimport {\n type AtprotoDid,\n type DidCache,\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n type DidResolverCommonOptions,\n assertAtprotoDid,\n} from '@atproto-labs/did-resolver'\nimport type { Fetch } from '@atproto-labs/fetch'\nimport type { HandleCache, HandleResolver } from '@atproto-labs/handle-resolver'\nimport { HANDLE_INVALID } from '@atproto-labs/identity-resolver'\nimport { SimpleStoreMemory } from '@atproto-labs/simple-store-memory'\nimport { FALLBACK_ALG } from './constants.js'\nimport { AuthMethodUnsatisfiableError } from './errors/auth-method-unsatisfiable-error.js'\nimport { TokenRevokedError } from './errors/token-revoked-error.js'\nimport {\n type CreateIdentityResolverOptions,\n createIdentityResolver,\n} from './identity-resolver.js'\nimport {\n type AuthorizationServerMetadataCache,\n OAuthAuthorizationServerMetadataResolver,\n} from './oauth-authorization-server-metadata-resolver.js'\nimport { OAuthCallbackError } from './oauth-callback-error.js'\nimport { negotiateClientAuthMethod } from './oauth-client-auth.js'\nimport {\n OAuthProtectedResourceMetadataResolver,\n type ProtectedResourceMetadataCache,\n} from './oauth-protected-resource-metadata-resolver.js'\nimport { OAuthResolver } from './oauth-resolver.js'\nimport type { DpopNonceCache, OAuthServerAgent } from './oauth-server-agent.js'\nimport { OAuthServerFactory } from './oauth-server-factory.js'\nimport { OAuthSession } from './oauth-session.js'\nimport type { RuntimeImplementation } from './runtime-implementation.js'\nimport { Runtime } from './runtime.js'\nimport {\n SessionGetter,\n type SessionHooks,\n type SessionStore,\n isExpectedSessionError,\n} from './session-getter.js'\nimport type { InternalStateData, StateStore } from './state-store.js'\nimport type {\n AuthorizeOptions,\n CallbackOptions,\n ClientMetadata,\n} from './types.js'\nimport { validateClientMetadata } from './validate-client-metadata.js'\n\n// Export all types needed to construct OAuthClientOptions\nexport type {\n AuthorizationServerMetadataCache,\n CreateIdentityResolverOptions,\n DidCache,\n DpopNonceCache,\n Fetch,\n HandleCache,\n HandleResolver,\n InternalStateData,\n OAuthClientMetadata,\n OAuthClientMetadataInput,\n OAuthResponseMode,\n ProtectedResourceMetadataCache,\n RuntimeImplementation,\n SessionHooks,\n SessionStore,\n StateStore,\n}\n\nexport { Key, Keyset }\n\nexport type OAuthClientOptions = {\n // Config\n responseMode: OAuthResponseMode\n clientMetadata: Readonly<OAuthClientMetadataInput>\n keyset?: Keyset | Iterable<Key | undefined | null | false>\n /**\n * Determines if the client will allow communicating with the OAuth Servers\n * (Authorization & Resource), or to retrieve \"did:web\" documents, over\n * unsafe HTTP connections. It is recommended to set this to `true` only for\n * development purposes.\n *\n * @note This does not affect the identity resolution mechanism, which will\n * allow HTTP connections to the PLC Directory (if the provided directory url\n * is \"http:\" based).\n * @default false\n * @see {@link OAuthProtectedResourceMetadataResolver.allowHttpResource}\n * @see {@link OAuthAuthorizationServerMetadataResolver.allowHttpIssuer}\n * @see {@link DidResolverCommonOptions.allowHttp}\n */\n allowHttp?: boolean\n\n // Stores\n stateStore: StateStore\n sessionStore: SessionStore\n authorizationServerMetadataCache?: AuthorizationServerMetadataCache\n protectedResourceMetadataCache?: ProtectedResourceMetadataCache\n dpopNonceCache?: DpopNonceCache\n\n // Services\n runtimeImplementation: RuntimeImplementation\n fetch?: Fetch\n} & CreateIdentityResolverOptions &\n SessionHooks\n\nexport type OAuthClientFetchMetadataOptions = {\n clientId: OAuthClientIdDiscoverable\n fetch?: Fetch\n signal?: AbortSignal\n}\n\nexport class OAuthClient {\n static async fetchMetadata({\n clientId,\n fetch = globalThis.fetch,\n signal,\n }: OAuthClientFetchMetadataOptions) {\n signal?.throwIfAborted()\n\n const request = new Request(clientId, {\n redirect: 'error',\n signal: signal,\n })\n const response = await fetch(request)\n\n if (response.status !== 200) {\n response.body?.cancel?.()\n throw new TypeError(`Failed to fetch client metadata: ${response.status}`)\n }\n\n // https://www.ietf.org/archive/id/draft-ietf-oauth-client-id-metadata-document-00.html#section-4.1\n const mime = response.headers.get('content-type')?.split(';')[0].trim()\n if (mime !== 'application/json') {\n response.body?.cancel?.()\n throw new TypeError(`Invalid client metadata content type: ${mime}`)\n }\n\n const json: unknown = await response.json()\n\n signal?.throwIfAborted()\n\n return oauthClientMetadataSchema.parse(json)\n }\n\n // Config\n readonly clientMetadata: ClientMetadata\n readonly responseMode: OAuthResponseMode\n readonly keyset?: Keyset\n\n // Services\n readonly runtime: Runtime\n readonly fetch: Fetch\n readonly oauthResolver: OAuthResolver\n readonly serverFactory: OAuthServerFactory\n\n // Stores\n protected readonly sessionGetter: SessionGetter\n protected readonly stateStore: StateStore\n\n constructor(options: OAuthClientOptions) {\n const {\n stateStore,\n sessionStore,\n\n dpopNonceCache = new SimpleStoreMemory({ ttl: 60e3, max: 100 }),\n authorizationServerMetadataCache = new SimpleStoreMemory({\n ttl: 60e3,\n max: 100,\n }),\n protectedResourceMetadataCache = new SimpleStoreMemory({\n ttl: 60e3,\n max: 100,\n }),\n\n responseMode,\n clientMetadata,\n runtimeImplementation,\n keyset,\n } = options\n\n this.keyset = keyset\n ? keyset instanceof Keyset\n ? keyset\n : new Keyset(keyset)\n : undefined\n this.clientMetadata = validateClientMetadata(clientMetadata, this.keyset)\n this.responseMode = responseMode\n\n this.runtime = new Runtime(runtimeImplementation)\n this.fetch = options.fetch ?? globalThis.fetch\n this.oauthResolver = new OAuthResolver(\n createIdentityResolver(options),\n new OAuthProtectedResourceMetadataResolver(\n protectedResourceMetadataCache,\n this.fetch,\n { allowHttpResource: options.allowHttp },\n ),\n new OAuthAuthorizationServerMetadataResolver(\n authorizationServerMetadataCache,\n this.fetch,\n { allowHttpIssuer: options.allowHttp },\n ),\n )\n this.serverFactory = new OAuthServerFactory(\n this.clientMetadata,\n this.runtime,\n this.oauthResolver,\n this.fetch,\n this.keyset,\n dpopNonceCache,\n )\n\n this.stateStore = stateStore\n this.sessionGetter = new SessionGetter(\n sessionStore,\n this.serverFactory,\n this.runtime,\n options,\n )\n }\n\n // Exposed as public API for convenience\n get identityResolver() {\n return this.oauthResolver.identityResolver\n }\n\n get jwks() {\n return this.keyset?.publicJwks ?? ({ keys: [] as const } as const)\n }\n\n async authorize(\n input: string,\n { signal, ...options }: AuthorizeOptions = {},\n ): Promise<URL> {\n const redirectUri =\n options?.redirect_uri ?? this.clientMetadata.redirect_uris[0]\n if (!this.clientMetadata.redirect_uris.includes(redirectUri)) {\n // The server will enforce this, but let's catch it early\n throw new TypeError('Invalid redirect_uri')\n }\n\n const { identityInfo, metadata } = await this.oauthResolver.resolve(input, {\n signal,\n })\n\n const pkce = await this.runtime.generatePKCE()\n const dpopKey = await this.runtime.generateKey(\n metadata.dpop_signing_alg_values_supported || [FALLBACK_ALG],\n )\n\n const authMethod = negotiateClientAuthMethod(\n metadata,\n this.clientMetadata,\n this.keyset,\n )\n const state = await this.runtime.generateNonce()\n\n await this.stateStore.set(state, {\n iss: metadata.issuer,\n dpopKey,\n authMethod,\n verifier: pkce.verifier,\n appState: options?.state,\n })\n\n const parameters: OAuthAuthorizationRequestParameters = {\n ...options,\n\n client_id: this.clientMetadata.client_id,\n redirect_uri: redirectUri,\n code_challenge: pkce.challenge,\n code_challenge_method: pkce.method,\n state,\n login_hint: identityInfo\n ? identityInfo.handle !== HANDLE_INVALID\n ? identityInfo.handle\n : identityInfo.did\n : undefined,\n response_mode: this.responseMode,\n response_type: 'code' as const,\n scope: options?.scope ?? this.clientMetadata.scope,\n }\n\n const authorizationUrl = new URL(metadata.authorization_endpoint)\n\n // Since the user will be redirected to the authorization_endpoint url using\n // a browser, we need to make sure that the url is valid.\n if (\n authorizationUrl.protocol !== 'https:' &&\n authorizationUrl.protocol !== 'http:'\n ) {\n throw new TypeError(\n `Invalid authorization endpoint protocol: ${authorizationUrl.protocol}`,\n )\n }\n\n if (metadata.pushed_authorization_request_endpoint) {\n const server = await this.serverFactory.fromMetadata(\n metadata,\n authMethod,\n dpopKey,\n )\n const parResponse = await server.request(\n 'pushed_authorization_request',\n parameters,\n )\n\n authorizationUrl.searchParams.set(\n 'client_id',\n this.clientMetadata.client_id,\n )\n authorizationUrl.searchParams.set('request_uri', parResponse.request_uri)\n return authorizationUrl\n } else if (metadata.require_pushed_authorization_requests) {\n throw new Error(\n 'Server requires pushed authorization requests (PAR) but no PAR endpoint is available',\n )\n } else {\n for (const [key, value] of Object.entries(parameters)) {\n if (value) authorizationUrl.searchParams.set(key, String(value))\n }\n\n // Length of the URL that will be sent to the server\n const urlLength =\n authorizationUrl.pathname.length + authorizationUrl.search.length\n if (urlLength < 2048) {\n return authorizationUrl\n } else if (!metadata.pushed_authorization_request_endpoint) {\n throw new Error('Login URL too long')\n }\n }\n\n throw new Error(\n 'Server does not support pushed authorization requests (PAR)',\n )\n }\n\n /**\n * This method allows the client to proactively revoke the request_uri it\n * created through PAR.\n */\n async abortRequest(authorizeUrl: URL) {\n const requestUri = authorizeUrl.searchParams.get('request_uri')\n if (!requestUri) return\n\n // @NOTE This is not implemented here because, 1) the request server should\n // invalidate the request_uri after some delay anyways, and 2) I am not sure\n // that the revocation endpoint is even supposed to support this (and I\n // don't want to spend the time checking now).\n\n // @TODO investigate actual necessity & feasibility of this feature\n }\n\n async callback(\n params: URLSearchParams,\n options: CallbackOptions = {},\n ): Promise<{\n session: OAuthSession\n state: string | null\n }> {\n const responseJwt = params.get('response')\n if (responseJwt != null) {\n // https://openid.net/specs/oauth-v2-jarm.html\n throw new OAuthCallbackError(params, 'JARM not supported')\n }\n\n const issuerParam = params.get('iss')\n const stateParam = params.get('state')\n const errorParam = params.get('error')\n const codeParam = params.get('code')\n\n if (!stateParam) {\n throw new OAuthCallbackError(params, 'Missing \"state\" parameter')\n }\n const stateData = await this.stateStore.get(stateParam)\n if (stateData) {\n // Prevent any kind of replay\n await this.stateStore.del(stateParam)\n } else {\n throw new OAuthCallbackError(\n params,\n `Unknown authorization session \"${stateParam}\"`,\n )\n }\n\n try {\n if (errorParam != null) {\n throw new OAuthCallbackError(params, undefined, stateData.appState)\n }\n\n if (!codeParam) {\n throw new OAuthCallbackError(\n params,\n 'Missing \"code\" query param',\n stateData.appState,\n )\n }\n\n const server = await this.serverFactory.fromIssuer(\n stateData.iss,\n stateData.authMethod,\n stateData.dpopKey,\n )\n\n if (issuerParam != null) {\n if (!server.issuer) {\n throw new OAuthCallbackError(\n params,\n 'Issuer not found in metadata',\n stateData.appState,\n )\n }\n if (server.issuer !== issuerParam) {\n throw new OAuthCallbackError(\n params,\n 'Issuer mismatch',\n stateData.appState,\n )\n }\n } else if (\n server.serverMetadata.authorization_response_iss_parameter_supported\n ) {\n throw new OAuthCallbackError(\n params,\n 'iss missing from the response',\n stateData.appState,\n )\n }\n\n const tokenSet = await server.exchangeCode(\n codeParam,\n stateData.verifier,\n options?.redirect_uri ?? server.clientMetadata.redirect_uris[0],\n )\n\n // We revoke any existing session first to avoid leaving orphaned sessions\n // on the AS.\n try {\n await this.revoke(tokenSet.sub)\n } catch {\n // No existing session, or failed to get it. This is fine.\n }\n\n try {\n await this.sessionGetter.setStored(tokenSet.sub, {\n dpopKey: stateData.dpopKey,\n authMethod: server.authMethod,\n tokenSet,\n })\n\n const session = this.createSession(server, tokenSet.sub)\n\n return { session, state: stateData.appState ?? null }\n } catch (err) {\n await server.revoke(tokenSet.refresh_token || tokenSet.access_token)\n\n throw err\n }\n } catch (err) {\n // Make sure, whatever the underlying error, that the appState is\n // available in the calling code\n throw OAuthCallbackError.from(err, params, stateData.appState)\n }\n }\n\n /**\n * Load a stored session. This will refresh the token only if needed (about to\n * expire) by default.\n *\n * @see {@link SessionGetter.restore}\n */\n async restore(\n sub: string,\n refresh: boolean | 'auto' = 'auto',\n ): Promise<OAuthSession> {\n // sub arg is lightly typed for convenience of library user\n assertAtprotoDid(sub)\n\n const { dpopKey, authMethod, tokenSet } =\n await this.sessionGetter.getSession(sub, refresh)\n\n try {\n const server = await this.serverFactory.fromIssuer(\n tokenSet.iss,\n authMethod,\n dpopKey,\n {\n noCache: refresh === true,\n allowStale: refresh === false,\n },\n )\n\n return this.createSession(server, sub)\n } catch (err) {\n if (err instanceof AuthMethodUnsatisfiableError) {\n await this.sessionGetter.delStored(sub, err)\n }\n\n throw err\n }\n }\n\n async revoke(sub: string) {\n // sub arg is lightly typed for convenience of library user\n assertAtprotoDid(sub)\n\n const res = await this.sessionGetter.getSession(sub, false).catch((err) => {\n if (isExpectedSessionError(err)) return null\n throw err\n })\n\n if (!res) return\n\n const { dpopKey, authMethod, tokenSet } = res\n\n // NOT using `;(await this.restore(sub, false)).signOut()` because we want\n // the tokens to be deleted even if it was not possible to fetch the issuer\n // data.\n try {\n const server = await this.serverFactory.fromIssuer(\n tokenSet.iss,\n authMethod,\n dpopKey,\n )\n await server.revoke(tokenSet.access_token)\n } finally {\n await this.sessionGetter.delStored(sub, new TokenRevokedError(sub))\n }\n }\n\n protected createSession(\n server: OAuthServerAgent,\n sub: AtprotoDid,\n ): OAuthSession {\n return new OAuthSession(server, sub, this.sessionGetter, this.fetch)\n }\n}\n"]}
1
+ {"version":3,"file":"oauth-client.js","sourceRoot":"","sources":["../src/oauth-client.ts"],"names":[],"mappings":"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","sourcesContent":["import { Key, Keyset } from '@atproto/jwk'\nimport {\n type OAuthAuthorizationRequestParameters,\n type OAuthClientIdDiscoverable,\n type OAuthClientMetadata,\n type OAuthClientMetadataInput,\n type OAuthResponseMode,\n oauthClientMetadataSchema,\n} from '@atproto/oauth-types'\nimport {\n type AtprotoDid,\n type DidCache,\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n type DidResolverCommonOptions,\n assertAtprotoDid,\n} from '@atproto-labs/did-resolver'\nimport type { Fetch } from '@atproto-labs/fetch'\nimport type { HandleCache, HandleResolver } from '@atproto-labs/handle-resolver'\nimport { HANDLE_INVALID } from '@atproto-labs/identity-resolver'\nimport { SimpleStoreMemory } from '@atproto-labs/simple-store-memory'\nimport { FALLBACK_ALG } from './constants.js'\nimport { AuthMethodUnsatisfiableError } from './errors/auth-method-unsatisfiable-error.js'\nimport { TokenRevokedError } from './errors/token-revoked-error.js'\nimport {\n type CreateIdentityResolverOptions,\n createIdentityResolver,\n} from './identity-resolver.js'\nimport {\n type AuthorizationServerMetadataCache,\n OAuthAuthorizationServerMetadataResolver,\n} from './oauth-authorization-server-metadata-resolver.js'\nimport { OAuthCallbackError } from './oauth-callback-error.js'\nimport { negotiateClientAuthMethod } from './oauth-client-auth.js'\nimport {\n OAuthProtectedResourceMetadataResolver,\n type ProtectedResourceMetadataCache,\n} from './oauth-protected-resource-metadata-resolver.js'\nimport { OAuthResolver } from './oauth-resolver.js'\nimport type { DpopNonceCache, OAuthServerAgent } from './oauth-server-agent.js'\nimport { OAuthServerFactory } from './oauth-server-factory.js'\nimport { OAuthSession } from './oauth-session.js'\nimport type { RuntimeImplementation } from './runtime-implementation.js'\nimport { Runtime } from './runtime.js'\nimport {\n SessionGetter,\n type SessionGetterOptions,\n type SessionStore,\n isExpectedSessionError,\n} from './session-getter.js'\nimport type { InternalStateData, StateStore } from './state-store.js'\nimport type {\n AuthorizeOptions,\n CallbackOptions,\n ClientMetadata,\n} from './types.js'\nimport { validateClientMetadata } from './validate-client-metadata.js'\n\n// Export all types needed to construct OAuthClientOptions\nexport type {\n AuthorizationServerMetadataCache,\n CreateIdentityResolverOptions,\n DidCache,\n DpopNonceCache,\n Fetch,\n HandleCache,\n HandleResolver,\n InternalStateData,\n OAuthClientMetadata,\n OAuthClientMetadataInput,\n OAuthResponseMode,\n ProtectedResourceMetadataCache,\n RuntimeImplementation,\n SessionGetterOptions,\n SessionStore,\n StateStore,\n}\n\nexport { Key, Keyset }\n\nexport type OAuthClientOptions = {\n // Config\n responseMode: OAuthResponseMode\n clientMetadata: Readonly<OAuthClientMetadataInput>\n keyset?: Keyset | Iterable<Key | undefined | null | false>\n /**\n * Determines if the client will allow communicating with the OAuth Servers\n * (Authorization & Resource), or to retrieve \"did:web\" documents, over\n * unsafe HTTP connections. It is recommended to set this to `true` only for\n * development purposes.\n *\n * @note This does not affect the identity resolution mechanism, which will\n * allow HTTP connections to the PLC Directory (if the provided directory url\n * is \"http:\" based).\n * @default false\n * @see {@link OAuthProtectedResourceMetadataResolver.allowHttpResource}\n * @see {@link OAuthAuthorizationServerMetadataResolver.allowHttpIssuer}\n * @see {@link DidResolverCommonOptions.allowHttp}\n */\n allowHttp?: boolean\n\n // Stores\n stateStore: StateStore\n sessionStore: SessionStore\n authorizationServerMetadataCache?: AuthorizationServerMetadataCache\n protectedResourceMetadataCache?: ProtectedResourceMetadataCache\n dpopNonceCache?: DpopNonceCache\n\n // Services\n runtimeImplementation: RuntimeImplementation\n fetch?: Fetch\n} & CreateIdentityResolverOptions &\n SessionGetterOptions\n\nexport type OAuthClientFetchMetadataOptions = {\n clientId: OAuthClientIdDiscoverable\n fetch?: Fetch\n signal?: AbortSignal\n}\n\nexport class OAuthClient {\n static async fetchMetadata({\n clientId,\n fetch = globalThis.fetch,\n signal,\n }: OAuthClientFetchMetadataOptions) {\n signal?.throwIfAborted()\n\n const request = new Request(clientId, {\n redirect: 'error',\n signal: signal,\n })\n const response = await fetch(request)\n\n if (response.status !== 200) {\n response.body?.cancel?.()\n throw new TypeError(`Failed to fetch client metadata: ${response.status}`)\n }\n\n // https://www.ietf.org/archive/id/draft-ietf-oauth-client-id-metadata-document-00.html#section-4.1\n const mime = response.headers.get('content-type')?.split(';')[0].trim()\n if (mime !== 'application/json') {\n response.body?.cancel?.()\n throw new TypeError(`Invalid client metadata content type: ${mime}`)\n }\n\n const json: unknown = await response.json()\n\n signal?.throwIfAborted()\n\n return oauthClientMetadataSchema.parse(json)\n }\n\n // Config\n readonly clientMetadata: ClientMetadata\n readonly responseMode: OAuthResponseMode\n readonly keyset?: Keyset\n\n // Services\n readonly runtime: Runtime\n readonly fetch: Fetch\n readonly oauthResolver: OAuthResolver\n readonly serverFactory: OAuthServerFactory\n\n // Stores\n protected readonly sessionGetter: SessionGetter\n protected readonly stateStore: StateStore\n\n constructor(options: OAuthClientOptions) {\n const {\n stateStore,\n sessionStore,\n\n dpopNonceCache = new SimpleStoreMemory({ ttl: 60e3, max: 100 }),\n authorizationServerMetadataCache = new SimpleStoreMemory({\n ttl: 60e3,\n max: 100,\n }),\n protectedResourceMetadataCache = new SimpleStoreMemory({\n ttl: 60e3,\n max: 100,\n }),\n\n responseMode,\n clientMetadata,\n runtimeImplementation,\n keyset,\n } = options\n\n this.keyset = keyset\n ? keyset instanceof Keyset\n ? keyset\n : new Keyset(keyset)\n : undefined\n this.clientMetadata = validateClientMetadata(clientMetadata, this.keyset)\n this.responseMode = responseMode\n\n this.runtime = new Runtime(runtimeImplementation)\n this.fetch = options.fetch ?? globalThis.fetch\n this.oauthResolver = new OAuthResolver(\n createIdentityResolver(options),\n new OAuthProtectedResourceMetadataResolver(\n protectedResourceMetadataCache,\n this.fetch,\n { allowHttpResource: options.allowHttp },\n ),\n new OAuthAuthorizationServerMetadataResolver(\n authorizationServerMetadataCache,\n this.fetch,\n { allowHttpIssuer: options.allowHttp },\n ),\n )\n this.serverFactory = new OAuthServerFactory(\n this.clientMetadata,\n this.runtime,\n this.oauthResolver,\n this.fetch,\n this.keyset,\n dpopNonceCache,\n )\n\n this.stateStore = stateStore\n this.sessionGetter = new SessionGetter(\n sessionStore,\n this.serverFactory,\n this.runtime,\n options,\n )\n }\n\n // Exposed as public API for convenience\n get identityResolver() {\n return this.oauthResolver.identityResolver\n }\n\n get jwks() {\n return this.keyset?.publicJwks ?? ({ keys: [] as const } as const)\n }\n\n async authorize(\n input: string,\n { signal, ...options }: AuthorizeOptions = {},\n ): Promise<URL> {\n const redirectUri =\n options?.redirect_uri ?? this.clientMetadata.redirect_uris[0]\n if (!this.clientMetadata.redirect_uris.includes(redirectUri)) {\n // The server will enforce this, but let's catch it early\n throw new TypeError('Invalid redirect_uri')\n }\n\n const { identityInfo, metadata } = await this.oauthResolver.resolve(input, {\n signal,\n })\n\n const pkce = await this.runtime.generatePKCE()\n const dpopKey = await this.runtime.generateKey(\n metadata.dpop_signing_alg_values_supported || [FALLBACK_ALG],\n )\n\n const authMethod = negotiateClientAuthMethod(\n metadata,\n this.clientMetadata,\n this.keyset,\n )\n const state = await this.runtime.generateNonce()\n\n await this.stateStore.set(state, {\n iss: metadata.issuer,\n dpopKey,\n authMethod,\n verifier: pkce.verifier,\n appState: options?.state,\n })\n\n const parameters: OAuthAuthorizationRequestParameters = {\n ...options,\n\n client_id: this.clientMetadata.client_id,\n redirect_uri: redirectUri,\n code_challenge: pkce.challenge,\n code_challenge_method: pkce.method,\n state,\n login_hint: identityInfo\n ? identityInfo.handle !== HANDLE_INVALID\n ? identityInfo.handle\n : identityInfo.did\n : undefined,\n response_mode: this.responseMode,\n response_type: 'code' as const,\n scope: options?.scope ?? this.clientMetadata.scope,\n }\n\n const authorizationUrl = new URL(metadata.authorization_endpoint)\n\n // Since the user will be redirected to the authorization_endpoint url using\n // a browser, we need to make sure that the url is valid.\n if (\n authorizationUrl.protocol !== 'https:' &&\n authorizationUrl.protocol !== 'http:'\n ) {\n throw new TypeError(\n `Invalid authorization endpoint protocol: ${authorizationUrl.protocol}`,\n )\n }\n\n if (metadata.pushed_authorization_request_endpoint) {\n const server = await this.serverFactory.fromMetadata(\n metadata,\n authMethod,\n dpopKey,\n )\n const parResponse = await server.request(\n 'pushed_authorization_request',\n parameters,\n )\n\n authorizationUrl.searchParams.set(\n 'client_id',\n this.clientMetadata.client_id,\n )\n authorizationUrl.searchParams.set('request_uri', parResponse.request_uri)\n return authorizationUrl\n } else if (metadata.require_pushed_authorization_requests) {\n throw new Error(\n 'Server requires pushed authorization requests (PAR) but no PAR endpoint is available',\n )\n } else {\n for (const [key, value] of Object.entries(parameters)) {\n if (value) authorizationUrl.searchParams.set(key, String(value))\n }\n\n // Length of the URL that will be sent to the server\n const urlLength =\n authorizationUrl.pathname.length + authorizationUrl.search.length\n if (urlLength < 2048) {\n return authorizationUrl\n } else if (!metadata.pushed_authorization_request_endpoint) {\n throw new Error('Login URL too long')\n }\n }\n\n throw new Error(\n 'Server does not support pushed authorization requests (PAR)',\n )\n }\n\n /**\n * This method allows the client to proactively revoke the request_uri it\n * created through PAR.\n */\n async abortRequest(authorizeUrl: URL) {\n const requestUri = authorizeUrl.searchParams.get('request_uri')\n if (!requestUri) return\n\n // @NOTE This is not implemented here because, 1) the request server should\n // invalidate the request_uri after some delay anyways, and 2) I am not sure\n // that the revocation endpoint is even supposed to support this (and I\n // don't want to spend the time checking now).\n\n // @TODO investigate actual necessity & feasibility of this feature\n }\n\n async callback(\n params: URLSearchParams,\n options: CallbackOptions = {},\n ): Promise<{\n session: OAuthSession\n state: string | null\n }> {\n const responseJwt = params.get('response')\n if (responseJwt != null) {\n // https://openid.net/specs/oauth-v2-jarm.html\n throw new OAuthCallbackError(params, 'JARM not supported')\n }\n\n const issuerParam = params.get('iss')\n const stateParam = params.get('state')\n const errorParam = params.get('error')\n const codeParam = params.get('code')\n\n if (!stateParam) {\n throw new OAuthCallbackError(params, 'Missing \"state\" parameter')\n }\n const stateData = await this.stateStore.get(stateParam)\n if (stateData) {\n // Prevent any kind of replay\n await this.stateStore.del(stateParam)\n } else {\n throw new OAuthCallbackError(\n params,\n `Unknown authorization session \"${stateParam}\"`,\n )\n }\n\n try {\n if (errorParam != null) {\n throw new OAuthCallbackError(params, undefined, stateData.appState)\n }\n\n if (!codeParam) {\n throw new OAuthCallbackError(\n params,\n 'Missing \"code\" query param',\n stateData.appState,\n )\n }\n\n const server = await this.serverFactory.fromIssuer(\n stateData.iss,\n stateData.authMethod,\n stateData.dpopKey,\n )\n\n if (issuerParam != null) {\n if (!server.issuer) {\n throw new OAuthCallbackError(\n params,\n 'Issuer not found in metadata',\n stateData.appState,\n )\n }\n if (server.issuer !== issuerParam) {\n throw new OAuthCallbackError(\n params,\n 'Issuer mismatch',\n stateData.appState,\n )\n }\n } else if (\n server.serverMetadata.authorization_response_iss_parameter_supported\n ) {\n throw new OAuthCallbackError(\n params,\n 'iss missing from the response',\n stateData.appState,\n )\n }\n\n const tokenSet = await server.exchangeCode(\n codeParam,\n stateData.verifier,\n options?.redirect_uri ?? server.clientMetadata.redirect_uris[0],\n )\n\n // We revoke any existing session first to avoid leaving orphaned sessions\n // on the AS.\n try {\n await this.revoke(tokenSet.sub)\n } catch {\n // No existing session, or failed to get it. This is fine.\n }\n\n try {\n await this.sessionGetter.setStored(tokenSet.sub, {\n dpopKey: stateData.dpopKey,\n authMethod: server.authMethod,\n tokenSet,\n })\n\n const session = this.createSession(server, tokenSet.sub)\n\n return { session, state: stateData.appState ?? null }\n } catch (err) {\n await server.revoke(tokenSet.refresh_token || tokenSet.access_token)\n\n throw err\n }\n } catch (err) {\n // Make sure, whatever the underlying error, that the appState is\n // available in the calling code\n throw OAuthCallbackError.from(err, params, stateData.appState)\n }\n }\n\n /**\n * Load a stored session. This will refresh the token only if needed (about to\n * expire) by default.\n *\n * @see {@link SessionGetter.restore}\n */\n async restore(\n sub: string,\n refresh: boolean | 'auto' = 'auto',\n ): Promise<OAuthSession> {\n // sub arg is lightly typed for convenience of library user\n assertAtprotoDid(sub)\n\n const { dpopKey, authMethod, tokenSet } =\n await this.sessionGetter.getSession(sub, refresh)\n\n try {\n const server = await this.serverFactory.fromIssuer(\n tokenSet.iss,\n authMethod,\n dpopKey,\n {\n noCache: refresh === true,\n allowStale: refresh === false,\n },\n )\n\n return this.createSession(server, sub)\n } catch (err) {\n if (err instanceof AuthMethodUnsatisfiableError) {\n await this.sessionGetter.delStored(sub, err)\n }\n\n throw err\n }\n }\n\n async revoke(sub: string) {\n // sub arg is lightly typed for convenience of library user\n assertAtprotoDid(sub)\n\n const res = await this.sessionGetter.getSession(sub, false).catch((err) => {\n if (isExpectedSessionError(err)) return null\n throw err\n })\n\n if (!res) return\n\n const { dpopKey, authMethod, tokenSet } = res\n\n // NOT using `;(await this.restore(sub, false)).signOut()` because we want\n // the tokens to be deleted even if it was not possible to fetch the issuer\n // data.\n try {\n const server = await this.serverFactory.fromIssuer(\n tokenSet.iss,\n authMethod,\n dpopKey,\n )\n await server.revoke(tokenSet.access_token)\n } finally {\n await this.sessionGetter.delStored(sub, new TokenRevokedError(sub))\n }\n }\n\n protected createSession(\n server: OAuthServerAgent,\n sub: AtprotoDid,\n ): OAuthSession {\n return new OAuthSession(server, sub, this.sessionGetter, this.fetch)\n }\n}\n"]}
@@ -1,10 +1,16 @@
1
1
  import { type OAuthProtectedResourceMetadata } from '@atproto/oauth-types';
2
2
  import { type Fetch } from '@atproto-labs/fetch';
3
- import { CachedGetter, type GetCachedOptions, type SimpleStore } from '@atproto-labs/simple-store';
3
+ import { CachedGetter, type GetCachedOptions, type SimpleStore, type StoreErrorHandler } from '@atproto-labs/simple-store';
4
4
  export type { GetCachedOptions, OAuthProtectedResourceMetadata };
5
5
  export type ProtectedResourceMetadataCache = SimpleStore<string, OAuthProtectedResourceMetadata | null>;
6
6
  export type OAuthProtectedResourceMetadataResolverConfig = {
7
7
  allowHttpResource?: boolean;
8
+ /**
9
+ * Called when the metadata cache throws. The cache is best-effort: failures
10
+ * are logged (by default) and degrade to a cache miss + refetch, rather than
11
+ * being propagated.
12
+ */
13
+ onCacheError?: StoreErrorHandler<string>;
8
14
  };
9
15
  /**
10
16
  * @see {@link https://www.rfc-editor.org/rfc/rfc9728.html}
@@ -1 +1 @@
1
- {"version":3,"file":"oauth-protected-resource-metadata-resolver.d.ts","sourceRoot":"","sources":["../src/oauth-protected-resource-metadata-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,8BAA8B,EAEpC,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EACL,KAAK,KAAK,EAIX,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACL,YAAY,EACZ,KAAK,gBAAgB,EACrB,KAAK,WAAW,EACjB,MAAM,4BAA4B,CAAA;AAGnC,YAAY,EAAE,gBAAgB,EAAE,8BAA8B,EAAE,CAAA;AAEhE,MAAM,MAAM,8BAA8B,GAAG,WAAW,CACtD,MAAM,EACN,8BAA8B,GAAG,IAAI,CACtC,CAAA;AAED,MAAM,MAAM,4CAA4C,GAAG;IACzD,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAC5B,CAAA;AAED;;GAEG;AACH,qBAAa,sCAAuC,SAAQ,YAAY,CACtE,MAAM,EACN,8BAA8B,GAAG,IAAI,CACtC;IACC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgB;IACtC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAE3C,YACE,KAAK,EAAE,8BAA8B,EACrC,KAAK,GAAE,KAAwB,EAC/B,MAAM,CAAC,EAAE,4CAA4C,EAMtD;IAEK,GAAG,CACP,QAAQ,EAAE,MAAM,GAAG,GAAG,EACtB,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,8BAA8B,GAAG,IAAI,CAAC,CAgBhD;YAEa,aAAa;CAmD5B"}
1
+ {"version":3,"file":"oauth-protected-resource-metadata-resolver.d.ts","sourceRoot":"","sources":["../src/oauth-protected-resource-metadata-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,8BAA8B,EAEpC,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EACL,KAAK,KAAK,EAIX,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACL,YAAY,EACZ,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAChB,KAAK,iBAAiB,EAEvB,MAAM,4BAA4B,CAAA;AAGnC,YAAY,EAAE,gBAAgB,EAAE,8BAA8B,EAAE,CAAA;AAEhE,MAAM,MAAM,8BAA8B,GAAG,WAAW,CACtD,MAAM,EACN,8BAA8B,GAAG,IAAI,CACtC,CAAA;AAED,MAAM,MAAM,4CAA4C,GAAG;IACzD,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAE3B;;;;OAIG;IACH,YAAY,CAAC,EAAE,iBAAiB,CAAC,MAAM,CAAC,CAAA;CACzC,CAAA;AAED;;GAEG;AACH,qBAAa,sCAAuC,SAAQ,YAAY,CACtE,MAAM,EACN,8BAA8B,GAAG,IAAI,CACtC;IACC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgB;IACtC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAE3C,YACE,KAAK,EAAE,8BAA8B,EACrC,KAAK,GAAE,KAAwB,EAC/B,MAAM,CAAC,EAAE,4CAA4C,EAStD;IAEK,GAAG,CACP,QAAQ,EAAE,MAAM,GAAG,GAAG,EACtB,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,8BAA8B,GAAG,IAAI,CAAC,CAgBhD;YAEa,aAAa;CAmD5B"}
@@ -1,13 +1,13 @@
1
1
  import { oauthProtectedResourceMetadataSchema, } from '@atproto/oauth-types';
2
2
  import { FetchResponseError, bindFetch, cancelBody, } from '@atproto-labs/fetch';
3
- import { CachedGetter, } from '@atproto-labs/simple-store';
3
+ import { CachedGetter, swallowStoreErrors, } from '@atproto-labs/simple-store';
4
4
  import { contentMime } from './util.js';
5
5
  /**
6
6
  * @see {@link https://www.rfc-editor.org/rfc/rfc9728.html}
7
7
  */
8
8
  export class OAuthProtectedResourceMetadataResolver extends CachedGetter {
9
9
  constructor(cache, fetch = globalThis.fetch, config) {
10
- super(async (origin, options) => this.fetchMetadata(origin, options), cache);
10
+ super(async (origin, options) => this.fetchMetadata(origin, options), swallowStoreErrors(cache, config?.onCacheError));
11
11
  this.fetch = bindFetch(fetch);
12
12
  this.allowHttpResource = config?.allowHttpResource === true;
13
13
  }
@@ -1 +1 @@
1
- {"version":3,"file":"oauth-protected-resource-metadata-resolver.js","sourceRoot":"","sources":["../src/oauth-protected-resource-metadata-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,oCAAoC,GACrC,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAEL,kBAAkB,EAClB,SAAS,EACT,UAAU,GACX,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACL,YAAY,GAGb,MAAM,4BAA4B,CAAA;AACnC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAavC;;GAEG;AACH,MAAM,OAAO,sCAAuC,SAAQ,YAG3D;IAIC,YACE,KAAqC,EACrC,KAAK,GAAU,UAAU,CAAC,KAAK,EAC/B,MAAqD;QAErD,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,CAAC,CAAA;QAE5E,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,CAAA;QAC7B,IAAI,CAAC,iBAAiB,GAAG,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAA;IAC7D,CAAC;IAED,KAAK,CAAC,GAAG,CACP,QAAsB,EACtB,OAA0B;QAE1B,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAA;QAE9C,IAAI,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;YAClD,MAAM,IAAI,SAAS,CACjB,qDAAqD,QAAQ,EAAE,CAChE,CAAA;QACH,CAAC;QAED,IAAI,QAAQ,KAAK,OAAO,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACpD,MAAM,IAAI,SAAS,CACjB,mCAAmC,QAAQ,qDAAqD,CACjG,CAAA;QACH,CAAC;QAED,OAAO,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACnC,CAAC;IAEO,KAAK,CAAC,aAAa,CACzB,MAAc,EACd,OAA0B;QAE1B,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,uCAAuC,EAAE,MAAM,CAAC,CAAA;QACpE,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE;YAC/B,MAAM,EAAE,OAAO,EAAE,MAAM;YACvB,OAAO,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE;YACvC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS;YAChD,QAAQ,EAAE,QAAQ,EAAE,0BAA0B;SAC/C,CAAC,CAAA;QAEF,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;QAE1C,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC5B,MAAM,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;YACjC,OAAO,IAAI,CAAA;QACb,CAAC;QAED,0DAA0D;QAC1D,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC5B,MAAM,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;YACjC,MAAM,MAAM,kBAAkB,CAAC,IAAI,CACjC,QAAQ,EACR,0BAA0B,QAAQ,CAAC,MAAM,SAAS,GAAG,GAAG,EACxD,SAAS,EACT,EAAE,KAAK,EAAE,OAAO,EAAE,CACnB,CAAA;QACH,CAAC;QAED,IAAI,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,kBAAkB,EAAE,CAAC;YACzD,MAAM,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;YACjC,MAAM,MAAM,kBAAkB,CAAC,IAAI,CACjC,QAAQ,EACR,gCAAgC,GAAG,GAAG,EACtC,SAAS,EACT,EAAE,KAAK,EAAE,OAAO,EAAE,CACnB,CAAA;QACH,CAAC;QAED,MAAM,QAAQ,GAAG,oCAAoC,CAAC,KAAK,CACzD,MAAM,QAAQ,CAAC,IAAI,EAAE,CACtB,CAAA;QAED,0DAA0D;QAC1D,IAAI,QAAQ,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;YACjC,MAAM,IAAI,SAAS,CAAC,kBAAkB,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAA;QAC5D,CAAC;QAED,OAAO,QAAQ,CAAA;IACjB,CAAC;CACF","sourcesContent":["import {\n type OAuthProtectedResourceMetadata,\n oauthProtectedResourceMetadataSchema,\n} from '@atproto/oauth-types'\nimport {\n type Fetch,\n FetchResponseError,\n bindFetch,\n cancelBody,\n} from '@atproto-labs/fetch'\nimport {\n CachedGetter,\n type GetCachedOptions,\n type SimpleStore,\n} from '@atproto-labs/simple-store'\nimport { contentMime } from './util.js'\n\nexport type { GetCachedOptions, OAuthProtectedResourceMetadata }\n\nexport type ProtectedResourceMetadataCache = SimpleStore<\n string,\n OAuthProtectedResourceMetadata | null\n>\n\nexport type OAuthProtectedResourceMetadataResolverConfig = {\n allowHttpResource?: boolean\n}\n\n/**\n * @see {@link https://www.rfc-editor.org/rfc/rfc9728.html}\n */\nexport class OAuthProtectedResourceMetadataResolver extends CachedGetter<\n string,\n OAuthProtectedResourceMetadata | null\n> {\n private readonly fetch: Fetch<unknown>\n private readonly allowHttpResource: boolean\n\n constructor(\n cache: ProtectedResourceMetadataCache,\n fetch: Fetch = globalThis.fetch,\n config?: OAuthProtectedResourceMetadataResolverConfig,\n ) {\n super(async (origin, options) => this.fetchMetadata(origin, options), cache)\n\n this.fetch = bindFetch(fetch)\n this.allowHttpResource = config?.allowHttpResource === true\n }\n\n async get(\n resource: string | URL,\n options?: GetCachedOptions,\n ): Promise<OAuthProtectedResourceMetadata | null> {\n const { protocol, origin } = new URL(resource)\n\n if (protocol !== 'https:' && protocol !== 'http:') {\n throw new TypeError(\n `Invalid protected resource metadata URL protocol: ${protocol}`,\n )\n }\n\n if (protocol === 'http:' && !this.allowHttpResource) {\n throw new TypeError(\n `Unsecure resource metadata URL (${protocol}) only allowed in development and test environments`,\n )\n }\n\n return super.get(origin, options)\n }\n\n private async fetchMetadata(\n origin: string,\n options?: GetCachedOptions,\n ): Promise<OAuthProtectedResourceMetadata | null> {\n const url = new URL(`/.well-known/oauth-protected-resource`, origin)\n const request = new Request(url, {\n signal: options?.signal,\n headers: { accept: 'application/json' },\n cache: options?.noCache ? 'no-cache' : undefined,\n redirect: 'manual', // response must be 200 OK\n })\n\n const response = await this.fetch(request)\n\n if (response.status === 404) {\n await cancelBody(response, 'log')\n return null\n }\n\n // https://www.rfc-editor.org/rfc/rfc9728.html#section-3.2\n if (response.status !== 200) {\n await cancelBody(response, 'log')\n throw await FetchResponseError.from(\n response,\n `Unexpected status code ${response.status} for \"${url}\"`,\n undefined,\n { cause: request },\n )\n }\n\n if (contentMime(response.headers) !== 'application/json') {\n await cancelBody(response, 'log')\n throw await FetchResponseError.from(\n response,\n `Unexpected content type for \"${url}\"`,\n undefined,\n { cause: request },\n )\n }\n\n const metadata = oauthProtectedResourceMetadataSchema.parse(\n await response.json(),\n )\n\n // https://www.rfc-editor.org/rfc/rfc9728.html#section-3.3\n if (metadata.resource !== origin) {\n throw new TypeError(`Invalid issuer ${metadata.resource}`)\n }\n\n return metadata\n }\n}\n"]}
1
+ {"version":3,"file":"oauth-protected-resource-metadata-resolver.js","sourceRoot":"","sources":["../src/oauth-protected-resource-metadata-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type OAuthProtectedResourceMetadata,\n oauthProtectedResourceMetadataSchema,\n} from '@atproto/oauth-types'\nimport {\n type Fetch,\n FetchResponseError,\n bindFetch,\n cancelBody,\n} from '@atproto-labs/fetch'\nimport {\n CachedGetter,\n type GetCachedOptions,\n type SimpleStore,\n type StoreErrorHandler,\n swallowStoreErrors,\n} from '@atproto-labs/simple-store'\nimport { contentMime } from './util.js'\n\nexport type { GetCachedOptions, OAuthProtectedResourceMetadata }\n\nexport type ProtectedResourceMetadataCache = SimpleStore<\n string,\n OAuthProtectedResourceMetadata | null\n>\n\nexport type OAuthProtectedResourceMetadataResolverConfig = {\n allowHttpResource?: boolean\n\n /**\n * Called when the metadata cache throws. The cache is best-effort: failures\n * are logged (by default) and degrade to a cache miss + refetch, rather than\n * being propagated.\n */\n onCacheError?: StoreErrorHandler<string>\n}\n\n/**\n * @see {@link https://www.rfc-editor.org/rfc/rfc9728.html}\n */\nexport class OAuthProtectedResourceMetadataResolver extends CachedGetter<\n string,\n OAuthProtectedResourceMetadata | null\n> {\n private readonly fetch: Fetch<unknown>\n private readonly allowHttpResource: boolean\n\n constructor(\n cache: ProtectedResourceMetadataCache,\n fetch: Fetch = globalThis.fetch,\n config?: OAuthProtectedResourceMetadataResolverConfig,\n ) {\n super(\n async (origin, options) => this.fetchMetadata(origin, options),\n swallowStoreErrors(cache, config?.onCacheError),\n )\n\n this.fetch = bindFetch(fetch)\n this.allowHttpResource = config?.allowHttpResource === true\n }\n\n async get(\n resource: string | URL,\n options?: GetCachedOptions,\n ): Promise<OAuthProtectedResourceMetadata | null> {\n const { protocol, origin } = new URL(resource)\n\n if (protocol !== 'https:' && protocol !== 'http:') {\n throw new TypeError(\n `Invalid protected resource metadata URL protocol: ${protocol}`,\n )\n }\n\n if (protocol === 'http:' && !this.allowHttpResource) {\n throw new TypeError(\n `Unsecure resource metadata URL (${protocol}) only allowed in development and test environments`,\n )\n }\n\n return super.get(origin, options)\n }\n\n private async fetchMetadata(\n origin: string,\n options?: GetCachedOptions,\n ): Promise<OAuthProtectedResourceMetadata | null> {\n const url = new URL(`/.well-known/oauth-protected-resource`, origin)\n const request = new Request(url, {\n signal: options?.signal,\n headers: { accept: 'application/json' },\n cache: options?.noCache ? 'no-cache' : undefined,\n redirect: 'manual', // response must be 200 OK\n })\n\n const response = await this.fetch(request)\n\n if (response.status === 404) {\n await cancelBody(response, 'log')\n return null\n }\n\n // https://www.rfc-editor.org/rfc/rfc9728.html#section-3.2\n if (response.status !== 200) {\n await cancelBody(response, 'log')\n throw await FetchResponseError.from(\n response,\n `Unexpected status code ${response.status} for \"${url}\"`,\n undefined,\n { cause: request },\n )\n }\n\n if (contentMime(response.headers) !== 'application/json') {\n await cancelBody(response, 'log')\n throw await FetchResponseError.from(\n response,\n `Unexpected content type for \"${url}\"`,\n undefined,\n { cause: request },\n )\n }\n\n const metadata = oauthProtectedResourceMetadataSchema.parse(\n await response.json(),\n )\n\n // https://www.rfc-editor.org/rfc/rfc9728.html#section-3.3\n if (metadata.resource !== origin) {\n throw new TypeError(`Invalid issuer ${metadata.resource}`)\n }\n\n return metadata\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"oauth-response-error.d.ts","sourceRoot":"","sources":["../src/oauth-response-error.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAA;AAG/C,qBAAa,kBAAmB,SAAQ,KAAK;aAKzB,QAAQ,EAAE,QAAQ;aAClB,OAAO,EAAE,IAAI;IAL/B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;IAElC,YACkB,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,IAAI,EAc9B;IAED,IAAI,MAAM,WAET;IAED,IAAI,OAAO,YAEV;CACF"}
1
+ {"version":3,"file":"oauth-response-error.d.ts","sourceRoot":"","sources":["../src/oauth-response-error.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAA;AAG/C,qBAAa,kBAAmB,SAAQ,KAAK;aAKzB,QAAQ,EAAE,QAAQ;aAClB,OAAO,EAAE,IAAI;IAL/B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;IAElC,YACkB,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,IAAI,EAa9B;IAED,IAAI,MAAM,WAET;IAED,IAAI,OAAO,YAEV;CACF"}
@@ -1,9 +1,8 @@
1
1
  import { ifString } from './util.js';
2
2
  export class OAuthResponseError extends Error {
3
3
  constructor(response, payload) {
4
- const objPayload = typeof payload === 'object' ? payload : undefined;
5
- const error = ifString(objPayload?.['error']);
6
- const errorDescription = ifString(objPayload?.['error_description']);
4
+ const error = ifString(payload?.['error']);
5
+ const errorDescription = ifString(payload?.['error_description']);
7
6
  const messageError = error ? `"${error}"` : 'unknown';
8
7
  const messageDesc = errorDescription ? `: ${errorDescription}` : '';
9
8
  const message = `OAuth ${messageError} error${messageDesc}`;
@@ -1 +1 @@
1
- {"version":3,"file":"oauth-response-error.js","sourceRoot":"","sources":["../src/oauth-response-error.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAEpC,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAI3C,YACkB,QAAkB,EAClB,OAAa;QAE7B,MAAM,UAAU,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAA;QACpE,MAAM,KAAK,GAAG,QAAQ,CAAC,UAAU,EAAE,CAAC,OAAO,CAAC,CAAC,CAAA;QAC7C,MAAM,gBAAgB,GAAG,QAAQ,CAAC,UAAU,EAAE,CAAC,mBAAmB,CAAC,CAAC,CAAA;QAEpE,MAAM,YAAY,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,SAAS,CAAA;QACrD,MAAM,WAAW,GAAG,gBAAgB,CAAC,CAAC,CAAC,KAAK,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;QACnE,MAAM,OAAO,GAAG,SAAS,YAAY,SAAS,WAAW,EAAE,CAAA;QAE3D,KAAK,CAAC,OAAO,CAAC,CAAA;wBAXE,QAAQ;uBACR,OAAO;QAYvB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;QAClB,IAAI,CAAC,gBAAgB,GAAG,gBAAgB,CAAA;IAC1C,CAAC;IAED,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAA;IAC7B,CAAC;IAED,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAA;IAC9B,CAAC;CACF","sourcesContent":["import type { Json } from '@atproto-labs/fetch'\nimport { ifString } from './util.js'\n\nexport class OAuthResponseError extends Error {\n readonly error?: string\n readonly errorDescription?: string\n\n constructor(\n public readonly response: Response,\n public readonly payload: Json,\n ) {\n const objPayload = typeof payload === 'object' ? payload : undefined\n const error = ifString(objPayload?.['error'])\n const errorDescription = ifString(objPayload?.['error_description'])\n\n const messageError = error ? `\"${error}\"` : 'unknown'\n const messageDesc = errorDescription ? `: ${errorDescription}` : ''\n const message = `OAuth ${messageError} error${messageDesc}`\n\n super(message)\n\n this.error = error\n this.errorDescription = errorDescription\n }\n\n get status() {\n return this.response.status\n }\n\n get headers() {\n return this.response.headers\n }\n}\n"]}
1
+ {"version":3,"file":"oauth-response-error.js","sourceRoot":"","sources":["../src/oauth-response-error.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAEpC,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAI3C,YACkB,QAAkB,EAClB,OAAa;QAE7B,MAAM,KAAK,GAAG,QAAQ,CAAE,OAAe,EAAE,CAAC,OAAO,CAAC,CAAC,CAAA;QACnD,MAAM,gBAAgB,GAAG,QAAQ,CAAE,OAAe,EAAE,CAAC,mBAAmB,CAAC,CAAC,CAAA;QAE1E,MAAM,YAAY,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,SAAS,CAAA;QACrD,MAAM,WAAW,GAAG,gBAAgB,CAAC,CAAC,CAAC,KAAK,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;QACnE,MAAM,OAAO,GAAG,SAAS,YAAY,SAAS,WAAW,EAAE,CAAA;QAE3D,KAAK,CAAC,OAAO,CAAC,CAAA;wBAVE,QAAQ;uBACR,OAAO;QAWvB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;QAClB,IAAI,CAAC,gBAAgB,GAAG,gBAAgB,CAAA;IAC1C,CAAC;IAED,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAA;IAC7B,CAAC;IAED,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAA;IAC9B,CAAC;CACF","sourcesContent":["import type { Json } from '@atproto-labs/fetch'\nimport { ifString } from './util.js'\n\nexport class OAuthResponseError extends Error {\n readonly error?: string\n readonly errorDescription?: string\n\n constructor(\n public readonly response: Response,\n public readonly payload: Json,\n ) {\n const error = ifString((payload as any)?.['error'])\n const errorDescription = ifString((payload as any)?.['error_description'])\n\n const messageError = error ? `\"${error}\"` : 'unknown'\n const messageDesc = errorDescription ? `: ${errorDescription}` : ''\n const message = `OAuth ${messageError} error${messageDesc}`\n\n super(message)\n\n this.error = error\n this.errorDescription = errorDescription\n }\n\n get status() {\n return this.response.status\n }\n\n get headers() {\n return this.response.headers\n }\n}\n"]}
@@ -7,7 +7,7 @@ import { type ClientAuthMethod, type ClientCredentialsFactory } from './oauth-cl
7
7
  import type { OAuthResolver } from './oauth-resolver.js';
8
8
  import type { Runtime } from './runtime.js';
9
9
  import type { ClientMetadata } from './types.js';
10
- export type { AtprotoOAuthScope, AtprotoOAuthTokenResponse };
10
+ export type { AtprotoDid, AtprotoOAuthScope, AtprotoOAuthTokenResponse };
11
11
  export type TokenSet = {
12
12
  iss: string;
13
13
  sub: AtprotoDid;
@@ -1 +1 @@
1
- {"version":3,"file":"oauth-server-agent.d.ts","sourceRoot":"","sources":["../src/oauth-server-agent.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"oauth-server-agent.d.ts","sourceRoot":"","sources":["../src/oauth-server-agent.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"oauth-server-agent.js","sourceRoot":"","sources":["../src/oauth-server-agent.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AtprotoDid } from '@atproto/did'\nimport type { Key, Keyset } from '@atproto/jwk'\nimport {\n type AtprotoOAuthScope,\n type AtprotoOAuthTokenResponse,\n type OAuthAuthorizationRequestPar,\n type OAuthAuthorizationServerMetadata,\n type OAuthEndpointName,\n type OAuthParResponse,\n type OAuthRedirectUri,\n type OAuthTokenRequest,\n atprotoOAuthTokenResponseSchema,\n oauthParResponseSchema,\n} from '@atproto/oauth-types'\nimport {\n type Fetch,\n type Json,\n bindFetch,\n fetchJsonProcessor,\n} from '@atproto-labs/fetch'\nimport type { SimpleStore } from '@atproto-labs/simple-store'\nimport { TokenRefreshError } from './errors/token-refresh-error.js'\nimport { dpopFetchWrapper } from './fetch-dpop.js'\nimport {\n type ClientAuthMethod,\n type ClientCredentialsFactory,\n createClientCredentialsFactory,\n} from './oauth-client-auth.js'\nimport type { OAuthResolver } from './oauth-resolver.js'\nimport { OAuthResponseError } from './oauth-response-error.js'\nimport type { Runtime } from './runtime.js'\nimport type { ClientMetadata } from './types.js'\nimport { timeoutSignal } from './util.js'\n\nexport type { AtprotoOAuthScope, AtprotoOAuthTokenResponse }\n\nexport type TokenSet = {\n iss: string\n sub: AtprotoDid\n aud: string\n scope: AtprotoOAuthScope\n\n refresh_token?: string\n access_token: string\n token_type: 'DPoP'\n /** ISO Date */\n expires_at?: string\n}\n\nexport type DpopNonceCache = SimpleStore<string, string>\n\nexport class OAuthServerAgent {\n protected dpopFetch: Fetch<unknown>\n protected clientCredentialsFactory: ClientCredentialsFactory\n\n /**\n * @throws see {@link createClientCredentialsFactory}\n */\n constructor(\n readonly authMethod: ClientAuthMethod,\n readonly dpopKey: Key,\n readonly serverMetadata: OAuthAuthorizationServerMetadata,\n readonly clientMetadata: ClientMetadata,\n readonly dpopNonces: DpopNonceCache,\n readonly oauthResolver: OAuthResolver,\n readonly runtime: Runtime,\n readonly keyset?: Keyset,\n fetch?: Fetch,\n ) {\n this.clientCredentialsFactory = createClientCredentialsFactory(\n authMethod,\n serverMetadata,\n clientMetadata,\n runtime,\n keyset,\n )\n\n this.dpopFetch = dpopFetchWrapper<void>({\n fetch: bindFetch(fetch),\n key: dpopKey,\n supportedAlgs: serverMetadata.dpop_signing_alg_values_supported,\n sha256: async (v) => runtime.sha256(v),\n nonces: dpopNonces,\n isAuthServer: true,\n })\n }\n\n get issuer() {\n return this.serverMetadata.issuer\n }\n\n async revoke(token: string) {\n try {\n await this.request('revocation', { token })\n } catch {\n // Don't care\n }\n }\n\n async exchangeCode(\n code: string,\n codeVerifier?: string,\n redirectUri?: OAuthRedirectUri,\n ): Promise<TokenSet> {\n const now = Date.now()\n\n const tokenResponse = await this.request('token', {\n grant_type: 'authorization_code',\n // redirectUri should always be passed by the calling code, but if it is\n // not, default to the first redirect_uri registered for the client:\n redirect_uri: redirectUri ?? this.clientMetadata.redirect_uris[0],\n code,\n code_verifier: codeVerifier,\n })\n\n try {\n // /!\\ IMPORTANT /!\\\n //\n // The tokenResponse MUST always be valid before the \"sub\" it contains\n // can be trusted (see Atproto's OAuth spec for details).\n const aud = await this.verifyIssuer(tokenResponse.sub)\n\n return {\n aud,\n sub: tokenResponse.sub,\n iss: this.issuer,\n\n scope: tokenResponse.scope,\n refresh_token: tokenResponse.refresh_token,\n access_token: tokenResponse.access_token,\n token_type: tokenResponse.token_type,\n\n expires_at:\n typeof tokenResponse.expires_in === 'number'\n ? new Date(now + tokenResponse.expires_in * 1000).toISOString()\n : undefined,\n }\n } catch (err) {\n await this.revoke(tokenResponse.access_token)\n\n throw err\n }\n }\n\n async refresh(tokenSet: TokenSet): Promise<TokenSet> {\n if (!tokenSet.refresh_token) {\n throw new TokenRefreshError(tokenSet.sub, 'No refresh token available')\n }\n\n // /!\\ IMPORTANT /!\\\n //\n // The \"sub\" MUST be a DID, whose issuer authority is indeed the server we\n // are trying to obtain credentials from. Note that we are doing this\n // *before* we actually try to refresh the token:\n // 1) To avoid unnecessary refresh\n // 2) So that the refresh is the last async operation, ensuring as few\n // async operations happen before the result gets a chance to be stored.\n const aud = await this.verifyIssuer(tokenSet.sub)\n\n const now = Date.now()\n\n const tokenResponse = await this.request('token', {\n grant_type: 'refresh_token',\n refresh_token: tokenSet.refresh_token,\n })\n\n return {\n aud,\n sub: tokenSet.sub,\n iss: this.issuer,\n\n scope: tokenResponse.scope,\n refresh_token: tokenResponse.refresh_token,\n access_token: tokenResponse.access_token,\n token_type: tokenResponse.token_type,\n\n expires_at:\n typeof tokenResponse.expires_in === 'number'\n ? new Date(now + tokenResponse.expires_in * 1000).toISOString()\n : undefined,\n }\n }\n\n /**\n * VERY IMPORTANT ! Always call this to process token responses.\n *\n * Whenever an OAuth token response is received, we **MUST** verify that the\n * \"sub\" is a DID, whose issuer authority is indeed the server we just\n * obtained credentials from. This check is a critical step to actually be\n * able to use the \"sub\" (DID) as being the actual user's identifier.\n *\n * @returns The user's PDS URL (the resource server for the user)\n */\n protected async verifyIssuer(sub: AtprotoDid): Promise<string> {\n const resolved = await this.oauthResolver.resolveFromIdentity(sub, {\n noCache: true,\n allowStale: false,\n signal: timeoutSignal(10e3),\n })\n\n if (this.issuer !== resolved.metadata.issuer) {\n // Best case scenario; the user switched PDS. Worst case scenario; a bad\n // actor is trying to impersonate a user. In any case, we must not allow\n // this token to be used.\n throw new TypeError('Issuer mismatch')\n }\n\n return resolved.pds.href\n }\n\n async request<Endpoint extends OAuthEndpointName>(\n endpoint: Endpoint,\n payload: Endpoint extends 'token'\n ? OAuthTokenRequest\n : Endpoint extends 'pushed_authorization_request'\n ? OAuthAuthorizationRequestPar\n : Record<string, unknown>,\n ): Promise<\n Endpoint extends 'token'\n ? AtprotoOAuthTokenResponse\n : Endpoint extends 'pushed_authorization_request'\n ? OAuthParResponse\n : Json\n >\n async request(\n endpoint: OAuthEndpointName,\n payload: Record<string, unknown>,\n ): Promise<unknown> {\n const url = this.serverMetadata[`${endpoint}_endpoint`]\n if (!url) throw new Error(`No ${endpoint} endpoint available`)\n\n const auth = await this.clientCredentialsFactory()\n\n // https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13#section-3.2.2\n // https://datatracker.ietf.org/doc/html/rfc7009#section-2.1\n // https://datatracker.ietf.org/doc/html/rfc7662#section-2.1\n // https://datatracker.ietf.org/doc/html/rfc9126#section-2\n const { response, json } = await this.dpopFetch(url, {\n method: 'POST',\n headers: {\n ...auth.headers,\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: wwwFormUrlEncode({ ...payload, ...auth.payload }),\n }).then(fetchJsonProcessor())\n\n if (response.ok) {\n switch (endpoint) {\n case 'token':\n return atprotoOAuthTokenResponseSchema.parse(json)\n case 'pushed_authorization_request':\n return oauthParResponseSchema.parse(json)\n default:\n return json\n }\n } else {\n throw new OAuthResponseError(response, json)\n }\n }\n}\n\nfunction wwwFormUrlEncode(payload: Record<string, undefined | unknown>) {\n return new URLSearchParams(\n Object.entries(payload)\n .filter(entryHasDefinedValue)\n .map(stringifyEntryValue),\n ).toString()\n}\n\nfunction entryHasDefinedValue(\n entry: [string, unknown],\n): entry is [string, null | NonNullable<unknown>] {\n return entry[1] !== undefined\n}\n\nfunction stringifyEntryValue(entry: [string, unknown]): [string, string] {\n const name = entry[0]\n const value = entry[1]\n\n switch (typeof value) {\n case 'string':\n return [name, value]\n case 'number':\n case 'boolean':\n return [name, String(value)]\n default: {\n const enc = JSON.stringify(value)\n if (enc === undefined) {\n throw new Error(`Unsupported value type for ${name}: ${String(value)}`)\n }\n return [name, enc]\n }\n }\n}\n"]}
1
+ {"version":3,"file":"oauth-server-agent.js","sourceRoot":"","sources":["../src/oauth-server-agent.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AtprotoDid } from '@atproto/did'\nimport type { Key, Keyset } from '@atproto/jwk'\nimport {\n type AtprotoOAuthScope,\n type AtprotoOAuthTokenResponse,\n type OAuthAuthorizationRequestPar,\n type OAuthAuthorizationServerMetadata,\n type OAuthEndpointName,\n type OAuthParResponse,\n type OAuthRedirectUri,\n type OAuthTokenRequest,\n atprotoOAuthTokenResponseSchema,\n oauthParResponseSchema,\n} from '@atproto/oauth-types'\nimport {\n type Fetch,\n type Json,\n bindFetch,\n fetchJsonProcessor,\n} from '@atproto-labs/fetch'\nimport type { SimpleStore } from '@atproto-labs/simple-store'\nimport { TokenRefreshError } from './errors/token-refresh-error.js'\nimport { dpopFetchWrapper } from './fetch-dpop.js'\nimport {\n type ClientAuthMethod,\n type ClientCredentialsFactory,\n createClientCredentialsFactory,\n} from './oauth-client-auth.js'\nimport type { OAuthResolver } from './oauth-resolver.js'\nimport { OAuthResponseError } from './oauth-response-error.js'\nimport type { Runtime } from './runtime.js'\nimport type { ClientMetadata } from './types.js'\nimport { timeoutSignal } from './util.js'\n\nexport type { AtprotoDid, AtprotoOAuthScope, AtprotoOAuthTokenResponse }\n\nexport type TokenSet = {\n iss: string\n sub: AtprotoDid\n aud: string\n scope: AtprotoOAuthScope\n\n refresh_token?: string\n access_token: string\n token_type: 'DPoP'\n /** ISO Date */\n expires_at?: string\n}\n\nexport type DpopNonceCache = SimpleStore<string, string>\n\nexport class OAuthServerAgent {\n protected dpopFetch: Fetch<unknown>\n protected clientCredentialsFactory: ClientCredentialsFactory\n\n /**\n * @throws see {@link createClientCredentialsFactory}\n */\n constructor(\n readonly authMethod: ClientAuthMethod,\n readonly dpopKey: Key,\n readonly serverMetadata: OAuthAuthorizationServerMetadata,\n readonly clientMetadata: ClientMetadata,\n readonly dpopNonces: DpopNonceCache,\n readonly oauthResolver: OAuthResolver,\n readonly runtime: Runtime,\n readonly keyset?: Keyset,\n fetch?: Fetch,\n ) {\n this.clientCredentialsFactory = createClientCredentialsFactory(\n authMethod,\n serverMetadata,\n clientMetadata,\n runtime,\n keyset,\n )\n\n this.dpopFetch = dpopFetchWrapper<void>({\n fetch: bindFetch(fetch),\n key: dpopKey,\n supportedAlgs: serverMetadata.dpop_signing_alg_values_supported,\n sha256: async (v) => runtime.sha256(v),\n nonces: dpopNonces,\n isAuthServer: true,\n })\n }\n\n get issuer() {\n return this.serverMetadata.issuer\n }\n\n async revoke(token: string) {\n try {\n await this.request('revocation', { token })\n } catch {\n // Don't care\n }\n }\n\n async exchangeCode(\n code: string,\n codeVerifier?: string,\n redirectUri?: OAuthRedirectUri,\n ): Promise<TokenSet> {\n const now = Date.now()\n\n const tokenResponse = await this.request('token', {\n grant_type: 'authorization_code',\n // redirectUri should always be passed by the calling code, but if it is\n // not, default to the first redirect_uri registered for the client:\n redirect_uri: redirectUri ?? this.clientMetadata.redirect_uris[0],\n code,\n code_verifier: codeVerifier,\n })\n\n try {\n // /!\\ IMPORTANT /!\\\n //\n // The tokenResponse MUST always be valid before the \"sub\" it contains\n // can be trusted (see Atproto's OAuth spec for details).\n const aud = await this.verifyIssuer(tokenResponse.sub)\n\n return {\n aud,\n sub: tokenResponse.sub,\n iss: this.issuer,\n\n scope: tokenResponse.scope,\n refresh_token: tokenResponse.refresh_token,\n access_token: tokenResponse.access_token,\n token_type: tokenResponse.token_type,\n\n expires_at:\n typeof tokenResponse.expires_in === 'number'\n ? new Date(now + tokenResponse.expires_in * 1000).toISOString()\n : undefined,\n }\n } catch (err) {\n await this.revoke(tokenResponse.access_token)\n\n throw err\n }\n }\n\n async refresh(tokenSet: TokenSet): Promise<TokenSet> {\n if (!tokenSet.refresh_token) {\n throw new TokenRefreshError(tokenSet.sub, 'No refresh token available')\n }\n\n // /!\\ IMPORTANT /!\\\n //\n // The \"sub\" MUST be a DID, whose issuer authority is indeed the server we\n // are trying to obtain credentials from. Note that we are doing this\n // *before* we actually try to refresh the token:\n // 1) To avoid unnecessary refresh\n // 2) So that the refresh is the last async operation, ensuring as few\n // async operations happen before the result gets a chance to be stored.\n const aud = await this.verifyIssuer(tokenSet.sub)\n\n const now = Date.now()\n\n const tokenResponse = await this.request('token', {\n grant_type: 'refresh_token',\n refresh_token: tokenSet.refresh_token,\n })\n\n return {\n aud,\n sub: tokenSet.sub,\n iss: this.issuer,\n\n scope: tokenResponse.scope,\n refresh_token: tokenResponse.refresh_token,\n access_token: tokenResponse.access_token,\n token_type: tokenResponse.token_type,\n\n expires_at:\n typeof tokenResponse.expires_in === 'number'\n ? new Date(now + tokenResponse.expires_in * 1000).toISOString()\n : undefined,\n }\n }\n\n /**\n * VERY IMPORTANT ! Always call this to process token responses.\n *\n * Whenever an OAuth token response is received, we **MUST** verify that the\n * \"sub\" is a DID, whose issuer authority is indeed the server we just\n * obtained credentials from. This check is a critical step to actually be\n * able to use the \"sub\" (DID) as being the actual user's identifier.\n *\n * @returns The user's PDS URL (the resource server for the user)\n */\n protected async verifyIssuer(sub: AtprotoDid): Promise<string> {\n const resolved = await this.oauthResolver.resolveFromIdentity(sub, {\n noCache: true,\n allowStale: false,\n signal: timeoutSignal(10e3),\n })\n\n if (this.issuer !== resolved.metadata.issuer) {\n // Best case scenario; the user switched PDS. Worst case scenario; a bad\n // actor is trying to impersonate a user. In any case, we must not allow\n // this token to be used.\n throw new TypeError('Issuer mismatch')\n }\n\n return resolved.pds.href\n }\n\n async request<Endpoint extends OAuthEndpointName>(\n endpoint: Endpoint,\n payload: Endpoint extends 'token'\n ? OAuthTokenRequest\n : Endpoint extends 'pushed_authorization_request'\n ? OAuthAuthorizationRequestPar\n : Record<string, unknown>,\n ): Promise<\n Endpoint extends 'token'\n ? AtprotoOAuthTokenResponse\n : Endpoint extends 'pushed_authorization_request'\n ? OAuthParResponse\n : Json\n >\n async request(\n endpoint: OAuthEndpointName,\n payload: Record<string, unknown>,\n ): Promise<unknown> {\n const url = this.serverMetadata[`${endpoint}_endpoint`]\n if (!url) throw new Error(`No ${endpoint} endpoint available`)\n\n const auth = await this.clientCredentialsFactory()\n\n // https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13#section-3.2.2\n // https://datatracker.ietf.org/doc/html/rfc7009#section-2.1\n // https://datatracker.ietf.org/doc/html/rfc7662#section-2.1\n // https://datatracker.ietf.org/doc/html/rfc9126#section-2\n const { response, json } = await this.dpopFetch(url, {\n method: 'POST',\n headers: {\n ...auth.headers,\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: wwwFormUrlEncode({ ...payload, ...auth.payload }),\n }).then(fetchJsonProcessor())\n\n if (response.ok) {\n switch (endpoint) {\n case 'token':\n return atprotoOAuthTokenResponseSchema.parse(json)\n case 'pushed_authorization_request':\n return oauthParResponseSchema.parse(json)\n default:\n return json\n }\n } else {\n throw new OAuthResponseError(response, json)\n }\n }\n}\n\nfunction wwwFormUrlEncode(payload: Record<string, undefined | unknown>) {\n return new URLSearchParams(\n Object.entries(payload)\n .filter(entryHasDefinedValue)\n .map(stringifyEntryValue),\n ).toString()\n}\n\nfunction entryHasDefinedValue(\n entry: [string, unknown],\n): entry is [string, null | NonNullable<unknown>] {\n return entry[1] !== undefined\n}\n\nfunction stringifyEntryValue(entry: [string, unknown]): [string, string] {\n const name = entry[0]\n const value = entry[1]\n\n switch (typeof value) {\n case 'string':\n return [name, value]\n case 'number':\n case 'boolean':\n return [name, String(value)]\n default: {\n const enc = JSON.stringify(value)\n if (enc === undefined) {\n throw new Error(`Unsupported value type for ${name}: ${String(value)}`)\n }\n return [name, enc]\n }\n }\n}\n"]}
package/dist/runtime.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Key } from '@atproto/jwk';
1
+ import type { Jwk, Key } from '@atproto/jwk';
2
2
  import type { RuntimeImplementation, RuntimeLock } from './runtime-implementation.js';
3
3
  export declare class Runtime {
4
4
  protected implementation: RuntimeImplementation;
@@ -13,7 +13,7 @@ export declare class Runtime {
13
13
  challenge: string;
14
14
  method: 'S256';
15
15
  }>;
16
- calculateJwkThumbprint(jwk: any): Promise<string>;
16
+ calculateJwkThumbprint(jwk: Jwk): Promise<string>;
17
17
  /**
18
18
  * @see {@link https://datatracker.ietf.org/doc/html/rfc7636#section-4.1}
19
19
  * @note It is RECOMMENDED that the output of a suitable random number generator
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AAEvC,OAAO,KAAK,EACV,qBAAqB,EACrB,WAAW,EACZ,MAAM,6BAA6B,CAAA;AAEpC,qBAAa,OAAO;IAIN,SAAS,CAAC,cAAc,EAAE,qBAAqB;IAH3D,QAAQ,CAAC,qBAAqB,EAAE,OAAO,CAAA;IACvC,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAA;IAE/B,YAAsB,cAAc,EAAE,qBAAqB,EAQ1D;IAEY,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,CAGrD;IAEY,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAIjD;IAEY,aAAa,CAAC,MAAM,SAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAGvD;IAEY,YAAY,CAAC,UAAU,CAAC,EAAE,MAAM;;;gBAKjC,MAAM;OAEjB;IAEY,sBAAsB,CAAC,GAAG,KAAA,mBAItC;IAED;;;;;;OAMG;IACH,UAAgB,gBAAgB,CAAC,UAAU,SAAK,mBAM/C;CACF"}
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AAE5C,OAAO,KAAK,EACV,qBAAqB,EACrB,WAAW,EACZ,MAAM,6BAA6B,CAAA;AAEpC,qBAAa,OAAO;IAIN,SAAS,CAAC,cAAc,EAAE,qBAAqB;IAH3D,QAAQ,CAAC,qBAAqB,EAAE,OAAO,CAAA;IACvC,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAA;IAE/B,YAAsB,cAAc,EAAE,qBAAqB,EAQ1D;IAEY,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,CAGrD;IAEY,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAIjD;IAEY,aAAa,CAAC,MAAM,SAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAGvD;IAEY,YAAY,CAAC,UAAU,CAAC,EAAE,MAAM;;;gBAKjC,MAAM;OAEjB;IAEY,sBAAsB,CAAC,GAAG,EAAE,GAAG,mBAI3C;IAED;;;;;;OAMG;IACH,UAAgB,gBAAgB,CAAC,UAAU,SAAK,mBAM/C;CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import { base64url } from 'multiformats/bases/base64'\nimport type { Key } from '@atproto/jwk'\nimport { requestLocalLock } from './lock.js'\nimport type {\n RuntimeImplementation,\n RuntimeLock,\n} from './runtime-implementation.js'\n\nexport class Runtime {\n readonly hasImplementationLock: boolean\n readonly usingLock: RuntimeLock\n\n constructor(protected implementation: RuntimeImplementation) {\n const { requestLock } = implementation\n\n this.hasImplementationLock = requestLock != null\n this.usingLock =\n requestLock?.bind(implementation) ||\n // Falling back to a local lock\n requestLocalLock\n }\n\n public async generateKey(algs: string[]): Promise<Key> {\n const algsSorted = Array.from(algs).sort(compareAlgos)\n return this.implementation.createKey(algsSorted)\n }\n\n public async sha256(text: string): Promise<string> {\n const bytes = new TextEncoder().encode(text)\n const digest = await this.implementation.digest(bytes, { name: 'sha256' })\n return base64url.baseEncode(digest)\n }\n\n public async generateNonce(length = 16): Promise<string> {\n const bytes = await this.implementation.getRandomValues(length)\n return base64url.baseEncode(bytes)\n }\n\n public async generatePKCE(byteLength?: number) {\n const verifier = await this.generateVerifier(byteLength)\n return {\n verifier,\n challenge: await this.sha256(verifier),\n method: 'S256' as const,\n }\n }\n\n public async calculateJwkThumbprint(jwk) {\n const components = extractJktComponents(jwk)\n const data = JSON.stringify(components)\n return this.sha256(data)\n }\n\n /**\n * @see {@link https://datatracker.ietf.org/doc/html/rfc7636#section-4.1}\n * @note It is RECOMMENDED that the output of a suitable random number generator\n * be used to create a 32-octet sequence. The octet sequence is then\n * base64url-encoded to produce a 43-octet URL safe string to use as the code\n * verifier.\n */\n protected async generateVerifier(byteLength = 32) {\n if (byteLength < 32 || byteLength > 96) {\n throw new TypeError('Invalid code_verifier length')\n }\n const bytes = await this.implementation.getRandomValues(byteLength)\n return base64url.baseEncode(bytes)\n }\n}\n\nfunction extractJktComponents(jwk) {\n const get = (field) => {\n const value = jwk[field]\n if (typeof value !== 'string' || !value) {\n throw new TypeError(`\"${field}\" Parameter missing or invalid`)\n }\n return value\n }\n\n switch (jwk.kty) {\n case 'EC':\n return { crv: get('crv'), kty: get('kty'), x: get('x'), y: get('y') }\n case 'OKP':\n return { crv: get('crv'), kty: get('kty'), x: get('x') }\n case 'RSA':\n return { e: get('e'), kty: get('kty'), n: get('n') }\n case 'oct':\n return { k: get('k'), kty: get('kty') }\n default:\n throw new TypeError('\"kty\" (Key Type) Parameter missing or unsupported')\n }\n}\n\n/**\n * 256K > ES (256 > 384 > 512) > PS (256 > 384 > 512) > RS (256 > 384 > 512) > other (in original order)\n */\nfunction compareAlgos(a: string, b: string): number {\n if (a === 'ES256K') return -1\n if (b === 'ES256K') return 1\n\n for (const prefix of ['ES', 'PS', 'RS']) {\n if (a.startsWith(prefix)) {\n if (b.startsWith(prefix)) {\n const aLen = parseInt(a.slice(2, 5))\n const bLen = parseInt(b.slice(2, 5))\n\n // Prefer shorter key lengths\n return aLen - bLen\n }\n return -1\n } else if (b.startsWith(prefix)) {\n return 1\n }\n }\n\n // Don't know how to compare, keep original order\n return 0\n}\n"]}
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import { base64url } from 'multiformats/bases/base64'\nimport type { Jwk, Key } from '@atproto/jwk'\nimport { requestLocalLock } from './lock.js'\nimport type {\n RuntimeImplementation,\n RuntimeLock,\n} from './runtime-implementation.js'\n\nexport class Runtime {\n readonly hasImplementationLock: boolean\n readonly usingLock: RuntimeLock\n\n constructor(protected implementation: RuntimeImplementation) {\n const { requestLock } = implementation\n\n this.hasImplementationLock = requestLock != null\n this.usingLock =\n requestLock?.bind(implementation) ||\n // Falling back to a local lock\n requestLocalLock\n }\n\n public async generateKey(algs: string[]): Promise<Key> {\n const algsSorted = Array.from(algs).sort(compareAlgos)\n return this.implementation.createKey(algsSorted)\n }\n\n public async sha256(text: string): Promise<string> {\n const bytes = new TextEncoder().encode(text)\n const digest = await this.implementation.digest(bytes, { name: 'sha256' })\n return base64url.baseEncode(digest)\n }\n\n public async generateNonce(length = 16): Promise<string> {\n const bytes = await this.implementation.getRandomValues(length)\n return base64url.baseEncode(bytes)\n }\n\n public async generatePKCE(byteLength?: number) {\n const verifier = await this.generateVerifier(byteLength)\n return {\n verifier,\n challenge: await this.sha256(verifier),\n method: 'S256' as const,\n }\n }\n\n public async calculateJwkThumbprint(jwk: Jwk) {\n const components = extractJktComponents(jwk)\n const data = JSON.stringify(components)\n return this.sha256(data)\n }\n\n /**\n * @see {@link https://datatracker.ietf.org/doc/html/rfc7636#section-4.1}\n * @note It is RECOMMENDED that the output of a suitable random number generator\n * be used to create a 32-octet sequence. The octet sequence is then\n * base64url-encoded to produce a 43-octet URL safe string to use as the code\n * verifier.\n */\n protected async generateVerifier(byteLength = 32) {\n if (byteLength < 32 || byteLength > 96) {\n throw new TypeError('Invalid code_verifier length')\n }\n const bytes = await this.implementation.getRandomValues(byteLength)\n return base64url.baseEncode(bytes)\n }\n}\n\nfunction extractJktComponents(jwk: Record<string, unknown>) {\n const get = (field: string) => {\n const value = jwk[field]\n if (typeof value !== 'string' || !value) {\n throw new TypeError(`\"${field}\" Parameter missing or invalid`)\n }\n return value\n }\n\n switch (jwk.kty) {\n case 'EC':\n return { crv: get('crv'), kty: get('kty'), x: get('x'), y: get('y') }\n case 'OKP':\n return { crv: get('crv'), kty: get('kty'), x: get('x') }\n case 'RSA':\n return { e: get('e'), kty: get('kty'), n: get('n') }\n case 'oct':\n return { k: get('k'), kty: get('kty') }\n default:\n throw new TypeError('\"kty\" (Key Type) Parameter missing or unsupported')\n }\n}\n\n/**\n * 256K > ES (256 > 384 > 512) > PS (256 > 384 > 512) > RS (256 > 384 > 512) > other (in original order)\n */\nfunction compareAlgos(a: string, b: string): number {\n if (a === 'ES256K') return -1\n if (b === 'ES256K') return 1\n\n for (const prefix of ['ES', 'PS', 'RS']) {\n if (a.startsWith(prefix)) {\n if (b.startsWith(prefix)) {\n const aLen = parseInt(a.slice(2, 5))\n const bLen = parseInt(b.slice(2, 5))\n\n // Prefer shorter key lengths\n return aLen - bLen\n }\n return -1\n } else if (b.startsWith(prefix)) {\n return 1\n }\n }\n\n // Don't know how to compare, keep original order\n return 0\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  import type { AtprotoDid } from '@atproto/did';
2
2
  import type { Key } from '@atproto/jwk';
3
- import { CachedGetter, type GetCachedOptions, type GetOptions, type SimpleStore } from '@atproto-labs/simple-store';
3
+ import { CachedGetter, type GetCachedOptions, type SimpleStore } from '@atproto-labs/simple-store';
4
4
  import { AuthMethodUnsatisfiableError } from './errors/auth-method-unsatisfiable-error.js';
5
5
  import { TokenInvalidError } from './errors/token-invalid-error.js';
6
6
  import { TokenRefreshError } from './errors/token-refresh-error.js';
@@ -15,9 +15,9 @@ export type Session = {
15
15
  tokenSet: TokenSet;
16
16
  };
17
17
  export type SessionStore = SimpleStore<string, Session>;
18
- export type SessionHooks = {
19
- onUpdate?: (sub: AtprotoDid, session: Session) => void;
20
- onDelete?: (sub: AtprotoDid, cause: TokenRefreshError | TokenRevokedError | TokenInvalidError | unknown) => void;
18
+ export type SessionGetterOptions = {
19
+ onSessionUpdated?: (sub: AtprotoDid, session: Session) => void;
20
+ onSessionDeleted?: (sub: AtprotoDid, cause: TokenRefreshError | TokenRevokedError | TokenInvalidError | unknown) => void;
21
21
  };
22
22
  export declare function isExpectedSessionError(err: unknown): err is AuthMethodUnsatisfiableError | TokenInvalidError | TokenRefreshError | TokenRevokedError | TypeError;
23
23
  /**
@@ -28,16 +28,10 @@ export declare function isExpectedSessionError(err: unknown): err is AuthMethodU
28
28
  * localStorage/indexedDB, will sync across multiple tabs (for a given sub).
29
29
  */
30
30
  export declare class SessionGetter extends CachedGetter<AtprotoDid, Session> {
31
- private readonly runtime;
32
- private readonly hooks;
33
- constructor(sessionStore: SessionStore, serverFactory: OAuthServerFactory, runtime: Runtime, hooks?: SessionHooks);
34
- getStored(sub: AtprotoDid, options?: GetOptions): Promise<Session | undefined>;
31
+ #private;
32
+ constructor(sessionStore: SessionStore, serverFactory: OAuthServerFactory, runtime: Runtime, options?: SessionGetterOptions);
35
33
  setStored(sub: AtprotoDid, session: Session): Promise<void>;
36
34
  delStored(sub: AtprotoDid, cause?: unknown): Promise<void>;
37
- /**
38
- * @deprecated Use {@link getSession} instead
39
- * @internal (not really deprecated)
40
- */
41
35
  get(sub: AtprotoDid, options?: GetCachedOptions): Promise<Session>;
42
36
  /**
43
37
  * @param refresh When `true`, the credentials will be refreshed even if they
@@ -1 +1 @@
1
- {"version":3,"file":"session-getter.d.ts","sourceRoot":"","sources":["../src/session-getter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAC9C,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EACL,YAAY,EACZ,KAAK,gBAAgB,EACrB,KAAK,UAAU,EACf,KAAK,WAAW,EACjB,MAAM,4BAA4B,CAAA;AACnC,OAAO,EAAE,4BAA4B,EAAE,MAAM,6CAA6C,CAAA;AAC1F,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAA;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAA;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAA;AACnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAE9D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AACvD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AACnE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAG3C,MAAM,MAAM,OAAO,GAAG;IACpB,OAAO,EAAE,GAAG,CAAA;IACZ,UAAU,EAAE,gBAAgB,CAAA;IAC5B,QAAQ,EAAE,QAAQ,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,YAAY,GAAG,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAEvD,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IACtD,QAAQ,CAAC,EAAE,CACT,GAAG,EAAE,UAAU,EACf,KAAK,EAAE,iBAAiB,GAAG,iBAAiB,GAAG,iBAAiB,GAAG,OAAO,KACvE,IAAI,CAAA;CACV,CAAA;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,+GAUlD;AAED;;;;;;GAMG;AACH,qBAAa,aAAc,SAAQ,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC;IAIhE,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,KAAK;IAJxB,YACE,YAAY,EAAE,YAAY,EAC1B,aAAa,EAAE,kBAAkB,EAChB,OAAO,EAAE,OAAO,EAChB,KAAK,GAAE,YAAiB,EA0J1C;IAEc,SAAS,CACtB,GAAG,EAAE,UAAU,EACf,OAAO,CAAC,EAAE,UAAU,GACnB,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAE9B;IAEc,SAAS,CAAC,GAAG,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,iBAOzD;IAEc,SAAS,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAGxE;IAED;;;OAGG;IACY,GAAG,CAChB,GAAG,EAAE,UAAU,EACf,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,OAAO,CAAC,CAuBlB;IAED;;;;;OAKG;IACG,UAAU,CAAC,GAAG,EAAE,UAAU,EAAE,OAAO,GAAE,OAAO,GAAG,MAAe,oBAKnE;CACF"}
1
+ {"version":3,"file":"session-getter.d.ts","sourceRoot":"","sources":["../src/session-getter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAC9C,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EACL,YAAY,EACZ,KAAK,gBAAgB,EACrB,KAAK,WAAW,EACjB,MAAM,4BAA4B,CAAA;AACnC,OAAO,EAAE,4BAA4B,EAAE,MAAM,6CAA6C,CAAA;AAC1F,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAA;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAA;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAA;AACnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAE9D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AACvD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AACnE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAG3C,MAAM,MAAM,OAAO,GAAG;IACpB,OAAO,EAAE,GAAG,CAAA;IACZ,UAAU,EAAE,gBAAgB,CAAA;IAC5B,QAAQ,EAAE,QAAQ,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,YAAY,GAAG,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAEvD,MAAM,MAAM,oBAAoB,GAAG;IACjC,gBAAgB,CAAC,EAAE,CAAC,GAAG,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IAC9D,gBAAgB,CAAC,EAAE,CACjB,GAAG,EAAE,UAAU,EACf,KAAK,EAAE,iBAAiB,GAAG,iBAAiB,GAAG,iBAAiB,GAAG,OAAO,KACvE,IAAI,CAAA;CACV,CAAA;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,+GAUlD;AAED;;;;;;GAMG;AACH,qBAAa,aAAc,SAAQ,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC;;IAKlE,YACE,YAAY,EAAE,YAAY,EAC1B,aAAa,EAAE,kBAAkB,EACjC,OAAO,EAAE,OAAO,EAChB,OAAO,GAAE,oBAAyB,EAwInC;IAEc,SAAS,CAAC,GAAG,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,iBAqCzD;IAEc,SAAS,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAGxE;IAEc,GAAG,CAChB,GAAG,EAAE,UAAU,EACf,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,OAAO,CAAC,CAuBlB;IAED;;;;;OAKG;IACG,UAAU,CAAC,GAAG,EAAE,UAAU,EAAE,OAAO,GAAE,OAAO,GAAG,MAAe,oBAKnE;CACF"}
@@ -74,7 +74,10 @@ export function isExpectedSessionError(err) {
74
74
  * localStorage/indexedDB, will sync across multiple tabs (for a given sub).
75
75
  */
76
76
  export class SessionGetter extends CachedGetter {
77
- constructor(sessionStore, serverFactory, runtime, hooks = {}) {
77
+ #serverFactory;
78
+ #runtime;
79
+ #options;
80
+ constructor(sessionStore, serverFactory, runtime, options = {}) {
78
81
  super(async (sub, { signal }, storedSession) => {
79
82
  // There needs to be a previous session to be able to refresh. If
80
83
  // storedSession is undefined, it means that the store does not contain
@@ -87,7 +90,7 @@ export class SessionGetter extends CachedGetter {
87
90
  // make sure an event is dispatched here if this occurs.
88
91
  const msg = 'The session was deleted by another process';
89
92
  const cause = new TokenRefreshError(sub, msg);
90
- await hooks.onDelete?.call(null, sub, cause);
93
+ await options.onSessionDeleted?.call(null, sub, cause);
91
94
  throw cause;
92
95
  }
93
96
  // @NOTE Throwing a TokenRefreshError (or any other error class defined
@@ -101,7 +104,7 @@ export class SessionGetter extends CachedGetter {
101
104
  if (!tokenSet.refresh_token) {
102
105
  throw new TokenRefreshError(sub, 'No refresh token available');
103
106
  }
104
- const server = await serverFactory.fromIssuer(tokenSet.iss, authMethod, dpopKey);
107
+ const server = await serverFactory.fromIssuer(tokenSet.iss, authMethod, dpopKey, { signal });
105
108
  // Because refresh tokens can only be used once, we must not use the
106
109
  // "signal" to abort the refresh, or throw any abort error beyond this
107
110
  // point. Any thrown error beyond this point will prevent the
@@ -178,51 +181,49 @@ export class SessionGetter extends CachedGetter {
178
181
  // instances trying to refresh the token at the same.
179
182
  30e3 * Math.random());
180
183
  },
181
- onStoreError: async (err, sub, { tokenSet, dpopKey, authMethod }) => {
182
- // If the token data cannot be stored, let's revoke it
183
- try {
184
- const server = await serverFactory.fromIssuer(tokenSet.iss, authMethod, dpopKey);
185
- await server.revoke(tokenSet.refresh_token ?? tokenSet.access_token);
186
- }
187
- catch {
188
- // At least we tried...
189
- }
190
- // Attempt to delete the session from the store. Note that this might
191
- // fail if the store is not available, which is fine.
192
- try {
193
- await this.delStored(sub, err);
194
- }
195
- catch {
196
- // Ignore (better to propagate the original storage error)
197
- }
198
- throw err;
199
- },
200
184
  deleteOnError: isExpectedSessionError,
201
185
  });
202
- this.runtime = runtime;
203
- this.hooks = hooks;
204
- }
205
- async getStored(sub, options) {
206
- return super.getStored(sub, options);
186
+ this.#serverFactory = serverFactory;
187
+ this.#runtime = runtime;
188
+ this.#options = options;
207
189
  }
208
190
  async setStored(sub, session) {
209
191
  // Prevent tampering with the stored value
210
192
  if (sub !== session.tokenSet.sub) {
211
- throw new TypeError('Token set does not match the expected sub');
193
+ throw new Error('Token set does not match the expected sub');
194
+ }
195
+ try {
196
+ await super.setStored(sub, session);
212
197
  }
213
- await super.setStored(sub, session);
214
- await this.hooks.onUpdate?.call(null, sub, session);
198
+ catch (err) {
199
+ // If we fail to persist the session, we assume that we won't be able to
200
+ // fetch it again. We don't want to leave the session in a "limbo" state,
201
+ // so we proactively revoke the session and delete it from the store.
202
+ const errors = [err];
203
+ try {
204
+ const { tokenSet, dpopKey, authMethod } = session;
205
+ const server = await this.#serverFactory.fromIssuer(tokenSet.iss, authMethod, dpopKey);
206
+ await server.revoke(tokenSet.refresh_token ?? tokenSet.access_token);
207
+ }
208
+ catch (err) {
209
+ errors.push(err);
210
+ }
211
+ try {
212
+ await this.delStored(sub, err);
213
+ }
214
+ catch (err) {
215
+ errors.push(err);
216
+ }
217
+ throw new AggregateError(errors, `Failed to store session for ${sub}`);
218
+ }
219
+ await this.#options.onSessionUpdated?.call(null, sub, session);
215
220
  }
216
221
  async delStored(sub, cause) {
217
222
  await super.delStored(sub, cause);
218
- await this.hooks.onDelete?.call(null, sub, cause);
223
+ await this.#options.onSessionDeleted?.call(null, sub, cause);
219
224
  }
220
- /**
221
- * @deprecated Use {@link getSession} instead
222
- * @internal (not really deprecated)
223
- */
224
225
  async get(sub, options) {
225
- const session = await this.runtime.usingLock(`@atproto-oauth-client-${sub}`, async () => {
226
+ const session = await this.#runtime.usingLock(`@atproto-oauth-client-${sub}`, async () => {
226
227
  const env_1 = { stack: [], error: void 0, hasError: false };
227
228
  try {
228
229
  // Make sure, even if there is no signal in the options, that the
@@ -1 +1 @@
1
- {"version":3,"file":"session-getter.js","sourceRoot":"","sources":["../src/session-getter.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AtprotoDid } from '@atproto/did'\nimport type { Key } from '@atproto/jwk'\nimport {\n CachedGetter,\n type GetCachedOptions,\n type GetOptions,\n type SimpleStore,\n} from '@atproto-labs/simple-store'\nimport { AuthMethodUnsatisfiableError } from './errors/auth-method-unsatisfiable-error.js'\nimport { TokenInvalidError } from './errors/token-invalid-error.js'\nimport { TokenRefreshError } from './errors/token-refresh-error.js'\nimport { TokenRevokedError } from './errors/token-revoked-error.js'\nimport type { ClientAuthMethod } from './oauth-client-auth.js'\nimport { OAuthResponseError } from './oauth-response-error.js'\nimport type { TokenSet } from './oauth-server-agent.js'\nimport type { OAuthServerFactory } from './oauth-server-factory.js'\nimport type { Runtime } from './runtime.js'\nimport { combineSignals, timeoutSignal } from './util.js'\n\nexport type Session = {\n dpopKey: Key\n authMethod: ClientAuthMethod\n tokenSet: TokenSet\n}\n\nexport type SessionStore = SimpleStore<string, Session>\n\nexport type SessionHooks = {\n onUpdate?: (sub: AtprotoDid, session: Session) => void\n onDelete?: (\n sub: AtprotoDid,\n cause: TokenRefreshError | TokenRevokedError | TokenInvalidError | unknown,\n ) => void\n}\n\nexport function isExpectedSessionError(err: unknown) {\n return (\n err instanceof TokenRefreshError ||\n err instanceof TokenRevokedError ||\n err instanceof TokenInvalidError ||\n err instanceof AuthMethodUnsatisfiableError ||\n // The stored session is invalid (e.g. missing properties) and cannot\n // be used properly\n err instanceof TypeError\n )\n}\n\n/**\n * There are several advantages to wrapping the sessionStore in a (single)\n * CachedGetter, the main of which is that the cached getter will ensure that at\n * most one fresh call is ever being made. Another advantage, is that it\n * contains the logic for reading from the cache which, if the cache is based on\n * localStorage/indexedDB, will sync across multiple tabs (for a given sub).\n */\nexport class SessionGetter extends CachedGetter<AtprotoDid, Session> {\n constructor(\n sessionStore: SessionStore,\n serverFactory: OAuthServerFactory,\n private readonly runtime: Runtime,\n private readonly hooks: SessionHooks = {},\n ) {\n super(\n async (sub, { signal }, storedSession) => {\n // There needs to be a previous session to be able to refresh. If\n // storedSession is undefined, it means that the store does not contain\n // a session for the given sub.\n if (storedSession === undefined) {\n // Because the session is not in the store, this.delStored() method\n // will not be called by the CachedGetter class (because there is\n // nothing to delete). This would typically happen if there is no\n // synchronization mechanism between instances of this class. Let's\n // make sure an event is dispatched here if this occurs.\n const msg = 'The session was deleted by another process'\n const cause = new TokenRefreshError(sub, msg)\n await hooks.onDelete?.call(null, sub, cause)\n throw cause\n }\n\n // @NOTE Throwing a TokenRefreshError (or any other error class defined\n // in the deleteOnError options) will result in this.delStored() being\n // called.\n\n const { dpopKey, authMethod, tokenSet } = storedSession\n\n if (sub !== tokenSet.sub) {\n // Fool-proofing (e.g. against invalid session storage)\n throw new TokenRefreshError(sub, 'Stored session sub mismatch')\n }\n\n if (!tokenSet.refresh_token) {\n throw new TokenRefreshError(sub, 'No refresh token available')\n }\n\n const server = await serverFactory.fromIssuer(\n tokenSet.iss,\n authMethod,\n dpopKey,\n )\n\n // Because refresh tokens can only be used once, we must not use the\n // \"signal\" to abort the refresh, or throw any abort error beyond this\n // point. Any thrown error beyond this point will prevent the\n // TokenGetter from obtaining, and storing, the new token set,\n // effectively rendering the currently saved session unusable.\n signal?.throwIfAborted()\n\n try {\n const newTokenSet = await server.refresh(tokenSet)\n\n if (sub !== newTokenSet.sub) {\n // The server returned another sub. Was the tokenSet manipulated?\n throw new TokenRefreshError(sub, 'Token set sub mismatch')\n }\n\n return {\n dpopKey,\n tokenSet: newTokenSet,\n authMethod: server.authMethod,\n }\n } catch (cause) {\n // Since refresh tokens can only be used once, we might run into\n // concurrency issues if multiple instances (e.g. browser tabs) are\n // trying to refresh the same token simultaneously. The chances of\n // this happening when multiple instances are started simultaneously\n // is reduced by randomizing the expiry time (see isStale() below).\n // The best solution is to use a mutex/lock to ensure that only one\n // instance is refreshing the token at a time (runtime.usingLock) but\n // that is not always possible. Let's try to recover from concurrency\n // issues, or force the session to be deleted by throwing a\n // TokenRefreshError.\n if (\n cause instanceof OAuthResponseError &&\n cause.status === 400 &&\n cause.error === 'invalid_grant'\n ) {\n // In case there is no lock implementation in the runtime, we will\n // wait for a short time to give the other concurrent instances a\n // chance to finish their refreshing of the token. If a concurrent\n // refresh did occur, we will pretend that this one succeeded.\n if (!runtime.hasImplementationLock) {\n await new Promise((r) => setTimeout(r, 1000))\n\n const stored = await this.getStored(sub)\n if (stored === undefined) {\n // A concurrent refresh occurred and caused the session to be\n // deleted (for a reason we can't know at this point).\n\n // Using a distinct error message mainly for debugging\n // purposes. Also, throwing a TokenRefreshError to trigger\n // deletion through the deleteOnError callback.\n const msg = 'The session was deleted by another process'\n throw new TokenRefreshError(sub, msg, { cause })\n } else if (\n stored.tokenSet.access_token !== tokenSet.access_token ||\n stored.tokenSet.refresh_token !== tokenSet.refresh_token\n ) {\n // A concurrent refresh occurred. Pretend this one succeeded.\n return stored\n } else {\n // There were no concurrent refresh. The token is (likely)\n // simply no longer valid.\n }\n }\n\n // Make sure the session gets deleted from the store\n const msg = cause.errorDescription ?? 'The session was revoked'\n throw new TokenRefreshError(sub, msg, { cause })\n }\n\n throw cause\n }\n },\n sessionStore,\n {\n isStale: (sub, { tokenSet }) => {\n return (\n tokenSet.expires_at != null &&\n new Date(tokenSet.expires_at).getTime() <\n Date.now() +\n // Add some lee way to ensure the token is not expired when it\n // reaches the server.\n 10e3 +\n // Add some randomness to reduce the chances of multiple\n // instances trying to refresh the token at the same.\n 30e3 * Math.random()\n )\n },\n onStoreError: async (err, sub, { tokenSet, dpopKey, authMethod }) => {\n // If the token data cannot be stored, let's revoke it\n try {\n const server = await serverFactory.fromIssuer(\n tokenSet.iss,\n authMethod,\n dpopKey,\n )\n await server.revoke(tokenSet.refresh_token ?? tokenSet.access_token)\n } catch {\n // At least we tried...\n }\n\n // Attempt to delete the session from the store. Note that this might\n // fail if the store is not available, which is fine.\n try {\n await this.delStored(sub, err)\n } catch {\n // Ignore (better to propagate the original storage error)\n }\n\n throw err\n },\n deleteOnError: isExpectedSessionError,\n },\n )\n }\n\n override async getStored(\n sub: AtprotoDid,\n options?: GetOptions,\n ): Promise<Session | undefined> {\n return super.getStored(sub, options)\n }\n\n override async setStored(sub: AtprotoDid, session: Session) {\n // Prevent tampering with the stored value\n if (sub !== session.tokenSet.sub) {\n throw new TypeError('Token set does not match the expected sub')\n }\n await super.setStored(sub, session)\n await this.hooks.onUpdate?.call(null, sub, session)\n }\n\n override async delStored(sub: AtprotoDid, cause?: unknown): Promise<void> {\n await super.delStored(sub, cause)\n await this.hooks.onDelete?.call(null, sub, cause)\n }\n\n /**\n * @deprecated Use {@link getSession} instead\n * @internal (not really deprecated)\n */\n override async get(\n sub: AtprotoDid,\n options?: GetCachedOptions,\n ): Promise<Session> {\n const session = await this.runtime.usingLock(\n `@atproto-oauth-client-${sub}`,\n async () => {\n // Make sure, even if there is no signal in the options, that the\n // request will be cancelled after at most 30 seconds.\n const signal = timeoutSignal(30e3)\n\n using abortController = combineSignals([options?.signal, signal])\n\n return await super.get(sub, {\n ...options,\n signal: abortController.signal,\n })\n },\n )\n\n if (sub !== session.tokenSet.sub) {\n // Fool-proofing (e.g. against invalid session storage)\n throw new Error('Token set does not match the expected sub')\n }\n\n return session\n }\n\n /**\n * @param refresh When `true`, the credentials will be refreshed even if they\n * are not expired. When `false`, the credentials will not be refreshed even\n * if they are expired. When `undefined`, the credentials will be refreshed\n * if, and only if, they are (about to be) expired. Defaults to `undefined`.\n */\n async getSession(sub: AtprotoDid, refresh: boolean | 'auto' = 'auto') {\n return this.get(sub, {\n noCache: refresh === true,\n allowStale: refresh === false,\n })\n }\n}\n"]}
1
+ {"version":3,"file":"session-getter.js","sourceRoot":"","sources":["../src/session-getter.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AtprotoDid } from '@atproto/did'\nimport type { Key } from '@atproto/jwk'\nimport {\n CachedGetter,\n type GetCachedOptions,\n type SimpleStore,\n} from '@atproto-labs/simple-store'\nimport { AuthMethodUnsatisfiableError } from './errors/auth-method-unsatisfiable-error.js'\nimport { TokenInvalidError } from './errors/token-invalid-error.js'\nimport { TokenRefreshError } from './errors/token-refresh-error.js'\nimport { TokenRevokedError } from './errors/token-revoked-error.js'\nimport type { ClientAuthMethod } from './oauth-client-auth.js'\nimport { OAuthResponseError } from './oauth-response-error.js'\nimport type { TokenSet } from './oauth-server-agent.js'\nimport type { OAuthServerFactory } from './oauth-server-factory.js'\nimport type { Runtime } from './runtime.js'\nimport { combineSignals, timeoutSignal } from './util.js'\n\nexport type Session = {\n dpopKey: Key\n authMethod: ClientAuthMethod\n tokenSet: TokenSet\n}\n\nexport type SessionStore = SimpleStore<string, Session>\n\nexport type SessionGetterOptions = {\n onSessionUpdated?: (sub: AtprotoDid, session: Session) => void\n onSessionDeleted?: (\n sub: AtprotoDid,\n cause: TokenRefreshError | TokenRevokedError | TokenInvalidError | unknown,\n ) => void\n}\n\nexport function isExpectedSessionError(err: unknown) {\n return (\n err instanceof TokenRefreshError ||\n err instanceof TokenRevokedError ||\n err instanceof TokenInvalidError ||\n err instanceof AuthMethodUnsatisfiableError ||\n // The stored session is invalid (e.g. missing properties) and cannot\n // be used properly\n err instanceof TypeError\n )\n}\n\n/**\n * There are several advantages to wrapping the sessionStore in a (single)\n * CachedGetter, the main of which is that the cached getter will ensure that at\n * most one fresh call is ever being made. Another advantage, is that it\n * contains the logic for reading from the cache which, if the cache is based on\n * localStorage/indexedDB, will sync across multiple tabs (for a given sub).\n */\nexport class SessionGetter extends CachedGetter<AtprotoDid, Session> {\n readonly #serverFactory: OAuthServerFactory\n readonly #runtime: Runtime\n readonly #options: SessionGetterOptions\n\n constructor(\n sessionStore: SessionStore,\n serverFactory: OAuthServerFactory,\n runtime: Runtime,\n options: SessionGetterOptions = {},\n ) {\n super(\n async (sub, { signal }, storedSession) => {\n // There needs to be a previous session to be able to refresh. If\n // storedSession is undefined, it means that the store does not contain\n // a session for the given sub.\n if (storedSession === undefined) {\n // Because the session is not in the store, this.delStored() method\n // will not be called by the CachedGetter class (because there is\n // nothing to delete). This would typically happen if there is no\n // synchronization mechanism between instances of this class. Let's\n // make sure an event is dispatched here if this occurs.\n const msg = 'The session was deleted by another process'\n const cause = new TokenRefreshError(sub, msg)\n await options.onSessionDeleted?.call(null, sub, cause)\n throw cause\n }\n\n // @NOTE Throwing a TokenRefreshError (or any other error class defined\n // in the deleteOnError options) will result in this.delStored() being\n // called.\n\n const { dpopKey, authMethod, tokenSet } = storedSession\n\n if (sub !== tokenSet.sub) {\n // Fool-proofing (e.g. against invalid session storage)\n throw new TokenRefreshError(sub, 'Stored session sub mismatch')\n }\n\n if (!tokenSet.refresh_token) {\n throw new TokenRefreshError(sub, 'No refresh token available')\n }\n\n const server = await serverFactory.fromIssuer(\n tokenSet.iss,\n authMethod,\n dpopKey,\n { signal },\n )\n\n // Because refresh tokens can only be used once, we must not use the\n // \"signal\" to abort the refresh, or throw any abort error beyond this\n // point. Any thrown error beyond this point will prevent the\n // TokenGetter from obtaining, and storing, the new token set,\n // effectively rendering the currently saved session unusable.\n signal?.throwIfAborted()\n\n try {\n const newTokenSet = await server.refresh(tokenSet)\n\n if (sub !== newTokenSet.sub) {\n // The server returned another sub. Was the tokenSet manipulated?\n throw new TokenRefreshError(sub, 'Token set sub mismatch')\n }\n\n return {\n dpopKey,\n tokenSet: newTokenSet,\n authMethod: server.authMethod,\n }\n } catch (cause) {\n // Since refresh tokens can only be used once, we might run into\n // concurrency issues if multiple instances (e.g. browser tabs) are\n // trying to refresh the same token simultaneously. The chances of\n // this happening when multiple instances are started simultaneously\n // is reduced by randomizing the expiry time (see isStale() below).\n // The best solution is to use a mutex/lock to ensure that only one\n // instance is refreshing the token at a time (runtime.usingLock) but\n // that is not always possible. Let's try to recover from concurrency\n // issues, or force the session to be deleted by throwing a\n // TokenRefreshError.\n if (\n cause instanceof OAuthResponseError &&\n cause.status === 400 &&\n cause.error === 'invalid_grant'\n ) {\n // In case there is no lock implementation in the runtime, we will\n // wait for a short time to give the other concurrent instances a\n // chance to finish their refreshing of the token. If a concurrent\n // refresh did occur, we will pretend that this one succeeded.\n if (!runtime.hasImplementationLock) {\n await new Promise((r) => setTimeout(r, 1000))\n\n const stored = await this.getStored(sub)\n if (stored === undefined) {\n // A concurrent refresh occurred and caused the session to be\n // deleted (for a reason we can't know at this point).\n\n // Using a distinct error message mainly for debugging\n // purposes. Also, throwing a TokenRefreshError to trigger\n // deletion through the deleteOnError callback.\n const msg = 'The session was deleted by another process'\n throw new TokenRefreshError(sub, msg, { cause })\n } else if (\n stored.tokenSet.access_token !== tokenSet.access_token ||\n stored.tokenSet.refresh_token !== tokenSet.refresh_token\n ) {\n // A concurrent refresh occurred. Pretend this one succeeded.\n return stored\n } else {\n // There were no concurrent refresh. The token is (likely)\n // simply no longer valid.\n }\n }\n\n // Make sure the session gets deleted from the store\n const msg = cause.errorDescription ?? 'The session was revoked'\n throw new TokenRefreshError(sub, msg, { cause })\n }\n\n throw cause\n }\n },\n sessionStore,\n {\n isStale: (sub, { tokenSet }) => {\n return (\n tokenSet.expires_at != null &&\n new Date(tokenSet.expires_at).getTime() <\n Date.now() +\n // Add some lee way to ensure the token is not expired when it\n // reaches the server.\n 10e3 +\n // Add some randomness to reduce the chances of multiple\n // instances trying to refresh the token at the same.\n 30e3 * Math.random()\n )\n },\n deleteOnError: isExpectedSessionError,\n },\n )\n\n this.#serverFactory = serverFactory\n this.#runtime = runtime\n this.#options = options\n }\n\n override async setStored(sub: AtprotoDid, session: Session) {\n // Prevent tampering with the stored value\n if (sub !== session.tokenSet.sub) {\n throw new Error('Token set does not match the expected sub')\n }\n\n try {\n await super.setStored(sub, session)\n } catch (err) {\n // If we fail to persist the session, we assume that we won't be able to\n // fetch it again. We don't want to leave the session in a \"limbo\" state,\n // so we proactively revoke the session and delete it from the store.\n\n const errors: unknown[] = [err]\n\n try {\n const { tokenSet, dpopKey, authMethod } = session\n const server = await this.#serverFactory.fromIssuer(\n tokenSet.iss,\n authMethod,\n dpopKey,\n )\n await server.revoke(tokenSet.refresh_token ?? tokenSet.access_token)\n } catch (err) {\n errors.push(err)\n }\n\n try {\n await this.delStored(sub, err)\n } catch (err) {\n errors.push(err)\n }\n\n throw new AggregateError(errors, `Failed to store session for ${sub}`)\n }\n\n await this.#options.onSessionUpdated?.call(null, sub, session)\n }\n\n override async delStored(sub: AtprotoDid, cause?: unknown): Promise<void> {\n await super.delStored(sub, cause)\n await this.#options.onSessionDeleted?.call(null, sub, cause)\n }\n\n override async get(\n sub: AtprotoDid,\n options?: GetCachedOptions,\n ): Promise<Session> {\n const session = await this.#runtime.usingLock(\n `@atproto-oauth-client-${sub}`,\n async () => {\n // Make sure, even if there is no signal in the options, that the\n // request will be cancelled after at most 30 seconds.\n const signal = timeoutSignal(30e3)\n\n using abortController = combineSignals([options?.signal, signal])\n\n return await super.get(sub, {\n ...options,\n signal: abortController.signal,\n })\n },\n )\n\n if (sub !== session.tokenSet.sub) {\n // Fool-proofing (e.g. against invalid session storage)\n throw new Error('Token set does not match the expected sub')\n }\n\n return session\n }\n\n /**\n * @param refresh When `true`, the credentials will be refreshed even if they\n * are not expired. When `false`, the credentials will not be refreshed even\n * if they are expired. When `undefined`, the credentials will be refreshed\n * if, and only if, they are (about to be) expired. Defaults to `undefined`.\n */\n async getSession(sub: AtprotoDid, refresh: boolean | 'auto' = 'auto') {\n return this.get(sub, {\n noCache: refresh === true,\n allowStale: refresh === false,\n })\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/oauth-client",
3
- "version": "0.7.10",
3
+ "version": "0.8.0",
4
4
  "license": "MIT",
5
5
  "description": "OAuth client for ATPROTO PDS. This package serves as common base for environment-specific implementations (NodeJS, Browser, React-Native).",
6
6
  "keywords": [
@@ -34,16 +34,16 @@
34
34
  "core-js": "^3",
35
35
  "multiformats": "^13.0.0",
36
36
  "zod": "^3.23.8",
37
- "@atproto-labs/did-resolver": "^0.3.5",
38
- "@atproto-labs/fetch": "^0.3.4",
39
- "@atproto-labs/identity-resolver": "^0.4.4",
40
- "@atproto-labs/handle-resolver": "^0.4.5",
41
- "@atproto-labs/simple-store": "^0.4.4",
37
+ "@atproto-labs/fetch": "^0.3.5",
38
+ "@atproto-labs/handle-resolver": "^0.4.7",
39
+ "@atproto-labs/did-resolver": "^0.3.6",
40
+ "@atproto-labs/simple-store": "^0.5.0",
41
+ "@atproto-labs/simple-store-memory": "^0.2.5",
42
+ "@atproto-labs/identity-resolver": "^0.4.6",
42
43
  "@atproto/did": "^0.5.4",
43
- "@atproto-labs/simple-store-memory": "^0.2.4",
44
- "@atproto/jwk": "^0.7.4",
45
44
  "@atproto/oauth-types": "^0.7.5",
46
- "@atproto/xrpc": "^0.8.6"
45
+ "@atproto/xrpc": "^0.8.7",
46
+ "@atproto/jwk": "^0.7.4"
47
47
  },
48
48
  "devDependencies": {
49
49
  "vitest": "^4.0.16"