@atproto/oauth-client 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/LICENSE.txt +7 -0
  3. package/README.md +124 -0
  4. package/dist/constants.d.ts +5 -0
  5. package/dist/constants.d.ts.map +1 -0
  6. package/dist/constants.js +8 -0
  7. package/dist/constants.js.map +1 -0
  8. package/dist/fetch-dpop.d.ts +21 -0
  9. package/dist/fetch-dpop.d.ts.map +1 -0
  10. package/dist/fetch-dpop.js +149 -0
  11. package/dist/fetch-dpop.js.map +1 -0
  12. package/dist/index.d.ts +15 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.js +35 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/lock.d.ts +2 -0
  17. package/dist/lock.d.ts.map +1 -0
  18. package/dist/lock.js +33 -0
  19. package/dist/lock.js.map +1 -0
  20. package/dist/oauth-agent.d.ts +29 -0
  21. package/dist/oauth-agent.d.ts.map +1 -0
  22. package/dist/oauth-agent.js +138 -0
  23. package/dist/oauth-agent.js.map +1 -0
  24. package/dist/oauth-authorization-server-metadata-resolver.d.ts +15 -0
  25. package/dist/oauth-authorization-server-metadata-resolver.d.ts.map +1 -0
  26. package/dist/oauth-authorization-server-metadata-resolver.js +56 -0
  27. package/dist/oauth-authorization-server-metadata-resolver.js.map +1 -0
  28. package/dist/oauth-callback-error.d.ts +7 -0
  29. package/dist/oauth-callback-error.d.ts.map +1 -0
  30. package/dist/oauth-callback-error.js +28 -0
  31. package/dist/oauth-callback-error.js.map +1 -0
  32. package/dist/oauth-client.d.ts +78 -0
  33. package/dist/oauth-client.d.ts.map +1 -0
  34. package/dist/oauth-client.js +278 -0
  35. package/dist/oauth-client.js.map +1 -0
  36. package/dist/oauth-protected-resource-metadata-resolver.d.ts +15 -0
  37. package/dist/oauth-protected-resource-metadata-resolver.d.ts.map +1 -0
  38. package/dist/oauth-protected-resource-metadata-resolver.js +58 -0
  39. package/dist/oauth-protected-resource-metadata-resolver.js.map +1 -0
  40. package/dist/oauth-resolver-error.d.ts +7 -0
  41. package/dist/oauth-resolver-error.d.ts.map +1 -0
  42. package/dist/oauth-resolver-error.js +17 -0
  43. package/dist/oauth-resolver-error.js.map +1 -0
  44. package/dist/oauth-resolver.d.ts +62 -0
  45. package/dist/oauth-resolver.d.ts.map +1 -0
  46. package/dist/oauth-resolver.js +73 -0
  47. package/dist/oauth-resolver.js.map +1 -0
  48. package/dist/oauth-response-error.d.ts +11 -0
  49. package/dist/oauth-response-error.d.ts.map +1 -0
  50. package/dist/oauth-response-error.js +48 -0
  51. package/dist/oauth-response-error.js.map +1 -0
  52. package/dist/oauth-server-agent.d.ts +51 -0
  53. package/dist/oauth-server-agent.d.ts.map +1 -0
  54. package/dist/oauth-server-agent.js +228 -0
  55. package/dist/oauth-server-agent.js.map +1 -0
  56. package/dist/oauth-server-factory.d.ts +20 -0
  57. package/dist/oauth-server-factory.d.ts.map +1 -0
  58. package/dist/oauth-server-factory.js +53 -0
  59. package/dist/oauth-server-factory.js.map +1 -0
  60. package/dist/refresh-error.d.ts +7 -0
  61. package/dist/refresh-error.d.ts.map +1 -0
  62. package/dist/refresh-error.js +16 -0
  63. package/dist/refresh-error.js.map +1 -0
  64. package/dist/runtime-implementation.d.ts +12 -0
  65. package/dist/runtime-implementation.d.ts.map +1 -0
  66. package/dist/runtime-implementation.js +3 -0
  67. package/dist/runtime-implementation.js.map +1 -0
  68. package/dist/runtime.d.ts +35 -0
  69. package/dist/runtime.d.ts.map +1 -0
  70. package/dist/runtime.js +185 -0
  71. package/dist/runtime.js.map +1 -0
  72. package/dist/session-getter.d.ts +30 -0
  73. package/dist/session-getter.d.ts.map +1 -0
  74. package/dist/session-getter.js +149 -0
  75. package/dist/session-getter.js.map +1 -0
  76. package/dist/types.d.ts +1580 -0
  77. package/dist/types.d.ts.map +1 -0
  78. package/dist/types.js +8 -0
  79. package/dist/types.js.map +1 -0
  80. package/dist/util.d.ts +9 -0
  81. package/dist/util.d.ts.map +1 -0
  82. package/dist/util.js +35 -0
  83. package/dist/util.js.map +1 -0
  84. package/dist/validate-client-metadata.d.ts +5 -0
  85. package/dist/validate-client-metadata.d.ts.map +1 -0
  86. package/dist/validate-client-metadata.js +46 -0
  87. package/dist/validate-client-metadata.js.map +1 -0
  88. package/package.json +46 -0
  89. package/src/constants.ts +4 -0
  90. package/src/fetch-dpop.ts +235 -0
  91. package/src/index.ts +18 -0
  92. package/src/lock.ts +34 -0
  93. package/src/oauth-agent.ts +150 -0
  94. package/src/oauth-authorization-server-metadata-resolver.ts +98 -0
  95. package/src/oauth-callback-error.ts +16 -0
  96. package/src/oauth-client.ts +440 -0
  97. package/src/oauth-protected-resource-metadata-resolver.ts +102 -0
  98. package/src/oauth-resolver-error.ts +12 -0
  99. package/src/oauth-resolver.ts +111 -0
  100. package/src/oauth-response-error.ts +31 -0
  101. package/src/oauth-server-agent.ts +275 -0
  102. package/src/oauth-server-factory.ts +41 -0
  103. package/src/refresh-error.ts +9 -0
  104. package/src/runtime-implementation.ts +17 -0
  105. package/src/runtime.ts +211 -0
  106. package/src/session-getter.ts +182 -0
  107. package/src/types.ts +26 -0
  108. package/src/util.ts +51 -0
  109. package/src/validate-client-metadata.ts +61 -0
  110. package/tsconfig.build.json +8 -0
  111. package/tsconfig.json +4 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,20 @@
