@atproto/oauth-client 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/LICENSE.txt +7 -0
  3. package/README.md +124 -0
  4. package/dist/constants.d.ts +5 -0
  5. package/dist/constants.d.ts.map +1 -0
  6. package/dist/constants.js +8 -0
  7. package/dist/constants.js.map +1 -0
  8. package/dist/fetch-dpop.d.ts +21 -0
  9. package/dist/fetch-dpop.d.ts.map +1 -0
  10. package/dist/fetch-dpop.js +149 -0
  11. package/dist/fetch-dpop.js.map +1 -0
  12. package/dist/index.d.ts +15 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.js +35 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/lock.d.ts +2 -0
  17. package/dist/lock.d.ts.map +1 -0
  18. package/dist/lock.js +33 -0
  19. package/dist/lock.js.map +1 -0
  20. package/dist/oauth-agent.d.ts +29 -0
  21. package/dist/oauth-agent.d.ts.map +1 -0
  22. package/dist/oauth-agent.js +138 -0
  23. package/dist/oauth-agent.js.map +1 -0
  24. package/dist/oauth-authorization-server-metadata-resolver.d.ts +15 -0
  25. package/dist/oauth-authorization-server-metadata-resolver.d.ts.map +1 -0
  26. package/dist/oauth-authorization-server-metadata-resolver.js +56 -0
  27. package/dist/oauth-authorization-server-metadata-resolver.js.map +1 -0
  28. package/dist/oauth-callback-error.d.ts +7 -0
  29. package/dist/oauth-callback-error.d.ts.map +1 -0
  30. package/dist/oauth-callback-error.js +28 -0
  31. package/dist/oauth-callback-error.js.map +1 -0
  32. package/dist/oauth-client.d.ts +78 -0
  33. package/dist/oauth-client.d.ts.map +1 -0
  34. package/dist/oauth-client.js +278 -0
  35. package/dist/oauth-client.js.map +1 -0
  36. package/dist/oauth-protected-resource-metadata-resolver.d.ts +15 -0
  37. package/dist/oauth-protected-resource-metadata-resolver.d.ts.map +1 -0
  38. package/dist/oauth-protected-resource-metadata-resolver.js +58 -0
  39. package/dist/oauth-protected-resource-metadata-resolver.js.map +1 -0
  40. package/dist/oauth-resolver-error.d.ts +7 -0
  41. package/dist/oauth-resolver-error.d.ts.map +1 -0
  42. package/dist/oauth-resolver-error.js +17 -0
  43. package/dist/oauth-resolver-error.js.map +1 -0
  44. package/dist/oauth-resolver.d.ts +62 -0
  45. package/dist/oauth-resolver.d.ts.map +1 -0
  46. package/dist/oauth-resolver.js +73 -0
  47. package/dist/oauth-resolver.js.map +1 -0
  48. package/dist/oauth-response-error.d.ts +11 -0
  49. package/dist/oauth-response-error.d.ts.map +1 -0
  50. package/dist/oauth-response-error.js +48 -0
  51. package/dist/oauth-response-error.js.map +1 -0
  52. package/dist/oauth-server-agent.d.ts +51 -0
  53. package/dist/oauth-server-agent.d.ts.map +1 -0
  54. package/dist/oauth-server-agent.js +228 -0
  55. package/dist/oauth-server-agent.js.map +1 -0
  56. package/dist/oauth-server-factory.d.ts +20 -0
  57. package/dist/oauth-server-factory.d.ts.map +1 -0
  58. package/dist/oauth-server-factory.js +53 -0
  59. package/dist/oauth-server-factory.js.map +1 -0
  60. package/dist/refresh-error.d.ts +7 -0
  61. package/dist/refresh-error.d.ts.map +1 -0
  62. package/dist/refresh-error.js +16 -0
  63. package/dist/refresh-error.js.map +1 -0
  64. package/dist/runtime-implementation.d.ts +12 -0
  65. package/dist/runtime-implementation.d.ts.map +1 -0
  66. package/dist/runtime-implementation.js +3 -0
  67. package/dist/runtime-implementation.js.map +1 -0
  68. package/dist/runtime.d.ts +35 -0
  69. package/dist/runtime.d.ts.map +1 -0
  70. package/dist/runtime.js +185 -0
  71. package/dist/runtime.js.map +1 -0
  72. package/dist/session-getter.d.ts +30 -0
  73. package/dist/session-getter.d.ts.map +1 -0
  74. package/dist/session-getter.js +149 -0
  75. package/dist/session-getter.js.map +1 -0
  76. package/dist/types.d.ts +1580 -0
  77. package/dist/types.d.ts.map +1 -0
  78. package/dist/types.js +8 -0
  79. package/dist/types.js.map +1 -0
  80. package/dist/util.d.ts +9 -0
  81. package/dist/util.d.ts.map +1 -0
  82. package/dist/util.js +35 -0
  83. package/dist/util.js.map +1 -0
  84. package/dist/validate-client-metadata.d.ts +5 -0
  85. package/dist/validate-client-metadata.d.ts.map +1 -0
  86. package/dist/validate-client-metadata.js +46 -0
  87. package/dist/validate-client-metadata.js.map +1 -0
  88. package/package.json +46 -0
  89. package/src/constants.ts +4 -0
  90. package/src/fetch-dpop.ts +235 -0
  91. package/src/index.ts +18 -0
  92. package/src/lock.ts +34 -0
  93. package/src/oauth-agent.ts +150 -0
  94. package/src/oauth-authorization-server-metadata-resolver.ts +98 -0
  95. package/src/oauth-callback-error.ts +16 -0
  96. package/src/oauth-client.ts +440 -0
  97. package/src/oauth-protected-resource-metadata-resolver.ts +102 -0
  98. package/src/oauth-resolver-error.ts +12 -0
  99. package/src/oauth-resolver.ts +111 -0
  100. package/src/oauth-response-error.ts +31 -0
  101. package/src/oauth-server-agent.ts +275 -0
  102. package/src/oauth-server-factory.ts +41 -0
  103. package/src/refresh-error.ts +9 -0
  104. package/src/runtime-implementation.ts +17 -0
  105. package/src/runtime.ts +211 -0
  106. package/src/session-getter.ts +182 -0
  107. package/src/types.ts +26 -0
  108. package/src/util.ts +51 -0
  109. package/src/validate-client-metadata.ts +61 -0
  110. package/tsconfig.build.json +8 -0
  111. package/tsconfig.json +4 -0
@@ -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,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=runtime-implementation.js.map
@@ -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"}
@@ -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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"}