@atproto/oauth-client 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +20 -0
- package/LICENSE.txt +7 -0
- package/README.md +124 -0
- package/dist/constants.d.ts +5 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +8 -0
- package/dist/constants.js.map +1 -0
- package/dist/fetch-dpop.d.ts +21 -0
- package/dist/fetch-dpop.d.ts.map +1 -0
- package/dist/fetch-dpop.js +149 -0
- package/dist/fetch-dpop.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/lock.d.ts +2 -0
- package/dist/lock.d.ts.map +1 -0
- package/dist/lock.js +33 -0
- package/dist/lock.js.map +1 -0
- package/dist/oauth-agent.d.ts +29 -0
- package/dist/oauth-agent.d.ts.map +1 -0
- package/dist/oauth-agent.js +138 -0
- package/dist/oauth-agent.js.map +1 -0
- package/dist/oauth-authorization-server-metadata-resolver.d.ts +15 -0
- package/dist/oauth-authorization-server-metadata-resolver.d.ts.map +1 -0
- package/dist/oauth-authorization-server-metadata-resolver.js +56 -0
- package/dist/oauth-authorization-server-metadata-resolver.js.map +1 -0
- package/dist/oauth-callback-error.d.ts +7 -0
- package/dist/oauth-callback-error.d.ts.map +1 -0
- package/dist/oauth-callback-error.js +28 -0
- package/dist/oauth-callback-error.js.map +1 -0
- package/dist/oauth-client.d.ts +78 -0
- package/dist/oauth-client.d.ts.map +1 -0
- package/dist/oauth-client.js +278 -0
- package/dist/oauth-client.js.map +1 -0
- package/dist/oauth-protected-resource-metadata-resolver.d.ts +15 -0
- package/dist/oauth-protected-resource-metadata-resolver.d.ts.map +1 -0
- package/dist/oauth-protected-resource-metadata-resolver.js +58 -0
- package/dist/oauth-protected-resource-metadata-resolver.js.map +1 -0
- package/dist/oauth-resolver-error.d.ts +7 -0
- package/dist/oauth-resolver-error.d.ts.map +1 -0
- package/dist/oauth-resolver-error.js +17 -0
- package/dist/oauth-resolver-error.js.map +1 -0
- package/dist/oauth-resolver.d.ts +62 -0
- package/dist/oauth-resolver.d.ts.map +1 -0
- package/dist/oauth-resolver.js +73 -0
- package/dist/oauth-resolver.js.map +1 -0
- package/dist/oauth-response-error.d.ts +11 -0
- package/dist/oauth-response-error.d.ts.map +1 -0
- package/dist/oauth-response-error.js +48 -0
- package/dist/oauth-response-error.js.map +1 -0
- package/dist/oauth-server-agent.d.ts +51 -0
- package/dist/oauth-server-agent.d.ts.map +1 -0
- package/dist/oauth-server-agent.js +228 -0
- package/dist/oauth-server-agent.js.map +1 -0
- package/dist/oauth-server-factory.d.ts +20 -0
- package/dist/oauth-server-factory.d.ts.map +1 -0
- package/dist/oauth-server-factory.js +53 -0
- package/dist/oauth-server-factory.js.map +1 -0
- package/dist/refresh-error.d.ts +7 -0
- package/dist/refresh-error.d.ts.map +1 -0
- package/dist/refresh-error.js +16 -0
- package/dist/refresh-error.js.map +1 -0
- package/dist/runtime-implementation.d.ts +12 -0
- package/dist/runtime-implementation.d.ts.map +1 -0
- package/dist/runtime-implementation.js +3 -0
- package/dist/runtime-implementation.js.map +1 -0
- package/dist/runtime.d.ts +35 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +185 -0
- package/dist/runtime.js.map +1 -0
- package/dist/session-getter.d.ts +30 -0
- package/dist/session-getter.d.ts.map +1 -0
- package/dist/session-getter.js +149 -0
- package/dist/session-getter.js.map +1 -0
- package/dist/types.d.ts +1580 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/dist/util.d.ts +9 -0
- package/dist/util.d.ts.map +1 -0
- package/dist/util.js +35 -0
- package/dist/util.js.map +1 -0
- package/dist/validate-client-metadata.d.ts +5 -0
- package/dist/validate-client-metadata.d.ts.map +1 -0
- package/dist/validate-client-metadata.js +46 -0
- package/dist/validate-client-metadata.js.map +1 -0
- package/package.json +46 -0
- package/src/constants.ts +4 -0
- package/src/fetch-dpop.ts +235 -0
- package/src/index.ts +18 -0
- package/src/lock.ts +34 -0
- package/src/oauth-agent.ts +150 -0
- package/src/oauth-authorization-server-metadata-resolver.ts +98 -0
- package/src/oauth-callback-error.ts +16 -0
- package/src/oauth-client.ts +440 -0
- package/src/oauth-protected-resource-metadata-resolver.ts +102 -0
- package/src/oauth-resolver-error.ts +12 -0
- package/src/oauth-resolver.ts +111 -0
- package/src/oauth-response-error.ts +31 -0
- package/src/oauth-server-agent.ts +275 -0
- package/src/oauth-server-factory.ts +41 -0
- package/src/refresh-error.ts +9 -0
- package/src/runtime-implementation.ts +17 -0
- package/src/runtime.ts +211 -0
- package/src/session-getter.ts +182 -0
- package/src/types.ts +26 -0
- package/src/util.ts +51 -0
- package/src/validate-client-metadata.ts +61 -0
- package/tsconfig.build.json +8 -0
- 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 @@
|
|
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 @@
|
|
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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"}
|
package/dist/index.d.ts
ADDED
@@ -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 @@
|
|
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
|
package/dist/lock.js.map
ADDED
@@ -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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"}
|