@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
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"runtime-implementation.d.ts","sourceRoot":"","sources":["../src/runtime-implementation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AAElC,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAA;CACrC,CAAA;AAED,YAAY,EAAE,GAAG,EAAE,CAAA;AAEnB,MAAM,WAAW,qBAAqB;IACpC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAG,GAAG,WAAW,CAAC,GAAG,CAAC,CAAA;IACjD,eAAe,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,UAAU,GAAG,WAAW,CAAC,UAAU,CAAC,CAAA;IACzE,MAAM,EAAE,CACN,KAAK,EAAE,UAAU,EACjB,SAAS,EAAE,eAAe,KACvB,UAAU,GAAG,WAAW,CAAC,UAAU,CAAC,CAAA;IACzC,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAA;CAC5E"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"runtime-implementation.js","sourceRoot":"","sources":["../src/runtime-implementation.ts"],"names":[],"mappings":""}
|
@@ -0,0 +1,35 @@
|
|
1
|
+
import { JwtHeader, JwtPayload, Key } from '@atproto/jwk';
|
2
|
+
import { RuntimeImplementation } from './runtime-implementation.js';
|
3
|
+
export declare class Runtime {
|
4
|
+
protected implementation: RuntimeImplementation;
|
5
|
+
constructor(implementation: RuntimeImplementation);
|
6
|
+
generateKey(algs: string[]): Promise<Key>;
|
7
|
+
sha256(text: string): Promise<string>;
|
8
|
+
generateNonce(length?: number): Promise<string>;
|
9
|
+
get hasLock(): boolean;
|
10
|
+
withLock<T>(name: string, fn: () => T | PromiseLike<T>): Promise<T>;
|
11
|
+
validateIdTokenClaims(token: string, state: string, nonce: string, code?: string, accessToken?: string): Promise<{
|
12
|
+
header: JwtHeader;
|
13
|
+
payload: JwtPayload;
|
14
|
+
}>;
|
15
|
+
private validateHashClaim;
|
16
|
+
protected generateHashClaim(source: string, header: {
|
17
|
+
alg: string;
|
18
|
+
crv?: string;
|
19
|
+
}): Promise<string>;
|
20
|
+
generatePKCE(byteLength?: number): Promise<{
|
21
|
+
verifier: string;
|
22
|
+
challenge: string;
|
23
|
+
method: string;
|
24
|
+
}>;
|
25
|
+
calculateJwkThumbprint(jwk: any): Promise<string>;
|
26
|
+
/**
|
27
|
+
* @see {@link https://datatracker.ietf.org/doc/html/rfc7636#section-4.1}
|
28
|
+
* @note It is RECOMMENDED that the output of a suitable random number generator
|
29
|
+
* be used to create a 32-octet sequence. The octet sequence is then
|
30
|
+
* base64url-encoded to produce a 43-octet URL safe string to use as the code
|
31
|
+
* verifier.
|
32
|
+
*/
|
33
|
+
protected generateVerifier(byteLength?: number): Promise<string>;
|
34
|
+
}
|
35
|
+
//# sourceMappingURL=runtime.d.ts.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,EAAmB,MAAM,cAAc,CAAA;AAI1E,OAAO,EAEL,qBAAqB,EACtB,MAAM,6BAA6B,CAAA;AAEpC,qBAAa,OAAO;IACN,SAAS,CAAC,cAAc,EAAE,qBAAqB;gBAArC,cAAc,EAAE,qBAAqB;IAE9C,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC;IAKzC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAMrC,aAAa,CAAC,MAAM,SAAK,GAAG,OAAO,CAAC,MAAM,CAAC;IAKxD,IAAI,OAAO,YAEV;IAEY,QAAQ,CAAC,CAAC,EACrB,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,GAC3B,OAAO,CAAC,CAAC,CAAC;IASA,qBAAqB,CAChC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE,MAAM,EACb,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC;QACT,MAAM,EAAE,SAAS,CAAA;QACjB,OAAO,EAAE,UAAU,CAAA;KACpB,CAAC;YAoBY,iBAAiB;cAiBf,iBAAiB,CAC/B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE;IAU1B,YAAY,CAAC,UAAU,CAAC,EAAE,MAAM;;;;;IAShC,sBAAsB,CAAC,GAAG,KAAA;IAMvC;;;;;;OAMG;cACa,gBAAgB,CAAC,UAAU,SAAK;CAOjD"}
|
package/dist/runtime.js
ADDED
@@ -0,0 +1,185 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.Runtime = void 0;
|
4
|
+
const jwk_1 = require("@atproto/jwk");
|
5
|
+
const base64_1 = require("multiformats/bases/base64");
|
6
|
+
const lock_js_1 = require("./lock.js");
|
7
|
+
class Runtime {
|
8
|
+
constructor(implementation) {
|
9
|
+
Object.defineProperty(this, "implementation", {
|
10
|
+
enumerable: true,
|
11
|
+
configurable: true,
|
12
|
+
writable: true,
|
13
|
+
value: implementation
|
14
|
+
});
|
15
|
+
}
|
16
|
+
async generateKey(algs) {
|
17
|
+
const algsSorted = Array.from(algs).sort(compareAlgos);
|
18
|
+
return this.implementation.createKey(algsSorted);
|
19
|
+
}
|
20
|
+
async sha256(text) {
|
21
|
+
const bytes = new TextEncoder().encode(text);
|
22
|
+
const digest = await this.implementation.digest(bytes, { name: 'sha256' });
|
23
|
+
return base64_1.base64url.baseEncode(digest);
|
24
|
+
}
|
25
|
+
async generateNonce(length = 16) {
|
26
|
+
const bytes = await this.implementation.getRandomValues(length);
|
27
|
+
return base64_1.base64url.baseEncode(bytes);
|
28
|
+
}
|
29
|
+
get hasLock() {
|
30
|
+
return !!this.implementation.requestLock;
|
31
|
+
}
|
32
|
+
async withLock(name, fn) {
|
33
|
+
if (this.implementation.requestLock) {
|
34
|
+
return this.implementation.requestLock(name, fn);
|
35
|
+
}
|
36
|
+
else {
|
37
|
+
// Falling back to a local lock
|
38
|
+
return (0, lock_js_1.requestLocalLock)(name, fn);
|
39
|
+
}
|
40
|
+
}
|
41
|
+
async validateIdTokenClaims(token, state, nonce, code, accessToken) {
|
42
|
+
// It's fine to use unsafeDecodeJwt here because the token was received from
|
43
|
+
// the server's token endpoint. The following checks are to ensure that the
|
44
|
+
// oauth flow was indeed initiated by the client.
|
45
|
+
const { header, payload } = (0, jwk_1.unsafeDecodeJwt)(token);
|
46
|
+
if (!payload.nonce || payload.nonce !== nonce) {
|
47
|
+
throw new TypeError('Nonce mismatch');
|
48
|
+
}
|
49
|
+
if (payload.c_hash) {
|
50
|
+
await this.validateHashClaim(payload.c_hash, code, header);
|
51
|
+
}
|
52
|
+
if (payload.s_hash) {
|
53
|
+
await this.validateHashClaim(payload.s_hash, state, header);
|
54
|
+
}
|
55
|
+
if (payload.at_hash) {
|
56
|
+
await this.validateHashClaim(payload.at_hash, accessToken, header);
|
57
|
+
}
|
58
|
+
return { header, payload };
|
59
|
+
}
|
60
|
+
async validateHashClaim(claim, source, header) {
|
61
|
+
if (typeof claim !== 'string' || !claim) {
|
62
|
+
throw new TypeError(`string "_hash" claim expected`);
|
63
|
+
}
|
64
|
+
if (typeof source !== 'string' || !source) {
|
65
|
+
throw new TypeError(`string value expected`);
|
66
|
+
}
|
67
|
+
const expected = await this.generateHashClaim(source, header);
|
68
|
+
if (expected !== claim) {
|
69
|
+
throw new TypeError(`"_hash" does not match`);
|
70
|
+
}
|
71
|
+
}
|
72
|
+
async generateHashClaim(source, header) {
|
73
|
+
const algo = getHashAlgo(header);
|
74
|
+
const bytes = new TextEncoder().encode(source);
|
75
|
+
const digest = await this.implementation.digest(bytes, algo);
|
76
|
+
if (digest.length % 2 !== 0)
|
77
|
+
throw new TypeError('Invalid digest length');
|
78
|
+
const digestHalf = digest.slice(0, digest.length / 2);
|
79
|
+
return base64_1.base64url.baseEncode(digestHalf);
|
80
|
+
}
|
81
|
+
async generatePKCE(byteLength) {
|
82
|
+
const verifier = await this.generateVerifier(byteLength);
|
83
|
+
return {
|
84
|
+
verifier,
|
85
|
+
challenge: await this.sha256(verifier),
|
86
|
+
method: 'S256',
|
87
|
+
};
|
88
|
+
}
|
89
|
+
async calculateJwkThumbprint(jwk) {
|
90
|
+
const components = extractJktComponents(jwk);
|
91
|
+
const data = JSON.stringify(components);
|
92
|
+
return this.sha256(data);
|
93
|
+
}
|
94
|
+
/**
|
95
|
+
* @see {@link https://datatracker.ietf.org/doc/html/rfc7636#section-4.1}
|
96
|
+
* @note It is RECOMMENDED that the output of a suitable random number generator
|
97
|
+
* be used to create a 32-octet sequence. The octet sequence is then
|
98
|
+
* base64url-encoded to produce a 43-octet URL safe string to use as the code
|
99
|
+
* verifier.
|
100
|
+
*/
|
101
|
+
async generateVerifier(byteLength = 32) {
|
102
|
+
if (byteLength < 32 || byteLength > 96) {
|
103
|
+
throw new TypeError('Invalid code_verifier length');
|
104
|
+
}
|
105
|
+
const bytes = await this.implementation.getRandomValues(byteLength);
|
106
|
+
return base64_1.base64url.baseEncode(bytes);
|
107
|
+
}
|
108
|
+
}
|
109
|
+
exports.Runtime = Runtime;
|
110
|
+
function getHashAlgo(header) {
|
111
|
+
switch (header.alg) {
|
112
|
+
case 'HS256':
|
113
|
+
case 'RS256':
|
114
|
+
case 'PS256':
|
115
|
+
case 'ES256':
|
116
|
+
case 'ES256K':
|
117
|
+
return { name: 'sha256' };
|
118
|
+
case 'HS384':
|
119
|
+
case 'RS384':
|
120
|
+
case 'PS384':
|
121
|
+
case 'ES384':
|
122
|
+
return { name: 'sha384' };
|
123
|
+
case 'HS512':
|
124
|
+
case 'RS512':
|
125
|
+
case 'PS512':
|
126
|
+
case 'ES512':
|
127
|
+
return { name: 'sha512' };
|
128
|
+
case 'EdDSA':
|
129
|
+
switch (header.crv) {
|
130
|
+
case 'Ed25519':
|
131
|
+
return { name: 'sha512' };
|
132
|
+
default:
|
133
|
+
throw new TypeError('unrecognized or invalid EdDSA curve provided');
|
134
|
+
}
|
135
|
+
default:
|
136
|
+
throw new TypeError('unrecognized or invalid JWS algorithm provided');
|
137
|
+
}
|
138
|
+
}
|
139
|
+
function extractJktComponents(jwk) {
|
140
|
+
const get = (field) => {
|
141
|
+
const value = jwk[field];
|
142
|
+
if (typeof value !== 'string' || !value) {
|
143
|
+
throw new TypeError(`"${field}" Parameter missing or invalid`);
|
144
|
+
}
|
145
|
+
return value;
|
146
|
+
};
|
147
|
+
switch (jwk.kty) {
|
148
|
+
case 'EC':
|
149
|
+
return { crv: get('crv'), kty: get('kty'), x: get('x'), y: get('y') };
|
150
|
+
case 'OKP':
|
151
|
+
return { crv: get('crv'), kty: get('kty'), x: get('x') };
|
152
|
+
case 'RSA':
|
153
|
+
return { e: get('e'), kty: get('kty'), n: get('n') };
|
154
|
+
case 'oct':
|
155
|
+
return { k: get('k'), kty: get('kty') };
|
156
|
+
default:
|
157
|
+
throw new TypeError('"kty" (Key Type) Parameter missing or unsupported');
|
158
|
+
}
|
159
|
+
}
|
160
|
+
/**
|
161
|
+
* 256K > ES (256 > 384 > 512) > PS (256 > 384 > 512) > RS (256 > 384 > 512) > other (in original order)
|
162
|
+
*/
|
163
|
+
function compareAlgos(a, b) {
|
164
|
+
if (a === 'ES256K')
|
165
|
+
return -1;
|
166
|
+
if (b === 'ES256K')
|
167
|
+
return 1;
|
168
|
+
for (const prefix of ['ES', 'PS', 'RS']) {
|
169
|
+
if (a.startsWith(prefix)) {
|
170
|
+
if (b.startsWith(prefix)) {
|
171
|
+
const aLen = parseInt(a.slice(2, 5));
|
172
|
+
const bLen = parseInt(b.slice(2, 5));
|
173
|
+
// Prefer shorter key lengths
|
174
|
+
return aLen - bLen;
|
175
|
+
}
|
176
|
+
return -1;
|
177
|
+
}
|
178
|
+
else if (b.startsWith(prefix)) {
|
179
|
+
return 1;
|
180
|
+
}
|
181
|
+
}
|
182
|
+
// Don't know how to compare, keep original order
|
183
|
+
return 0;
|
184
|
+
}
|
185
|
+
//# sourceMappingURL=runtime.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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"}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import { CachedGetter, GetCachedOptions, SimpleStore } from '@atproto-labs/simple-store';
|
2
|
+
import { Key } from '@atproto/jwk';
|
3
|
+
import { TokenSet } from './oauth-server-agent.js';
|
4
|
+
import { OAuthServerFactory } from './oauth-server-factory.js';
|
5
|
+
import { Runtime } from './runtime.js';
|
6
|
+
export type Session = {
|
7
|
+
dpopKey: Key;
|
8
|
+
tokenSet: TokenSet;
|
9
|
+
};
|
10
|
+
export type SessionStore = SimpleStore<string, Session>;
|
11
|
+
/**
|
12
|
+
* There are several advantages to wrapping the sessionStore in a (single)
|
13
|
+
* CachedGetter, the main of which is that the cached getter will ensure that at
|
14
|
+
* most one fresh call is ever being made. Another advantage, is that it
|
15
|
+
* contains the logic for reading from the cache which, if the cache is based on
|
16
|
+
* localStorage/indexedDB, will sync across multiple tabs (for a given sub).
|
17
|
+
*/
|
18
|
+
export declare class SessionGetter extends CachedGetter<string, Session> {
|
19
|
+
private readonly runtime;
|
20
|
+
constructor(sessionStore: SessionStore, serverFactory: OAuthServerFactory, runtime: Runtime);
|
21
|
+
/**
|
22
|
+
* @param refresh When `true`, the credentials will be refreshed even if they
|
23
|
+
* are not expired. When `false`, the credentials will not be refreshed even
|
24
|
+
* if they are expired. When `undefined`, the credentials will be refreshed
|
25
|
+
* if, and only if, they are (about to be) expired. Defaults to `undefined`.
|
26
|
+
*/
|
27
|
+
getSession(sub: string, refresh?: boolean): Promise<Session>;
|
28
|
+
get(sub: string, options?: GetCachedOptions): Promise<Session>;
|
29
|
+
}
|
30
|
+
//# sourceMappingURL=session-getter.d.ts.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"session-getter.d.ts","sourceRoot":"","sources":["../src/session-getter.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,WAAW,EACZ,MAAM,4BAA4B,CAAA;AACnC,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AAElC,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAE9D,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAGtC,MAAM,MAAM,OAAO,GAAG;IACpB,OAAO,EAAE,GAAG,CAAA;IACZ,QAAQ,EAAE,QAAQ,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,YAAY,GAAG,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAEvD;;;;;;GAMG;AACH,qBAAa,aAAc,SAAQ,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC;IAI5D,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAFxB,YAAY,EAAE,YAAY,EAC1B,aAAa,EAAE,kBAAkB,EAChB,OAAO,EAAE,OAAO;IAyHnC;;;;;OAKG;IACG,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO;IAczC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;CASrE"}
|
@@ -0,0 +1,149 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.SessionGetter = void 0;
|
4
|
+
const simple_store_1 = require("@atproto-labs/simple-store");
|
5
|
+
const oauth_response_error_js_1 = require("./oauth-response-error.js");
|
6
|
+
const refresh_error_js_1 = require("./refresh-error.js");
|
7
|
+
const util_js_1 = require("./util.js");
|
8
|
+
/**
|
9
|
+
* There are several advantages to wrapping the sessionStore in a (single)
|
10
|
+
* CachedGetter, the main of which is that the cached getter will ensure that at
|
11
|
+
* most one fresh call is ever being made. Another advantage, is that it
|
12
|
+
* contains the logic for reading from the cache which, if the cache is based on
|
13
|
+
* localStorage/indexedDB, will sync across multiple tabs (for a given sub).
|
14
|
+
*/
|
15
|
+
class SessionGetter extends simple_store_1.CachedGetter {
|
16
|
+
constructor(sessionStore, serverFactory, runtime) {
|
17
|
+
super(async (sub, options, storedSession) => {
|
18
|
+
// There needs to be a previous session to be able to refresh. If
|
19
|
+
// storedSession is undefined, it means that the store does not contain
|
20
|
+
// a session for the given sub. Since this might have been caused by the
|
21
|
+
// value being cleared in another process (e.g. another tab), we will
|
22
|
+
// give a chance to the process running this code to detect that the
|
23
|
+
// session was revoked. This should allow processes not implementing a
|
24
|
+
// subscribe/notify between instances to still be "notified" that the
|
25
|
+
// session was revoked.
|
26
|
+
if (storedSession === undefined) {
|
27
|
+
// Because the session is not in the store, the sessionStore.del
|
28
|
+
// function will not be called, even if the "deleteOnError" callback
|
29
|
+
// returns true when the error is an "OAuthRefreshError". Let's
|
30
|
+
// call it here manually.
|
31
|
+
await sessionStore.del(sub);
|
32
|
+
throw new refresh_error_js_1.RefreshError(sub, 'The session was revoked');
|
33
|
+
}
|
34
|
+
if (sub !== storedSession.tokenSet.sub) {
|
35
|
+
// Fool-proofing (e.g. against invalid session storage)
|
36
|
+
throw new refresh_error_js_1.RefreshError(sub, 'Stored session sub mismatch');
|
37
|
+
}
|
38
|
+
// Since refresh tokens can only be used once, we might run into
|
39
|
+
// concurrency issues if multiple tabs/instances are trying to refresh
|
40
|
+
// the same token. The chances of this happening when multiple instances
|
41
|
+
// are started simultaneously is reduced by randomizing the expiry time
|
42
|
+
// (see isStale() bellow). Even so, There still exist chances that
|
43
|
+
// multiple tabs will try to refresh the token at the same time. The
|
44
|
+
// best solution would be to use a mutex/lock to ensure that only one
|
45
|
+
// instance is refreshing the token at a time. A simpler workaround is
|
46
|
+
// to check if the value stored in the session store is the same as the
|
47
|
+
// one in memory. If it isn't, then another instance has already
|
48
|
+
// refreshed the token.
|
49
|
+
const { tokenSet, dpopKey } = storedSession;
|
50
|
+
const server = await serverFactory.fromIssuer(tokenSet.iss, dpopKey);
|
51
|
+
// We must not use the "signal" to cancel the refresh or its storage in
|
52
|
+
// case of successful refresh. If we obtain a new refresh token, we must
|
53
|
+
// ensure that is gets stored in the session store (by returning the new
|
54
|
+
// session object). Failing to do so would result in the new credentials
|
55
|
+
// being lost.
|
56
|
+
options?.signal?.throwIfAborted();
|
57
|
+
const newTokenSet = await server
|
58
|
+
.refresh(tokenSet)
|
59
|
+
.catch(async (cause) => {
|
60
|
+
if (cause instanceof oauth_response_error_js_1.OAuthResponseError &&
|
61
|
+
cause.status === 400 &&
|
62
|
+
cause.error === 'invalid_grant') {
|
63
|
+
// In case there is no lock implementation in the runtime, we will
|
64
|
+
// wait for a short time to give the other concurrent instances a
|
65
|
+
// chance to finish their refreshing of the token. If a concurrent
|
66
|
+
// refresh did occur, we will pretend that this one succeeded.
|
67
|
+
if (!runtime.hasLock) {
|
68
|
+
await new Promise((r) => setTimeout(r, 1000));
|
69
|
+
const stored = await this.getStored(sub);
|
70
|
+
if (stored === undefined) {
|
71
|
+
// Using a distinct error message mainly for debugging
|
72
|
+
// purposes
|
73
|
+
const msg = 'The session was revoked by another process';
|
74
|
+
throw new refresh_error_js_1.RefreshError(sub, msg, { cause });
|
75
|
+
}
|
76
|
+
else if (stored.tokenSet.access_token !== tokenSet.access_token ||
|
77
|
+
stored.tokenSet.refresh_token !== tokenSet.refresh_token) {
|
78
|
+
// A concurrent refresh occurred. Pretend this one succeeded.
|
79
|
+
return stored.tokenSet;
|
80
|
+
}
|
81
|
+
else {
|
82
|
+
// There were no concurrent refresh. The token is (likely)
|
83
|
+
// simply no longer valid.
|
84
|
+
}
|
85
|
+
}
|
86
|
+
// Throwing an RefreshError to trigger deletion through the
|
87
|
+
// deleteOnError callback.
|
88
|
+
const msg = cause.errorDescription ?? 'The session was revoked';
|
89
|
+
throw new refresh_error_js_1.RefreshError(sub, msg, { cause });
|
90
|
+
}
|
91
|
+
throw cause;
|
92
|
+
});
|
93
|
+
if (sub !== newTokenSet.sub) {
|
94
|
+
// The server returned another sub. Was the tokenSet manipulated?
|
95
|
+
throw new refresh_error_js_1.RefreshError(sub, 'Token set sub mismatch');
|
96
|
+
}
|
97
|
+
return { ...storedSession, tokenSet: newTokenSet };
|
98
|
+
}, sessionStore, {
|
99
|
+
isStale: (sub, { tokenSet }) => {
|
100
|
+
return (tokenSet.expires_at != null &&
|
101
|
+
new Date(tokenSet.expires_at).getTime() <
|
102
|
+
// Add some lee way to ensure the token is not expired when it
|
103
|
+
// reaches the server.
|
104
|
+
Date.now() + 60e3);
|
105
|
+
},
|
106
|
+
onStoreError: async (err, sub, { tokenSet, dpopKey }) => {
|
107
|
+
// If the token data cannot be stored, let's revoke it
|
108
|
+
const server = await serverFactory.fromIssuer(tokenSet.iss, dpopKey);
|
109
|
+
await server.revoke(tokenSet.refresh_token ?? tokenSet.access_token);
|
110
|
+
throw err;
|
111
|
+
},
|
112
|
+
deleteOnError: async (err) => {
|
113
|
+
return err instanceof refresh_error_js_1.RefreshError;
|
114
|
+
},
|
115
|
+
});
|
116
|
+
Object.defineProperty(this, "runtime", {
|
117
|
+
enumerable: true,
|
118
|
+
configurable: true,
|
119
|
+
writable: true,
|
120
|
+
value: runtime
|
121
|
+
});
|
122
|
+
}
|
123
|
+
/**
|
124
|
+
* @param refresh When `true`, the credentials will be refreshed even if they
|
125
|
+
* are not expired. When `false`, the credentials will not be refreshed even
|
126
|
+
* if they are expired. When `undefined`, the credentials will be refreshed
|
127
|
+
* if, and only if, they are (about to be) expired. Defaults to `undefined`.
|
128
|
+
*/
|
129
|
+
async getSession(sub, refresh) {
|
130
|
+
const session = await this.get(sub, {
|
131
|
+
noCache: refresh === true,
|
132
|
+
allowStale: refresh === false,
|
133
|
+
});
|
134
|
+
if (sub !== session.tokenSet.sub) {
|
135
|
+
// Fool-proofing (e.g. against invalid session storage)
|
136
|
+
throw new Error('Token set does not match the expected sub');
|
137
|
+
}
|
138
|
+
return session;
|
139
|
+
}
|
140
|
+
async get(sub, options) {
|
141
|
+
return this.runtime.withLock(`@atproto-oauth-client-${sub}`, async () => {
|
142
|
+
// Make sure, even if there is no signal in the options, that the request
|
143
|
+
// will be cancelled after at most 30 seconds.
|
144
|
+
return (0, util_js_1.withSignal)({ signal: options?.signal, timeout: 30e3 }, (signal) => super.get(sub, { ...options, signal }));
|
145
|
+
});
|
146
|
+
}
|
147
|
+
}
|
148
|
+
exports.SessionGetter = SessionGetter;
|
149
|
+
//# sourceMappingURL=session-getter.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"session-getter.js","sourceRoot":"","sources":["../src/session-getter.ts"],"names":[],"mappings":"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"}
|