@atproto/oauth-client 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +15 -0
- package/README.md +162 -32
- package/dist/errors/token-invalid-error.d.ts +7 -0
- package/dist/errors/token-invalid-error.d.ts.map +1 -0
- package/dist/errors/token-invalid-error.js +16 -0
- package/dist/errors/token-invalid-error.js.map +1 -0
- package/dist/errors/token-refresh-error.d.ts +7 -0
- package/dist/errors/token-refresh-error.d.ts.map +1 -0
- package/dist/errors/token-refresh-error.js +16 -0
- package/dist/errors/token-refresh-error.js.map +1 -0
- package/dist/errors/token-revoked-error.d.ts +7 -0
- package/dist/errors/token-revoked-error.d.ts.map +1 -0
- package/dist/errors/token-revoked-error.js +16 -0
- package/dist/errors/token-revoked-error.js.map +1 -0
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/dist/lock.d.ts +2 -1
- package/dist/lock.d.ts.map +1 -1
- package/dist/lock.js +2 -2
- package/dist/lock.js.map +1 -1
- package/dist/oauth-agent.d.ts.map +1 -1
- package/dist/oauth-agent.js +14 -9
- package/dist/oauth-agent.js.map +1 -1
- package/dist/oauth-client.d.ts +250 -20
- package/dist/oauth-client.d.ts.map +1 -1
- package/dist/oauth-client.js +67 -9
- package/dist/oauth-client.js.map +1 -1
- package/dist/oauth-resolver.d.ts +5 -4
- package/dist/oauth-resolver.d.ts.map +1 -1
- package/dist/oauth-resolver.js.map +1 -1
- package/dist/oauth-server-agent.d.ts.map +1 -1
- package/dist/oauth-server-agent.js +85 -29
- package/dist/oauth-server-agent.js.map +1 -1
- package/dist/runtime-implementation.d.ts +10 -5
- package/dist/runtime-implementation.d.ts.map +1 -1
- package/dist/runtime.d.ts +3 -3
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +18 -12
- package/dist/runtime.js.map +1 -1
- package/dist/session-getter.d.ts +19 -0
- package/dist/session-getter.d.ts.map +1 -1
- package/dist/session-getter.js +134 -42
- package/dist/session-getter.js.map +1 -1
- package/dist/state-store.d.ts +11 -0
- package/dist/state-store.d.ts.map +1 -0
- package/dist/state-store.js +3 -0
- package/dist/state-store.js.map +1 -0
- package/dist/types.d.ts +3 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/util.d.ts +10 -3
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +43 -23
- package/dist/util.js.map +1 -1
- package/dist/validate-client-metadata.d.ts.map +1 -1
- package/dist/validate-client-metadata.js +17 -0
- package/dist/validate-client-metadata.js.map +1 -1
- package/package.json +8 -8
- package/src/errors/token-invalid-error.ts +9 -0
- package/src/errors/token-refresh-error.ts +9 -0
- package/src/errors/token-revoked-error.ts +9 -0
- package/src/index.ts +11 -1
- package/src/lock.ts +3 -4
- package/src/oauth-agent.ts +20 -9
- package/src/oauth-client.ts +113 -31
- package/src/oauth-resolver.ts +4 -4
- package/src/oauth-server-agent.ts +9 -9
- package/src/runtime-implementation.ts +19 -11
- package/src/runtime.ts +13 -17
- package/src/session-getter.ts +135 -71
- package/src/state-store.ts +12 -0
- package/src/types.ts +5 -2
- package/src/util.ts +63 -32
- package/src/validate-client-metadata.ts +18 -0
@@ -1,12 +1,57 @@
|
|
1
1
|
"use strict";
|
2
|
+
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
|
3
|
+
if (value !== null && value !== void 0) {
|
4
|
+
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
5
|
+
var dispose;
|
6
|
+
if (async) {
|
7
|
+
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
8
|
+
dispose = value[Symbol.asyncDispose];
|
9
|
+
}
|
10
|
+
if (dispose === void 0) {
|
11
|
+
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
12
|
+
dispose = value[Symbol.dispose];
|
13
|
+
}
|
14
|
+
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
15
|
+
env.stack.push({ value: value, dispose: dispose, async: async });
|
16
|
+
}
|
17
|
+
else if (async) {
|
18
|
+
env.stack.push({ async: true });
|
19
|
+
}
|
20
|
+
return value;
|
21
|
+
};
|
22
|
+
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
|
23
|
+
return function (env) {
|
24
|
+
function fail(e) {
|
25
|
+
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
26
|
+
env.hasError = true;
|
27
|
+
}
|
28
|
+
function next() {
|
29
|
+
while (env.stack.length) {
|
30
|
+
var rec = env.stack.pop();
|
31
|
+
try {
|
32
|
+
var result = rec.dispose && rec.dispose.call(rec.value);
|
33
|
+
if (rec.async) return Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
|
34
|
+
}
|
35
|
+
catch (e) {
|
36
|
+
fail(e);
|
37
|
+
}
|
38
|
+
}
|
39
|
+
if (env.hasError) throw env.error;
|
40
|
+
}
|
41
|
+
return next();
|
42
|
+
};
|
43
|
+
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
44
|
+
var e = new Error(message);
|
45
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
46
|
+
});
|
2
47
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
48
|
exports.OAuthServerAgent = void 0;
|
4
49
|
const fetch_1 = require("@atproto-labs/fetch");
|
5
50
|
const oauth_types_1 = require("@atproto/oauth-types");
|
6
51
|
const constants_js_1 = require("./constants.js");
|
52
|
+
const token_refresh_error_js_1 = require("./errors/token-refresh-error.js");
|
7
53
|
const fetch_dpop_js_1 = require("./fetch-dpop.js");
|
8
54
|
const oauth_response_error_js_1 = require("./oauth-response-error.js");
|
9
|
-
const refresh_error_js_1 = require("./refresh-error.js");
|
10
55
|
const util_js_1 = require("./util.js");
|
11
56
|
class OAuthServerAgent {
|
12
57
|
constructor(dpopKey, serverMetadata, clientMetadata, dpopNonces, oauthResolver, runtime, keyset, fetch) {
|
@@ -93,7 +138,7 @@ class OAuthServerAgent {
|
|
93
138
|
}
|
94
139
|
async refresh(tokenSet) {
|
95
140
|
if (!tokenSet.refresh_token) {
|
96
|
-
throw new
|
141
|
+
throw new token_refresh_error_js_1.TokenRefreshError(tokenSet.sub, 'No refresh token available');
|
97
142
|
}
|
98
143
|
const tokenResponse = await this.request('token', {
|
99
144
|
grant_type: 'refresh_token',
|
@@ -101,10 +146,10 @@ class OAuthServerAgent {
|
|
101
146
|
});
|
102
147
|
try {
|
103
148
|
if (tokenSet.sub !== tokenResponse.sub) {
|
104
|
-
throw new
|
149
|
+
throw new token_refresh_error_js_1.TokenRefreshError(tokenSet.sub, `Unexpected "sub" in token response (${tokenResponse.sub})`);
|
105
150
|
}
|
106
151
|
if (tokenSet.iss !== this.serverMetadata.issuer) {
|
107
|
-
throw new
|
152
|
+
throw new token_refresh_error_js_1.TokenRefreshError(tokenSet.sub, 'Issuer mismatch');
|
108
153
|
}
|
109
154
|
return this.processTokenResponse(tokenResponse);
|
110
155
|
}
|
@@ -122,31 +167,42 @@ class OAuthServerAgent {
|
|
122
167
|
* able to use the "sub" (DID) as being the actual user's identifier.
|
123
168
|
*/
|
124
169
|
async processTokenResponse(tokenResponse) {
|
125
|
-
const {
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
:
|
149
|
-
|
170
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
171
|
+
try {
|
172
|
+
const { sub } = tokenResponse;
|
173
|
+
// ATPROTO requires that the "sub" is always present in the token response.
|
174
|
+
if (!sub)
|
175
|
+
throw new TypeError(`Missing "sub" in token response`);
|
176
|
+
// @TODO (?) make timeout configurable
|
177
|
+
const signal = __addDisposableResource(env_1, (0, util_js_1.timeoutSignal)(10e3), false);
|
178
|
+
const resolved = await this.oauthResolver.resolve(sub, { signal });
|
179
|
+
if (resolved.metadata.issuer !== this.serverMetadata.issuer) {
|
180
|
+
// Best case scenario; the user switched PDS. Worst case scenario; a bad
|
181
|
+
// actor is trying to impersonate a user. In any case, we must not allow
|
182
|
+
// this token to be used.
|
183
|
+
throw new TypeError('Issuer mismatch');
|
184
|
+
}
|
185
|
+
return {
|
186
|
+
sub,
|
187
|
+
aud: resolved.identity.pds.href,
|
188
|
+
iss: resolved.metadata.issuer,
|
189
|
+
scope: tokenResponse.scope,
|
190
|
+
id_token: tokenResponse.id_token,
|
191
|
+
refresh_token: tokenResponse.refresh_token,
|
192
|
+
access_token: tokenResponse.access_token,
|
193
|
+
token_type: tokenResponse.token_type ?? 'Bearer',
|
194
|
+
expires_at: typeof tokenResponse.expires_in === 'number'
|
195
|
+
? new Date(Date.now() + tokenResponse.expires_in * 1000).toISOString()
|
196
|
+
: undefined,
|
197
|
+
};
|
198
|
+
}
|
199
|
+
catch (e_1) {
|
200
|
+
env_1.error = e_1;
|
201
|
+
env_1.hasError = true;
|
202
|
+
}
|
203
|
+
finally {
|
204
|
+
__disposeResources(env_1);
|
205
|
+
}
|
150
206
|
}
|
151
207
|
async request(endpoint, payload) {
|
152
208
|
const url = this.serverMetadata[`${endpoint}_endpoint`];
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"oauth-server-agent.js","sourceRoot":"","sources":["../src/oauth-server-agent.ts"],"names":[],"mappings":"
|
1
|
+
{"version":3,"file":"oauth-server-agent.js","sourceRoot":"","sources":["../src/oauth-server-agent.ts"],"names":[],"mappings":"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"}
|
@@ -1,12 +1,17 @@
|
|
1
1
|
import { Key } from '@atproto/jwk';
|
2
|
+
import { Awaitable } from './util.js';
|
3
|
+
export type { Key };
|
4
|
+
export type RuntimeKeyFactory = (algs: string[]) => Key | PromiseLike<Key>;
|
5
|
+
export type RuntimeRandomValues = (length: number) => Awaitable<Uint8Array>;
|
2
6
|
export type DigestAlgorithm = {
|
3
7
|
name: 'sha256' | 'sha384' | 'sha512';
|
4
8
|
};
|
5
|
-
export type
|
9
|
+
export type RuntimeDigest = (data: Uint8Array, alg: DigestAlgorithm) => Awaitable<Uint8Array>;
|
10
|
+
export type RuntimeLock = <T>(name: string, fn: () => Awaitable<T>) => Awaitable<T>;
|
6
11
|
export interface RuntimeImplementation {
|
7
|
-
createKey
|
8
|
-
getRandomValues:
|
9
|
-
digest:
|
10
|
-
requestLock?:
|
12
|
+
createKey: RuntimeKeyFactory;
|
13
|
+
getRandomValues: RuntimeRandomValues;
|
14
|
+
digest: RuntimeDigest;
|
15
|
+
requestLock?: RuntimeLock;
|
11
16
|
}
|
12
17
|
//# sourceMappingURL=runtime-implementation.d.ts.map
|
@@ -1 +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;
|
1
|
+
{"version":3,"file":"runtime-implementation.d.ts","sourceRoot":"","sources":["../src/runtime-implementation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AAClC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAErC,YAAY,EAAE,GAAG,EAAE,CAAA;AACnB,MAAM,MAAM,iBAAiB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,GAAG,GAAG,WAAW,CAAC,GAAG,CAAC,CAAA;AAE1E,MAAM,MAAM,mBAAmB,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,SAAS,CAAC,UAAU,CAAC,CAAA;AAE3E,MAAM,MAAM,eAAe,GAAG;IAAE,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAA;CAAE,CAAA;AACtE,MAAM,MAAM,aAAa,GAAG,CAC1B,IAAI,EAAE,UAAU,EAChB,GAAG,EAAE,eAAe,KACjB,SAAS,CAAC,UAAU,CAAC,CAAA;AAE1B,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,EAC1B,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,SAAS,CAAC,CAAC,CAAC,KACnB,SAAS,CAAC,CAAC,CAAC,CAAA;AAEjB,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,iBAAiB,CAAA;IAC5B,eAAe,EAAE,mBAAmB,CAAA;IACpC,MAAM,EAAE,aAAa,CAAA;IACrB,WAAW,CAAC,EAAE,WAAW,CAAA;CAC1B"}
|
package/dist/runtime.d.ts
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
import { JwtHeader, JwtPayload, Key } from '@atproto/jwk';
|
2
|
-
import { RuntimeImplementation } from './runtime-implementation.js';
|
2
|
+
import { RuntimeImplementation, RuntimeLock } from './runtime-implementation.js';
|
3
3
|
export declare class Runtime {
|
4
4
|
protected implementation: RuntimeImplementation;
|
5
|
+
readonly hasImplementationLock: boolean;
|
6
|
+
readonly usingLock: RuntimeLock;
|
5
7
|
constructor(implementation: RuntimeImplementation);
|
6
8
|
generateKey(algs: string[]): Promise<Key>;
|
7
9
|
sha256(text: string): Promise<string>;
|
8
10
|
generateNonce(length?: number): Promise<string>;
|
9
|
-
get hasLock(): boolean;
|
10
|
-
withLock<T>(name: string, fn: () => T | PromiseLike<T>): Promise<T>;
|
11
11
|
validateIdTokenClaims(token: string, state: string, nonce: string, code?: string, accessToken?: string): Promise<{
|
12
12
|
header: JwtHeader;
|
13
13
|
payload: JwtPayload;
|
package/dist/runtime.d.ts.map
CHANGED
@@ -1 +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,
|
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,EACrB,WAAW,EACZ,MAAM,6BAA6B,CAAA;AAEpC,qBAAa,OAAO;IAIN,SAAS,CAAC,cAAc,EAAE,qBAAqB;IAH3D,QAAQ,CAAC,qBAAqB,EAAE,OAAO,CAAA;IACvC,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAA;gBAET,cAAc,EAAE,qBAAqB;IAU9C,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;IAK3C,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
CHANGED
@@ -12,6 +12,24 @@ class Runtime {
|
|
12
12
|
writable: true,
|
13
13
|
value: implementation
|
14
14
|
});
|
15
|
+
Object.defineProperty(this, "hasImplementationLock", {
|
16
|
+
enumerable: true,
|
17
|
+
configurable: true,
|
18
|
+
writable: true,
|
19
|
+
value: void 0
|
20
|
+
});
|
21
|
+
Object.defineProperty(this, "usingLock", {
|
22
|
+
enumerable: true,
|
23
|
+
configurable: true,
|
24
|
+
writable: true,
|
25
|
+
value: void 0
|
26
|
+
});
|
27
|
+
const { requestLock } = implementation;
|
28
|
+
this.hasImplementationLock = requestLock != null;
|
29
|
+
this.usingLock =
|
30
|
+
requestLock?.bind(implementation) ||
|
31
|
+
// Falling back to a local lock
|
32
|
+
lock_js_1.requestLocalLock;
|
15
33
|
}
|
16
34
|
async generateKey(algs) {
|
17
35
|
const algsSorted = Array.from(algs).sort(compareAlgos);
|
@@ -26,18 +44,6 @@ class Runtime {
|
|
26
44
|
const bytes = await this.implementation.getRandomValues(length);
|
27
45
|
return base64_1.base64url.baseEncode(bytes);
|
28
46
|
}
|
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
47
|
async validateIdTokenClaims(token, state, nonce, code, accessToken) {
|
42
48
|
// It's fine to use unsafeDecodeJwt here because the token was received from
|
43
49
|
// the server's token endpoint. The following checks are to ensure that the
|
package/dist/runtime.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":";;;AAAA,sCAA0E;AAC1E,sDAAqD;AAErD,uCAA4C;
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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"}
|
package/dist/session-getter.d.ts
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
import { CachedGetter, GetCachedOptions, SimpleStore } from '@atproto-labs/simple-store';
|
2
2
|
import { Key } from '@atproto/jwk';
|
3
|
+
import { TokenInvalidError } from './errors/token-invalid-error.js';
|
4
|
+
import { TokenRefreshError } from './errors/token-refresh-error.js';
|
5
|
+
import { TokenRevokedError } from './errors/token-revoked-error.js';
|
3
6
|
import { TokenSet } from './oauth-server-agent.js';
|
4
7
|
import { OAuthServerFactory } from './oauth-server-factory.js';
|
5
8
|
import { Runtime } from './runtime.js';
|
@@ -8,6 +11,16 @@ export type Session = {
|
|
8
11
|
tokenSet: TokenSet;
|
9
12
|
};
|
10
13
|
export type SessionStore = SimpleStore<string, Session>;
|
14
|
+
export type SessionEventMap = {
|
15
|
+
updated: {
|
16
|
+
sub: string;
|
17
|
+
} & Session;
|
18
|
+
deleted: {
|
19
|
+
sub: string;
|
20
|
+
cause: TokenRefreshError | TokenRevokedError | TokenInvalidError | unknown;
|
21
|
+
};
|
22
|
+
};
|
23
|
+
export type SessionEventListener<T extends keyof SessionEventMap = keyof SessionEventMap> = (event: CustomEvent<SessionEventMap[T]>) => void;
|
11
24
|
/**
|
12
25
|
* There are several advantages to wrapping the sessionStore in a (single)
|
13
26
|
* CachedGetter, the main of which is that the cached getter will ensure that at
|
@@ -17,7 +30,13 @@ export type SessionStore = SimpleStore<string, Session>;
|
|
17
30
|
*/
|
18
31
|
export declare class SessionGetter extends CachedGetter<string, Session> {
|
19
32
|
private readonly runtime;
|
33
|
+
private readonly eventTarget;
|
20
34
|
constructor(sessionStore: SessionStore, serverFactory: OAuthServerFactory, runtime: Runtime);
|
35
|
+
addEventListener<T extends keyof SessionEventMap>(type: T, callback: SessionEventListener<T>, options?: AddEventListenerOptions | boolean): void;
|
36
|
+
removeEventListener<T extends keyof SessionEventMap>(type: T, callback: SessionEventListener<T>, options?: EventListenerOptions | boolean): void;
|
37
|
+
dispatchEvent<T extends keyof SessionEventMap>(type: T, detail: SessionEventMap[T]): boolean;
|
38
|
+
setStored(sub: string, session: Session): Promise<void>;
|
39
|
+
delStored(sub: string, cause?: unknown): Promise<void>;
|
21
40
|
/**
|
22
41
|
* @param refresh When `true`, the credentials will be refreshed even if they
|
23
42
|
* are not expired. When `false`, the credentials will not be refreshed even
|
@@ -1 +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;
|
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,iBAAiB,EAAE,MAAM,iCAAiC,CAAA;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAA;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAA;AAEnE,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAC9D,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,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE;QACP,GAAG,EAAE,MAAM,CAAA;KACZ,GAAG,OAAO,CAAA;IACX,OAAO,EAAE;QACP,GAAG,EAAE,MAAM,CAAA;QACX,KAAK,EAAE,iBAAiB,GAAG,iBAAiB,GAAG,iBAAiB,GAAG,OAAO,CAAA;KAC3E,CAAA;CACF,CAAA;AAED,MAAM,MAAM,oBAAoB,CAC9B,CAAC,SAAS,MAAM,eAAe,GAAG,MAAM,eAAe,IACrD,CAAC,KAAK,EAAE,WAAW,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAA;AAEpD;;;;;;GAMG;AACH,qBAAa,aAAc,SAAQ,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC;IAM5D,OAAO,CAAC,QAAQ,CAAC,OAAO;IAL1B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA2C;gBAGrE,YAAY,EAAE,YAAY,EAC1B,aAAa,EAAE,kBAAkB,EAChB,OAAO,EAAE,OAAO;IAqInC,gBAAgB,CAAC,CAAC,SAAS,MAAM,eAAe,EAC9C,IAAI,EAAE,CAAC,EACP,QAAQ,EAAE,oBAAoB,CAAC,CAAC,CAAC,EACjC,OAAO,CAAC,EAAE,uBAAuB,GAAG,OAAO;IAK7C,mBAAmB,CAAC,CAAC,SAAS,MAAM,eAAe,EACjD,IAAI,EAAE,CAAC,EACP,QAAQ,EAAE,oBAAoB,CAAC,CAAC,CAAC,EACjC,OAAO,CAAC,EAAE,oBAAoB,GAAG,OAAO;IAK1C,aAAa,CAAC,CAAC,SAAS,MAAM,eAAe,EAC3C,IAAI,EAAE,CAAC,EACP,MAAM,EAAE,eAAe,CAAC,CAAC,CAAC,GACzB,OAAO;IAIJ,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO;IAKvC,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAK5D;;;;;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"}
|
package/dist/session-getter.js
CHANGED
@@ -1,9 +1,56 @@
|
|
1
1
|
"use strict";
|
2
|
+
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
|
3
|
+
if (value !== null && value !== void 0) {
|
4
|
+
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
5
|
+
var dispose;
|
6
|
+
if (async) {
|
7
|
+
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
8
|
+
dispose = value[Symbol.asyncDispose];
|
9
|
+
}
|
10
|
+
if (dispose === void 0) {
|
11
|
+
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
12
|
+
dispose = value[Symbol.dispose];
|
13
|
+
}
|
14
|
+
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
15
|
+
env.stack.push({ value: value, dispose: dispose, async: async });
|
16
|
+
}
|
17
|
+
else if (async) {
|
18
|
+
env.stack.push({ async: true });
|
19
|
+
}
|
20
|
+
return value;
|
21
|
+
};
|
22
|
+
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
|
23
|
+
return function (env) {
|
24
|
+
function fail(e) {
|
25
|
+
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
26
|
+
env.hasError = true;
|
27
|
+
}
|
28
|
+
function next() {
|
29
|
+
while (env.stack.length) {
|
30
|
+
var rec = env.stack.pop();
|
31
|
+
try {
|
32
|
+
var result = rec.dispose && rec.dispose.call(rec.value);
|
33
|
+
if (rec.async) return Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
|
34
|
+
}
|
35
|
+
catch (e) {
|
36
|
+
fail(e);
|
37
|
+
}
|
38
|
+
}
|
39
|
+
if (env.hasError) throw env.error;
|
40
|
+
}
|
41
|
+
return next();
|
42
|
+
};
|
43
|
+
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
44
|
+
var e = new Error(message);
|
45
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
46
|
+
});
|
2
47
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
48
|
exports.SessionGetter = void 0;
|
4
49
|
const simple_store_1 = require("@atproto-labs/simple-store");
|
50
|
+
const token_invalid_error_js_1 = require("./errors/token-invalid-error.js");
|
51
|
+
const token_refresh_error_js_1 = require("./errors/token-refresh-error.js");
|
52
|
+
const token_revoked_error_js_1 = require("./errors/token-revoked-error.js");
|
5
53
|
const oauth_response_error_js_1 = require("./oauth-response-error.js");
|
6
|
-
const refresh_error_js_1 = require("./refresh-error.js");
|
7
54
|
const util_js_1 = require("./util.js");
|
8
55
|
/**
|
9
56
|
* There are several advantages to wrapping the sessionStore in a (single)
|
@@ -17,23 +64,26 @@ class SessionGetter extends simple_store_1.CachedGetter {
|
|
17
64
|
super(async (sub, options, storedSession) => {
|
18
65
|
// There needs to be a previous session to be able to refresh. If
|
19
66
|
// storedSession is undefined, it means that the store does not contain
|
20
|
-
// a session for the given sub.
|
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.
|
67
|
+
// a session for the given sub.
|
26
68
|
if (storedSession === undefined) {
|
27
|
-
// Because the session is not in the store,
|
28
|
-
//
|
29
|
-
//
|
30
|
-
//
|
31
|
-
|
32
|
-
|
69
|
+
// Because the session is not in the store, this.delStored() method
|
70
|
+
// will not be called by the CachedGetter class (because there is
|
71
|
+
// nothing to delete). This would typically happen if there is no
|
72
|
+
// synchronization mechanism between instances of this class. Let's
|
73
|
+
// make sure an event is dispatched here if this occurs.
|
74
|
+
const msg = 'The session was deleted by another process';
|
75
|
+
const cause = new token_refresh_error_js_1.TokenRefreshError(sub, msg);
|
76
|
+
this.dispatchEvent('deleted', { sub, cause });
|
77
|
+
throw cause;
|
33
78
|
}
|
79
|
+
// From this point forward, throwing a TokenRefreshError will result in
|
80
|
+
// this.delStored() being called, resulting in an event being
|
81
|
+
// dispatched, even if the session was removed from the store through a
|
82
|
+
// concurrent access (which, normally, should not happen if a proper
|
83
|
+
// runtime lock was provided).
|
34
84
|
if (sub !== storedSession.tokenSet.sub) {
|
35
85
|
// Fool-proofing (e.g. against invalid session storage)
|
36
|
-
throw new
|
86
|
+
throw new token_refresh_error_js_1.TokenRefreshError(sub, 'Stored session sub mismatch');
|
37
87
|
}
|
38
88
|
// Since refresh tokens can only be used once, we might run into
|
39
89
|
// concurrency issues if multiple tabs/instances are trying to refresh
|
@@ -48,15 +98,24 @@ class SessionGetter extends simple_store_1.CachedGetter {
|
|
48
98
|
// refreshed the token.
|
49
99
|
const { tokenSet, dpopKey } = storedSession;
|
50
100
|
const server = await serverFactory.fromIssuer(tokenSet.iss, dpopKey);
|
51
|
-
//
|
52
|
-
//
|
53
|
-
//
|
54
|
-
//
|
55
|
-
//
|
101
|
+
// Because refresh tokens can only be used once, we must not use the
|
102
|
+
// "signal" to abort the refresh, or throw any abort error beyond this
|
103
|
+
// point. Any thrown error beyond this point will prevent the
|
104
|
+
// SessionGetter from obtaining, and storing, the new token set,
|
105
|
+
// effectively rendering the currently saved session unusable.
|
56
106
|
options?.signal?.throwIfAborted();
|
57
|
-
|
58
|
-
.refresh(tokenSet)
|
59
|
-
|
107
|
+
try {
|
108
|
+
const newTokenSet = await server.refresh(tokenSet);
|
109
|
+
if (sub !== newTokenSet.sub) {
|
110
|
+
// The server returned another sub. Was the tokenSet manipulated?
|
111
|
+
throw new token_refresh_error_js_1.TokenRefreshError(sub, 'Token set sub mismatch');
|
112
|
+
}
|
113
|
+
return { dpopKey, tokenSet: newTokenSet };
|
114
|
+
}
|
115
|
+
catch (cause) {
|
116
|
+
// If the refresh token is invalid, let's try to recover from
|
117
|
+
// concurrency issues, or make sure the session is deleted by throwing
|
118
|
+
// a TokenRefreshError.
|
60
119
|
if (cause instanceof oauth_response_error_js_1.OAuthResponseError &&
|
61
120
|
cause.status === 400 &&
|
62
121
|
cause.error === 'invalid_grant') {
|
@@ -64,37 +123,34 @@ class SessionGetter extends simple_store_1.CachedGetter {
|
|
64
123
|
// wait for a short time to give the other concurrent instances a
|
65
124
|
// chance to finish their refreshing of the token. If a concurrent
|
66
125
|
// refresh did occur, we will pretend that this one succeeded.
|
67
|
-
if (!runtime.
|
126
|
+
if (!runtime.hasImplementationLock) {
|
68
127
|
await new Promise((r) => setTimeout(r, 1000));
|
69
128
|
const stored = await this.getStored(sub);
|
70
129
|
if (stored === undefined) {
|
130
|
+
// A concurrent refresh occurred and caused the session to be
|
131
|
+
// deleted (for a reason we can't know at this point).
|
71
132
|
// Using a distinct error message mainly for debugging
|
72
|
-
// purposes
|
73
|
-
|
74
|
-
|
133
|
+
// purposes. Also, throwing a TokenRefreshError to trigger
|
134
|
+
// deletion through the deleteOnError callback.
|
135
|
+
const msg = 'The session was deleted by another process';
|
136
|
+
throw new token_refresh_error_js_1.TokenRefreshError(sub, msg, { cause });
|
75
137
|
}
|
76
138
|
else if (stored.tokenSet.access_token !== tokenSet.access_token ||
|
77
139
|
stored.tokenSet.refresh_token !== tokenSet.refresh_token) {
|
78
140
|
// A concurrent refresh occurred. Pretend this one succeeded.
|
79
|
-
return stored.tokenSet;
|
141
|
+
return { dpopKey, tokenSet: stored.tokenSet };
|
80
142
|
}
|
81
143
|
else {
|
82
144
|
// There were no concurrent refresh. The token is (likely)
|
83
145
|
// simply no longer valid.
|
84
146
|
}
|
85
147
|
}
|
86
|
-
//
|
87
|
-
// deleteOnError callback.
|
148
|
+
// Make sure the session gets deleted from the store
|
88
149
|
const msg = cause.errorDescription ?? 'The session was revoked';
|
89
|
-
throw new
|
150
|
+
throw new token_refresh_error_js_1.TokenRefreshError(sub, msg, { cause });
|
90
151
|
}
|
91
152
|
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
153
|
}
|
97
|
-
return { ...storedSession, tokenSet: newTokenSet };
|
98
154
|
}, sessionStore, {
|
99
155
|
isStale: (sub, { tokenSet }) => {
|
100
156
|
return (tokenSet.expires_at != null &&
|
@@ -109,9 +165,11 @@ class SessionGetter extends simple_store_1.CachedGetter {
|
|
109
165
|
await server.revoke(tokenSet.refresh_token ?? tokenSet.access_token);
|
110
166
|
throw err;
|
111
167
|
},
|
112
|
-
deleteOnError: async (err) =>
|
113
|
-
|
114
|
-
|
168
|
+
deleteOnError: async (err) =>
|
169
|
+
// Optimization: More likely to happen first
|
170
|
+
err instanceof token_refresh_error_js_1.TokenRefreshError ||
|
171
|
+
err instanceof token_revoked_error_js_1.TokenRevokedError ||
|
172
|
+
err instanceof token_invalid_error_js_1.TokenInvalidError,
|
115
173
|
});
|
116
174
|
Object.defineProperty(this, "runtime", {
|
117
175
|
enumerable: true,
|
@@ -119,6 +177,29 @@ class SessionGetter extends simple_store_1.CachedGetter {
|
|
119
177
|
writable: true,
|
120
178
|
value: runtime
|
121
179
|
});
|
180
|
+
Object.defineProperty(this, "eventTarget", {
|
181
|
+
enumerable: true,
|
182
|
+
configurable: true,
|
183
|
+
writable: true,
|
184
|
+
value: new util_js_1.CustomEventTarget()
|
185
|
+
});
|
186
|
+
}
|
187
|
+
addEventListener(type, callback, options) {
|
188
|
+
this.eventTarget.addEventListener(type, callback, options);
|
189
|
+
}
|
190
|
+
removeEventListener(type, callback, options) {
|
191
|
+
this.eventTarget.removeEventListener(type, callback, options);
|
192
|
+
}
|
193
|
+
dispatchEvent(type, detail) {
|
194
|
+
return this.eventTarget.dispatchCustomEvent(type, detail);
|
195
|
+
}
|
196
|
+
async setStored(sub, session) {
|
197
|
+
await super.setStored(sub, session);
|
198
|
+
this.dispatchEvent('updated', { sub, ...session });
|
199
|
+
}
|
200
|
+
async delStored(sub, cause) {
|
201
|
+
await super.delStored(sub, cause);
|
202
|
+
this.dispatchEvent('deleted', { sub, cause });
|
122
203
|
}
|
123
204
|
/**
|
124
205
|
* @param refresh When `true`, the credentials will be refreshed even if they
|
@@ -138,10 +219,21 @@ class SessionGetter extends simple_store_1.CachedGetter {
|
|
138
219
|
return session;
|
139
220
|
}
|
140
221
|
async get(sub, options) {
|
141
|
-
return this.runtime.
|
142
|
-
|
143
|
-
|
144
|
-
|
222
|
+
return this.runtime.usingLock(`@atproto-oauth-client-${sub}`, async () => {
|
223
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
224
|
+
try {
|
225
|
+
// Make sure, even if there is no signal in the options, that the request
|
226
|
+
// will be cancelled after at most 30 seconds.
|
227
|
+
const signal = __addDisposableResource(env_1, (0, util_js_1.timeoutSignal)(30e3, options), false);
|
228
|
+
return await super.get(sub, { ...options, signal });
|
229
|
+
}
|
230
|
+
catch (e_1) {
|
231
|
+
env_1.error = e_1;
|
232
|
+
env_1.hasError = true;
|
233
|
+
}
|
234
|
+
finally {
|
235
|
+
__disposeResources(env_1);
|
236
|
+
}
|
145
237
|
});
|
146
238
|
}
|
147
239
|
}
|