1
+ # @atproto/oauth-client
2
+
3
+ ## 0.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#2482](https://github.com/bluesky-social/atproto/pull/2482) [`a8d6c1123`](https://github.com/bluesky-social/atproto/commit/a8d6c112359f5c4c0cfbe2df63443ed275f2a646) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Add OAuth provider capability & support for DPoP signed tokens
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [[`a8d6c1123`](https://github.com/bluesky-social/atproto/commit/a8d6c112359f5c4c0cfbe2df63443ed275f2a646)]:
12
+ - @atproto-labs/simple-store-memory@0.1.0
13
+ - @atproto-labs/identity-resolver@0.1.0
14
+ - @atproto-labs/handle-resolver@0.1.0
15
+ - @atproto-labs/did-resolver@0.1.0
16
+ - @atproto-labs/simple-store@0.1.0
17
+ - @atproto/oauth-types@0.1.0
18
+ - @atproto-labs/fetch@0.1.0
19
+ - @atproto/jwk@0.1.0
20
+ - @atproto/did@0.1.0
package/LICENSE.txt ADDED
@@ -0,0 +1,7 @@
1
+ Dual MIT/Apache-2.0 License
2
+
3
+ Copyright (c) 2022-2024 Bluesky PBC, and Contributors
4
+
5
+ Except as otherwise noted in individual files, this software is licensed under the MIT license (<http://opensource.org/licenses/MIT>), or the Apache License, Version 2.0 (<http://www.apache.org/licenses/LICENSE-2.0>).
6
+
7
+ Downstream projects and end users may chose either license individually, or both together, at their discretion. The motivation for this dual-licensing is the additional software patent assurance provided by Apache 2.0.
package/README.md ADDED
@@ -0,0 +1,124 @@
1
+ # @atproto/oauth-client: atproto flavoured OAuth client
2
+
3
+ Core library for implementing ATPROTO OAuth clients.
4
+
5
+ For a browser specific implementation, see `@atproto/oauth-client-browser`.
6
+ For a node specific implementation, see `@atproto/oauth-client-node`.
7
+
8
+ ```ts
9
+ import { OAuthClient } from '@atproto/oauth-client'
10
+ import { JoseKey } from '@atproto/jwk-jose' // NodeJS/Browser only
11
+
12
+ const client = new OAuthClient({
13
+ handleResolver: 'https://bsky.social', // On node, you should use a DNS based resolver
14
+ responseMode: 'query', // or "fragment" or "form_post" (for backend clients only)
15
+ clientMetadata: {
16
+ // These must be the same metadata as the one exposed on the
17
+ // "/.well-known/oauth-client-metadata" endpoint (except when using a
18
+ // loopback client)
19
+ },
20
+
21
+ runtimeImplementation: {
22
+ // A runtime specific implementation of the crypto operations needed by the
23
+ // OAuth client.
24
+
25
+ createKey(algs: string[]): Promise<Key> {
26
+ // algs is an ordered array of preferred algorithms (e.g. ['RS256', 'ES256'])
27
+
28
+ // Note, in browser environments, it is better to use non extractable keys
29
+ // to prevent leaking the private key. This can be done using the
30
+ // WebcryptoKey class from the "@atproto/jwk-webcrypto" package. The
31
+ // inconvenient of these keys (which is also what makes them stronger) is
32
+ // that the only way to persist them across browser reloads is to save
33
+ // them in the indexed DB.
34
+ return JoseKey.generate(algs)
35
+ },
36
+ getRandomValues(length: number): Uint8Array | PromiseLike<Uint8Array> {
37
+ // length is the number of bytes to generate
38
+
39
+ const bytes = new Uint8Array(byteLength)
40
+ crypto.getRandomValues(bytes)
41
+ return bytes
42
+ },
43
+ digest(
44
+ bytes: Uint8Array,
45
+ algorithm: { name: 'sha256' | 'sha384' | 'sha512' },
46
+ ): Uint8Array | PromiseLike<Uint8Array> {
47
+ // sha256 is required. Unsupported algorithms should throw an error.
48
+
49
+ const buffer = await this.crypto.subtle.digest(
50
+ algorithm.name.startsWith('sha')
51
+ ? `SHA-${algorithm.name.slice(-3)}`
52
+ : 'invalid',
53
+ bytes,
54
+ )
55
+ return new Uint8Array(buffer)
56
+ },
57
+ },
58
+
59
+ stateStore: {
60
+ // A store for saving state data while the user is being redirected to the
61
+ // authorization server.
62
+
63
+ set(key: string, internalState: InternalStateData): Promise<void> {
64
+ throw new Error('Not implemented')
65
+ },
66
+ get(key: string): Promise<InternalStateData | undefined> {
67
+ throw new Error('Not implemented')
68
+ },
69
+ del(key: string): Promise<void> {
70
+ throw new Error('Not implemented')
71
+ },
72
+ },
73
+
74
+ sessionStore: {
75
+ // A store for saving session data.
76
+
77
+ set(sub: string, session: Session): Promise<void> {
78
+ throw new Error('Not implemented')
79
+ },
80
+ get(sub: string): Promise<Session | undefined> {
81
+ throw new Error('Not implemented')
82
+ },
83
+ del(sub: string): Promise<void> {
84
+ throw new Error('Not implemented')
85
+ },
86
+ },
87
+
88
+ keyset: [
89
+ // For backend clients only, a list of private keys to use for signing
90
+ // credentials. These keys MUST correspond to the public keys exposed on the
91
+ // "jwks_uri" of the client metadata.
92
+ await JoseKey.fromImportable(process.env.PRIVATE_KEY_1),
93
+ await JoseKey.fromImportable(process.env.PRIVATE_KEY_2),
94
+ await JoseKey.fromImportable(process.env.PRIVATE_KEY_3),
95
+ ],
96
+ })
97
+ ```
98
+
99
+ ```ts
100
+ const url = await client.authorize('foo.bsky.team', {
101
+ state: '434321',
102
+ prompt: 'consent',
103
+ scope: 'email',
104
+ ui_locales: 'fr',
105
+ })
106
+
107
+ // Make user visit "url". Then, once it was redirected to the callback URI, call:
108
+
109
+ const params = new URLSearchParams('code=...&state=...')
110
+ const result = await client.callback(params)
111
+
112
+ // Verify the state (e.g. to link to an internal user)
113
+ result.state === '434321'
114
+
115
+ // The authenticated user's identifier
116
+ result.agent.sub
117
+
118
+ // Make an authenticated request to the server. New credentials will be
119
+ // automatically fetched if needed (causing sessionStore.set() to be called).
120
+ await result.agent.request('/xrpc/foo.bar')
121
+
122
+ // revoke credentials on the server (causing sessionStore.del() to be called)
123
+ await result.agent.signOut()
124
+ ```
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Per ATProto spec (OpenID uses RS256)
3
+ */
4
+ export declare const FALLBACK_ALG = "ES256";
5
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,YAAY,UAAU,CAAA"}
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FALLBACK_ALG = void 0;
4
+ /**
5
+ * Per ATProto spec (OpenID uses RS256)
6
+ */
7
+ exports.FALLBACK_ALG = 'ES256';
8
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":";;;AAAA;;GAEG;AACU,QAAA,YAAY,GAAG,OAAO,CAAA"}
@@ -0,0 +1,21 @@
1
+ import { Fetch, FetchContext } from '@atproto-labs/fetch';
2
+ import { SimpleStore } from '@atproto-labs/simple-store';
3
+ import { Key } from '@atproto/jwk';
4
+ export type DpopFetchWrapperOptions<C = FetchContext> = {
5
+ key: Key;
6
+ iss: string;
7
+ nonces: SimpleStore<string, string>;
8
+ supportedAlgs?: string[];
9
+ sha256?: (input: string) => Promise<string>;
10
+ /**
11
+ * Is the intended server an authorization server (true) or a resource server
12
+ * (false)? Setting this may allow to avoid parsing the response body to
13
+ * determine the dpop-nonce.
14
+ *
15
+ * @default undefined
16
+ */
17
+ isAuthServer?: boolean;
18
+ fetch?: Fetch<C>;
19
+ };
20
+ export declare function dpopFetchWrapper<C = FetchContext>({ key, iss, supportedAlgs, nonces, sha256, isAuthServer, fetch, }: DpopFetchWrapperOptions<C>): Fetch<C>;
21
+ //# sourceMappingURL=fetch-dpop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch-dpop.d.ts","sourceRoot":"","sources":["../src/fetch-dpop.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,YAAY,EAAwB,MAAM,qBAAqB,CAAA;AAC/E,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAA;AACxD,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AAUlC,MAAM,MAAM,uBAAuB,CAAC,CAAC,GAAG,YAAY,IAAI;IACtD,GAAG,EAAE,GAAG,CAAA;IACR,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACnC,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IAE3C;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAA;CACjB,CAAA;AAED,wBAAgB,gBAAgB,CAAC,CAAC,GAAG,YAAY,EAAE,EACjD,GAAG,EACH,GAAG,EACH,aAAa,EACb,MAAM,EACN,MAAiE,EACjE,YAAY,EACZ,KAAwB,GACzB,EAAE,uBAAuB,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAwGvC"}
@@ -0,0 +1,149 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.dpopFetchWrapper = void 0;
4
+ const fetch_1 = require("@atproto-labs/fetch");
5
+ const base64_1 = require("multiformats/bases/base64");
6
+ // "undefined" in non https environments or environments without crypto
7
+ const subtle = globalThis.crypto?.subtle;
8
+ const ReadableStream = globalThis.ReadableStream;
9
+ function dpopFetchWrapper({ key, iss, supportedAlgs, nonces, sha256 = typeof subtle !== 'undefined' ? subtleSha256 : undefined, isAuthServer, fetch = globalThis.fetch, }) {
10
+ if (!sha256) {
11
+ throw new TypeError(`crypto.subtle is not available in this environment. Please provide a sha256 function.`);
12
+ }
13
+ const alg = negotiateAlg(key, supportedAlgs);
14
+ return async function (input, init) {
15
+ if (!key.algorithms.includes(alg)) {
16
+ throw new TypeError(`Key does not support the algorithm ${alg}`);
17
+ }
18
+ const request = init == null && input instanceof Request
19
+ ? input
20
+ : new Request(input, init);
21
+ const authorizationHeader = request.headers.get('Authorization');
22
+ const ath = authorizationHeader?.startsWith('DPoP ')
23
+ ? await sha256(authorizationHeader.slice(5))
24
+ : undefined;
25
+ const { method, url } = request;
26
+ const { origin } = new URL(url);
27
+ let initNonce;
28
+ try {
29
+ initNonce = await nonces.get(origin);
30
+ }
31
+ catch {
32
+ // Ignore get errors, we will just not send a nonce
33
+ }
34
+ const initProof = await buildProof(key, alg, iss, method, url, initNonce, ath);
35
+ request.headers.set('DPoP', initProof);
36
+ const initResponse = await fetch.call(this, request);
37
+ // Make sure the response body is consumed. Either by the caller (when the
38
+ // response is returned), of if an error is thrown (catch block).
39
+ const nextNonce = initResponse.headers.get('DPoP-Nonce');
40
+ if (!nextNonce || nextNonce === initNonce) {
41
+ // No nonce was returned or it is the same as the one we sent. No need to
42
+ // update the nonce store, or retry the request.
43
+ return initResponse;
44
+ }
45
+ // Store the fresh nonce for future requests
46
+ try {
47
+ await nonces.set(origin, nextNonce);
48
+ }
49
+ catch {
50
+ // Ignore set errors
51
+ }
52
+ const shouldRetry = await isUseDpopNonceError(initResponse, isAuthServer);
53
+ if (!shouldRetry) {
54
+ // Not a "use_dpop_nonce" error, so there is no need to retry
55
+ return initResponse;
56
+ }
57
+ // If the input stream was already consumed, we cannot retry the request. A
58
+ // solution would be to clone() the request but that would bufferize the
59
+ // entire stream in memory which can lead to memory starvation. Instead, we
60
+ // will return the original response and let the calling code handle retries.
61
+ if (input === request) {
62
+ // The input request body was consumed. We cannot retry the request.
63
+ return initResponse;
64
+ }
65
+ if (ReadableStream && init?.body instanceof ReadableStream) {
66
+ // The init body was consumed. We cannot retry the request.
67
+ return initResponse;
68
+ }
69
+ // We will now retry the request with the fresh nonce.
70
+ // The initial response body must be consumed (see cancelBody's doc).
71
+ await (0, fetch_1.cancelBody)(initResponse, 'log');
72
+ const nextProof = await buildProof(key, alg, iss, method, url, nextNonce, ath);
73
+ const nextRequest = new Request(input, init);
74
+ nextRequest.headers.set('DPoP', nextProof);
75
+ return fetch.call(this, nextRequest);
76
+ };
77
+ }
78
+ exports.dpopFetchWrapper = dpopFetchWrapper;
79
+ async function buildProof(key, alg, iss, htm, htu, nonce, ath) {
80
+ if (!key.bareJwk) {
81
+ throw new Error('Only asymmetric keys can be used as DPoP proofs');
82
+ }
83
+ const now = Math.floor(Date.now() / 1e3);
84
+ return key.createJwt({
85
+ alg,
86
+ typ: 'dpop+jwt',
87
+ jwk: key.bareJwk,
88
+ }, {
89
+ iss,
90
+ iat: now,
91
+ exp: now + 10,
92
+ // Any collision will cause the request to be rejected by the server. no biggie.
93
+ jti: Math.random().toString(36).slice(2),
94
+ htm,
95
+ htu,
96
+ nonce,
97
+ ath,
98
+ });
99
+ }
100
+ async function isUseDpopNonceError(response, isAuthServer) {
101
+ // https://datatracker.ietf.org/doc/html/rfc6750#section-3
102
+ // https://datatracker.ietf.org/doc/html/rfc9449#name-resource-server-provided-no
103
+ if (isAuthServer === undefined || isAuthServer === false) {
104
+ if (response.status === 401) {
105
+ const wwwAuth = response.headers.get('WWW-Authenticate');
106
+ if (wwwAuth?.startsWith('DPoP')) {
107
+ return wwwAuth.includes('error="use_dpop_nonce"');
108
+ }
109
+ }
110
+ }
111
+ // https://datatracker.ietf.org/doc/html/rfc9449#name-authorization-server-provid
112
+ if (isAuthServer === undefined || isAuthServer === true) {
113
+ if (response.status === 400) {
114
+ try {
115
+ const json = await (0, fetch_1.peekJson)(response, 10 * 1024);
116
+ return typeof json === 'object' && json?.['error'] === 'use_dpop_nonce';
117
+ }
118
+ catch {
119
+ // Response too big (to be "use_dpop_nonce" error) or invalid JSON
120
+ return false;
121
+ }
122
+ }
123
+ }
124
+ return false;
125
+ }
126
+ function negotiateAlg(key, supportedAlgs) {
127
+ if (supportedAlgs) {
128
+ // Use order of supportedAlgs as preference
129
+ const alg = supportedAlgs.find((a) => key.algorithms.includes(a));
130
+ if (alg)
131
+ return alg;
132
+ }
133
+ else {
134
+ const [alg] = key.algorithms;
135
+ if (alg)
136
+ return alg;
137
+ }
138
+ throw new Error('Key does not match any alg supported by the server');
139
+ }
140
+ async function subtleSha256(input) {
141
+ if (subtle == null) {
142
+ throw new Error(`crypto.subtle is not available in this environment. Please provide a sha256 function.`);
143
+ }
144
+ const bytes = new TextEncoder().encode(input);
145
+ const digest = await subtle.digest('SHA-256', bytes);
146
+ const digestBytes = new Uint8Array(digest);
147
+ return base64_1.base64url.baseEncode(digestBytes);
148
+ }
149
+ //# sourceMappingURL=fetch-dpop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch-dpop.js","sourceRoot":"","sources":["../src/fetch-dpop.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ export { FetchError, FetchRequestError, FetchResponseError, } from '@atproto-labs/fetch';
2
+ export * from './oauth-agent.js';
3
+ export * from './oauth-authorization-server-metadata-resolver.js';
4
+ export * from './oauth-callback-error.js';
5
+ export * from './oauth-client.js';
6
+ export * from './oauth-protected-resource-metadata-resolver.js';
7
+ export * from './oauth-resolver-error.js';
8
+ export * from './oauth-response-error.js';
9
+ export * from './oauth-server-agent.js';
10
+ export * from './oauth-server-factory.js';
11
+ export * from './refresh-error.js';
12
+ export * from './runtime-implementation.js';
13
+ export * from './session-getter.js';
14
+ export * from './types.js';
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,qBAAqB,CAAA;AAC5B,cAAc,kBAAkB,CAAA;AAChC,cAAc,mDAAmD,CAAA;AACjE,cAAc,2BAA2B,CAAA;AACzC,cAAc,mBAAmB,CAAA;AACjC,cAAc,iDAAiD,CAAA;AAC/D,cAAc,2BAA2B,CAAA;AACzC,cAAc,2BAA2B,CAAA;AACzC,cAAc,yBAAyB,CAAA;AACvC,cAAc,2BAA2B,CAAA;AACzC,cAAc,oBAAoB,CAAA;AAClC,cAAc,6BAA6B,CAAA;AAC3C,cAAc,qBAAqB,CAAA;AACnC,cAAc,YAAY,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.FetchResponseError = exports.FetchRequestError = exports.FetchError = void 0;
18
+ var fetch_1 = require("@atproto-labs/fetch");
19
+ Object.defineProperty(exports, "FetchError", { enumerable: true, get: function () { return fetch_1.FetchError; } });
20
+ Object.defineProperty(exports, "FetchRequestError", { enumerable: true, get: function () { return fetch_1.FetchRequestError; } });
21
+ Object.defineProperty(exports, "FetchResponseError", { enumerable: true, get: function () { return fetch_1.FetchResponseError; } });
22
+ __exportStar(require("./oauth-agent.js"), exports);
23
+ __exportStar(require("./oauth-authorization-server-metadata-resolver.js"), exports);
24
+ __exportStar(require("./oauth-callback-error.js"), exports);
25
+ __exportStar(require("./oauth-client.js"), exports);
26
+ __exportStar(require("./oauth-protected-resource-metadata-resolver.js"), exports);
27
+ __exportStar(require("./oauth-resolver-error.js"), exports);
28
+ __exportStar(require("./oauth-response-error.js"), exports);
29
+ __exportStar(require("./oauth-server-agent.js"), exports);
30
+ __exportStar(require("./oauth-server-factory.js"), exports);
31
+ __exportStar(require("./refresh-error.js"), exports);
32
+ __exportStar(require("./runtime-implementation.js"), exports);
33
+ __exportStar(require("./session-getter.js"), exports);
34
+ __exportStar(require("./types.js"), exports);
35
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,6CAI4B;AAH1B,mGAAA,UAAU,OAAA;AACV,0GAAA,iBAAiB,OAAA;AACjB,2GAAA,kBAAkB,OAAA;AAEpB,mDAAgC;AAChC,oFAAiE;AACjE,4DAAyC;AACzC,oDAAiC;AACjC,kFAA+D;AAC/D,4DAAyC;AACzC,4DAAyC;AACzC,0DAAuC;AACvC,4DAAyC;AACzC,qDAAkC;AAClC,8DAA2C;AAC3C,sDAAmC;AACnC,6CAA0B"}
package/dist/lock.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export declare function requestLocalLock<T>(name: string, fn: () => T | PromiseLike<T>): Promise<T>;
2
+ //# sourceMappingURL=lock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lock.d.ts","sourceRoot":"","sources":["../src/lock.ts"],"names":[],"mappings":"AAsBA,wBAAgB,gBAAgB,CAAC,CAAC,EAChC,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,GAC3B,OAAO,CAAC,CAAC,CAAC,CAQZ"}
package/dist/lock.js ADDED
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.requestLocalLock = void 0;
4
+ const locks = new Map();
5
+ function acquireLocalLock(name) {
6
+ return new Promise((resolveAcquire) => {
7
+ const prev = locks.get(name) ?? Promise.resolve();
8
+ const next = prev.then(() => {
9
+ return new Promise((resolveRelease) => {
10
+ const release = () => {
11
+ // Only delete the lock if it is still the current one
12
+ if (locks.get(name) === next)
13
+ locks.delete(name);
14
+ resolveRelease();
15
+ };
16
+ resolveAcquire(release);
17
+ });
18
+ });
19
+ locks.set(name, next);
20
+ });
21
+ }
22
+ function requestLocalLock(name, fn) {
23
+ return acquireLocalLock(name).then(async (release) => {
24
+ try {
25
+ return await fn();
26
+ }
27
+ finally {
28
+ release();
29
+ }
30
+ });
31
+ }
32
+ exports.requestLocalLock = requestLocalLock;
33
+ //# sourceMappingURL=lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lock.js","sourceRoot":"","sources":["../src/lock.ts"],"names":[],"mappings":";;;AAAA,MAAM,KAAK,GAAG,IAAI,GAAG,EAA0B,CAAA;AAE/C,SAAS,gBAAgB,CAAC,IAAa;IACrC,OAAO,IAAI,OAAO,CAAC,CAAC,cAAc,EAAE,EAAE;QACpC,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAA;QACjD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE;YAC1B,OAAO,IAAI,OAAO,CAAO,CAAC,cAAc,EAAE,EAAE;gBAC1C,MAAM,OAAO,GAAG,GAAG,EAAE;oBACnB,sDAAsD;oBACtD,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI;wBAAE,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;oBAEhD,cAAc,EAAE,CAAA;gBAClB,CAAC,CAAA;gBAED,cAAc,CAAC,OAAO,CAAC,CAAA;YACzB,CAAC,CAAC,CAAA;QACJ,CAAC,CAAC,CAAA;QAEF,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;IACvB,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,SAAgB,gBAAgB,CAC9B,IAAY,EACZ,EAA4B;IAE5B,OAAO,gBAAgB,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;QACnD,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,EAAE,CAAA;QACnB,CAAC;gBAAS,CAAC;YACT,OAAO,EAAE,CAAA;QACX,CAAC;IACH,CAAC,CAAC,CAAA;AACJ,CAAC;AAXD,4CAWC"}
@@ -0,0 +1,29 @@
1
+ import { Fetch } from '@atproto-labs/fetch';
2
+ import { JwtPayload } from '@atproto/jwk';
3
+ import { OAuthAuthorizationServerMetadata } from '@atproto/oauth-types';
4
+ import { OAuthServerAgent, TokenSet } from './oauth-server-agent.js';
5
+ import { SessionGetter } from './session-getter.js';
6
+ export declare class OAuthAgent {
7
+ readonly server: OAuthServerAgent;
8
+ readonly sub: string;
9
+ private readonly sessionGetter;
10
+ protected dpopFetch: Fetch<unknown>;
11
+ constructor(server: OAuthServerAgent, sub: string, sessionGetter: SessionGetter, fetch?: Fetch);
12
+ get serverMetadata(): Readonly<OAuthAuthorizationServerMetadata>;
13
+ refreshIfNeeded(): Promise<void>;
14
+ /**
15
+ * @param refresh See {@link SessionGetter.getSession}
16
+ */
17
+ protected getTokenSet(refresh?: boolean): Promise<TokenSet>;
18
+ getInfo(): Promise<{
19
+ userinfo?: JwtPayload;
20
+ expired?: boolean;
21
+ scope?: string;
22
+ iss: string;
23
+ aud: string;
24
+ sub: string;
25
+ }>;
26
+ signOut(): Promise<void>;
27
+ request(pathname: string, init?: RequestInit): Promise<Response>;
28
+ }
29
+ //# sourceMappingURL=oauth-agent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-agent.d.ts","sourceRoot":"","sources":["../src/oauth-agent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAa,MAAM,qBAAqB,CAAA;AACtD,OAAO,EAAE,UAAU,EAAmB,MAAM,cAAc,CAAA;AAC1D,OAAO,EAAE,gCAAgC,EAAE,MAAM,sBAAsB,CAAA;AAGvE,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAMnD,qBAAa,UAAU;aAIH,MAAM,EAAE,gBAAgB;aACxB,GAAG,EAAE,MAAM;IAC3B,OAAO,CAAC,QAAQ,CAAC,aAAa;IALhC,SAAS,CAAC,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,CAAA;gBAGjB,MAAM,EAAE,gBAAgB,EACxB,GAAG,EAAE,MAAM,EACV,aAAa,EAAE,aAAa,EAC7C,KAAK,GAAE,KAAwB;IAajC,IAAI,cAAc,IAAI,QAAQ,CAAC,gCAAgC,CAAC,CAE/D;IAEY,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAI7C;;OAEG;cACa,WAAW,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;IAK3D,OAAO,IAAI,OAAO,CAAC;QACvB,QAAQ,CAAC,EAAE,UAAU,CAAA;QACrB,OAAO,CAAC,EAAE,OAAO,CAAA;QACjB,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,GAAG,EAAE,MAAM,CAAA;QACX,GAAG,EAAE,MAAM,CAAA;QACX,GAAG,EAAE,MAAM,CAAA;KACZ,CAAC;IAkBI,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IASxB,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC;CAqDvE"}
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OAuthAgent = void 0;
4
+ const fetch_1 = require("@atproto-labs/fetch");
5
+ const jwk_1 = require("@atproto/jwk");
6
+ const fetch_dpop_js_1 = require("./fetch-dpop.js");
7
+ const ReadableStream = globalThis.ReadableStream;
8
+ class OAuthAgent {
9
+ constructor(server, sub, sessionGetter, fetch = globalThis.fetch) {
10
+ Object.defineProperty(this, "server", {
11
+ enumerable: true,
12
+ configurable: true,
13
+ writable: true,
14
+ value: server
15
+ });
16
+ Object.defineProperty(this, "sub", {
17
+ enumerable: true,
18
+ configurable: true,
19
+ writable: true,
20
+ value: sub
21
+ });
22
+ Object.defineProperty(this, "sessionGetter", {
23
+ enumerable: true,
24
+ configurable: true,
25
+ writable: true,
26
+ value: sessionGetter
27
+ });
28
+ Object.defineProperty(this, "dpopFetch", {
29
+ enumerable: true,
30
+ configurable: true,
31
+ writable: true,
32
+ value: void 0
33
+ });
34
+ this.dpopFetch = (0, fetch_dpop_js_1.dpopFetchWrapper)({
35
+ fetch: (0, fetch_1.bindFetch)(fetch),
36
+ iss: server.clientMetadata.client_id,
37
+ key: server.dpopKey,
38
+ supportedAlgs: server.serverMetadata.dpop_signing_alg_values_supported,
39
+ sha256: async (v) => server.runtime.sha256(v),
40
+ nonces: server.dpopNonces,
41
+ isAuthServer: false,
42
+ });
43
+ }
44
+ get serverMetadata() {
45
+ return this.server.serverMetadata;
46
+ }
47
+ async refreshIfNeeded() {
48
+ await this.getTokenSet(undefined);
49
+ }
50
+ /**
51
+ * @param refresh See {@link SessionGetter.getSession}
52
+ */
53
+ async getTokenSet(refresh) {
54
+ const { tokenSet } = await this.sessionGetter.getSession(this.sub, refresh);
55
+ return tokenSet;
56
+ }
57
+ async getInfo() {
58
+ const tokenSet = await this.getTokenSet();
59
+ return {
60
+ userinfo: tokenSet.id_token
61
+ ? (0, jwk_1.unsafeDecodeJwt)(tokenSet.id_token).payload
62
+ : undefined,
63
+ expired: tokenSet.expires_at == null
64
+ ? undefined
65
+ : new Date(tokenSet.expires_at).getTime() < Date.now() - 5e3,
66
+ scope: tokenSet.scope,
67
+ iss: tokenSet.iss,
68
+ aud: tokenSet.aud,
69
+ sub: tokenSet.sub,
70
+ };
71
+ }
72
+ async signOut() {
73
+ try {
74
+ const { tokenSet } = await this.sessionGetter.getSession(this.sub, false);
75
+ await this.server.revoke(tokenSet.access_token);
76
+ }
77
+ finally {
78
+ await this.sessionGetter.delStored(this.sub);
79
+ }
80
+ }
81
+ async request(pathname, init) {
82
+ // This will try and refresh the token if it is known to be expired
83
+ const tokenSet = await this.getTokenSet(undefined);
84
+ const initialUrl = new URL(pathname, tokenSet.aud);
85
+ const initialAuth = `${tokenSet.token_type} ${tokenSet.access_token}`;
86
+ const headers = new Headers(init?.headers);
87
+ headers.set('Authorization', initialAuth);
88
+ const initialResponse = await this.dpopFetch(initialUrl, {
89
+ ...init,
90
+ headers,
91
+ });
92
+ // If the token is not expired, we don't need to refresh it
93
+ if (!isTokenExpiredResponse(initialResponse)) {
94
+ return initialResponse;
95
+ }
96
+ let tokenSetFresh;
97
+ try {
98
+ // "true" here will cause the token to be refreshed
99
+ tokenSetFresh = await this.getTokenSet(true);
100
+ }
101
+ catch (err) {
102
+ return initialResponse;
103
+ }
104
+ // The stream was already consumed. We cannot retry the request. A solution
105
+ // would be to tee() the input stream but that would bufferize the entire
106
+ // stream in memory which can lead to memory starvation. Instead, we will
107
+ // return the original response and let the calling code handle retries.
108
+ if (ReadableStream && init?.body instanceof ReadableStream) {
109
+ return initialResponse;
110
+ }
111
+ const finalAuth = `${tokenSetFresh.token_type} ${tokenSetFresh.access_token}`;
112
+ const finalUrl = new URL(pathname, tokenSetFresh.aud);
113
+ headers.set('Authorization', finalAuth);
114
+ const finalResponse = await this.dpopFetch(finalUrl, { ...init, headers });
115
+ // There is no need to keep the session in the store if the token is expired
116
+ // and there is no way to refresh it.
117
+ if (isTokenExpiredResponse(finalResponse)) {
118
+ // TODO: Is there a "softer" way to handle this, e.g. by marking the
119
+ // session as "expired" and allow the user to trigger a new login?
120
+ await this.sessionGetter.delStored(this.sub);
121
+ }
122
+ return finalResponse;
123
+ }
124
+ }
125
+ exports.OAuthAgent = OAuthAgent;
126
+ /**
127
+ * @see {@link https://datatracker.ietf.org/doc/html/rfc6750#section-3}
128
+ * @see {@link https://datatracker.ietf.org/doc/html/rfc9449#name-resource-server-provided-no}
129
+ */
130
+ function isTokenExpiredResponse(response) {
131
+ if (response.status !== 401)
132
+ return false;
133
+ const wwwAuth = response.headers.get('WWW-Authenticate');
134
+ return (wwwAuth != null &&
135
+ (wwwAuth.startsWith('Bearer ') || wwwAuth.startsWith('DPoP ')) &&
136
+ wwwAuth.includes('error="invalid_token"'));
137
+ }
138
+ //# sourceMappingURL=oauth-agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-agent.js","sourceRoot":"","sources":["../src/oauth-agent.ts"],"names":[],"mappings":"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"}