@levr-one/auth 1.0.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 (70) hide show
  1. package/README.md +37 -0
  2. package/dist/config-watcher.d.ts +65 -0
  3. package/dist/config-watcher.d.ts.map +1 -0
  4. package/dist/config-watcher.js +140 -0
  5. package/dist/config-watcher.js.map +1 -0
  6. package/dist/config-watcher.test.d.ts +2 -0
  7. package/dist/config-watcher.test.d.ts.map +1 -0
  8. package/dist/config-watcher.test.js +116 -0
  9. package/dist/config-watcher.test.js.map +1 -0
  10. package/dist/config.d.ts +58 -0
  11. package/dist/config.d.ts.map +1 -0
  12. package/dist/config.js +234 -0
  13. package/dist/config.js.map +1 -0
  14. package/dist/config.test.d.ts +2 -0
  15. package/dist/config.test.d.ts.map +1 -0
  16. package/dist/config.test.js +221 -0
  17. package/dist/config.test.js.map +1 -0
  18. package/dist/file-lock.d.ts +41 -0
  19. package/dist/file-lock.d.ts.map +1 -0
  20. package/dist/file-lock.js +110 -0
  21. package/dist/file-lock.js.map +1 -0
  22. package/dist/index.d.ts +43 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +43 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/oauth-client.d.ts +156 -0
  27. package/dist/oauth-client.d.ts.map +1 -0
  28. package/dist/oauth-client.js +572 -0
  29. package/dist/oauth-client.js.map +1 -0
  30. package/dist/oauth-client.test.d.ts +2 -0
  31. package/dist/oauth-client.test.d.ts.map +1 -0
  32. package/dist/oauth-client.test.js +290 -0
  33. package/dist/oauth-client.test.js.map +1 -0
  34. package/dist/test-utils.d.ts +10 -0
  35. package/dist/test-utils.d.ts.map +1 -0
  36. package/dist/test-utils.js +45 -0
  37. package/dist/test-utils.js.map +1 -0
  38. package/dist/token-store.d.ts +78 -0
  39. package/dist/token-store.d.ts.map +1 -0
  40. package/dist/token-store.js +299 -0
  41. package/dist/token-store.js.map +1 -0
  42. package/dist/token-store.test.d.ts +2 -0
  43. package/dist/token-store.test.d.ts.map +1 -0
  44. package/dist/token-store.test.js +239 -0
  45. package/dist/token-store.test.js.map +1 -0
  46. package/dist/token-watcher.d.ts +82 -0
  47. package/dist/token-watcher.d.ts.map +1 -0
  48. package/dist/token-watcher.js +185 -0
  49. package/dist/token-watcher.js.map +1 -0
  50. package/dist/types.d.ts +70 -0
  51. package/dist/types.d.ts.map +1 -0
  52. package/dist/types.js +5 -0
  53. package/dist/types.js.map +1 -0
  54. package/dist/workspace-store.d.ts +60 -0
  55. package/dist/workspace-store.d.ts.map +1 -0
  56. package/dist/workspace-store.js +147 -0
  57. package/dist/workspace-store.js.map +1 -0
  58. package/dist/workspace-store.test.d.ts +2 -0
  59. package/dist/workspace-store.test.d.ts.map +1 -0
  60. package/dist/workspace-store.test.js +169 -0
  61. package/dist/workspace-store.test.js.map +1 -0
  62. package/dist/workspaces.d.ts +60 -0
  63. package/dist/workspaces.d.ts.map +1 -0
  64. package/dist/workspaces.js +80 -0
  65. package/dist/workspaces.js.map +1 -0
  66. package/dist/workspaces.test.d.ts +2 -0
  67. package/dist/workspaces.test.d.ts.map +1 -0
  68. package/dist/workspaces.test.js +111 -0
  69. package/dist/workspaces.test.js.map +1 -0
  70. package/package.json +58 -0
@@ -0,0 +1,572 @@
1
+ /**
2
+ * OAuth 2.0 client for TQ MCP servers
3
+ * Implements authorization code flow with PKCE (RFC 7636)
4
+ */
5
+ import * as crypto from 'crypto';
6
+ import * as http from 'http';
7
+ import { URL } from 'url';
8
+ import { loadTokens, saveTokens, clearTokens, restoreTokensFromBackup, } from './token-store.js';
9
+ import { acquireFileLock, getLockPath } from './file-lock.js';
10
+ import { saveWorkspace, clearWorkspace } from './workspace-store.js';
11
+ import { loadConfig } from './config.js';
12
+ /**
13
+ * OAuth 2.0 client with PKCE support
14
+ */
15
+ /**
16
+ * Number of consecutive 400/401 refresh failures before clearing tokens.
17
+ * A single transient backend error (restart, DB hiccup) returns 400 but the
18
+ * refresh token may still be valid. Only clear after repeated failures.
19
+ */
20
+ const CLEAR_TOKENS_AFTER_FAILURES = 3;
21
+ export class OAuthClient {
22
+ config;
23
+ accessToken = null;
24
+ refreshToken = null;
25
+ expiresAt = 0;
26
+ consecutiveRefreshFailures = 0;
27
+ /**
28
+ * Set when refresh is definitively rejected (400/401).
29
+ * Prevents further network refresh attempts until re-authorization
30
+ * or fresh tokens are loaded via setTokensFromStorage/authorize.
31
+ */
32
+ refreshRejected = false;
33
+ /**
34
+ * Deduplicates concurrent refresh calls. When multiple callers invoke
35
+ * refresh() simultaneously (e.g., parallel MCP tool requests), they
36
+ * all await the same in-flight promise instead of each firing a
37
+ * separate token rotation request — which would cause the second
38
+ * request to fail with invalid_grant (token already revoked by first).
39
+ */
40
+ refreshPromise = null;
41
+ constructor(config) {
42
+ // Resolve defaults from unified config (staging default)
43
+ const tqConfig = loadConfig();
44
+ const defaults = {
45
+ authServerUrl: tqConfig.apiUrl,
46
+ redirectPort: 6274,
47
+ scopes: ['read:own', 'write:own'],
48
+ };
49
+ this.config = {
50
+ ...defaults,
51
+ ...config,
52
+ };
53
+ // Try to load stored tokens on initialization
54
+ this.loadStoredTokens();
55
+ }
56
+ /**
57
+ * Load tokens from persistent storage
58
+ */
59
+ loadStoredTokens() {
60
+ const stored = loadTokens();
61
+ if (stored) {
62
+ this.accessToken = stored.accessToken;
63
+ this.refreshToken = stored.refreshToken;
64
+ this.expiresAt = stored.expiresAt;
65
+ }
66
+ }
67
+ /**
68
+ * Set tokens from external storage (used during initialization)
69
+ */
70
+ setTokensFromStorage(tokens) {
71
+ this.accessToken = tokens.accessToken;
72
+ this.refreshToken = tokens.refreshToken;
73
+ this.expiresAt = tokens.expiresAt;
74
+ // Fresh tokens from external source clear the rejection flag
75
+ this.refreshRejected = false;
76
+ }
77
+ /**
78
+ * Generate PKCE code verifier and challenge
79
+ * @see https://datatracker.ietf.org/doc/html/rfc7636
80
+ */
81
+ generatePKCE() {
82
+ // Code verifier: 43-128 character random string
83
+ const verifier = crypto.randomBytes(32).toString('base64url');
84
+ // Code challenge: base64url(SHA256(verifier))
85
+ const challenge = crypto
86
+ .createHash('sha256')
87
+ .update(verifier)
88
+ .digest('base64url');
89
+ return { verifier, challenge };
90
+ }
91
+ /**
92
+ * Start local HTTP server to receive OAuth callback
93
+ */
94
+ async waitForCallback(port, expectedState) {
95
+ return new Promise((resolve, reject) => {
96
+ const server = http.createServer((req, res) => {
97
+ const url = new URL(req.url, `http://localhost:${port}`);
98
+ const code = url.searchParams.get('code');
99
+ const error = url.searchParams.get('error');
100
+ const state = url.searchParams.get('state');
101
+ // Validate state parameter to prevent CSRF
102
+ if (state !== expectedState) {
103
+ res.writeHead(400, { 'Content-Type': 'text/html' });
104
+ res.end(`
105
+ <html>
106
+ <body style="font-family: system-ui; padding: 40px; text-align: center;">
107
+ <h1>Authorization Failed</h1>
108
+ <p>Invalid state parameter. This may be a security issue.</p>
109
+ </body>
110
+ </html>
111
+ `);
112
+ server.close();
113
+ reject(new Error('Invalid state parameter'));
114
+ return;
115
+ }
116
+ if (error) {
117
+ const errorDescription = url.searchParams.get('error_description');
118
+ res.writeHead(400, { 'Content-Type': 'text/html' });
119
+ res.end(`
120
+ <html>
121
+ <body style="font-family: system-ui; padding: 40px; text-align: center;">
122
+ <h1>Authorization Failed</h1>
123
+ <p>${errorDescription || error}</p>
124
+ </body>
125
+ </html>
126
+ `);
127
+ server.close();
128
+ reject(new Error(errorDescription || error));
129
+ return;
130
+ }
131
+ if (code) {
132
+ res.writeHead(200, { 'Content-Type': 'text/html' });
133
+ res.end(`
134
+ <html>
135
+ <body style="font-family: system-ui; padding: 40px; text-align: center;">
136
+ <h1>Authorization Successful!</h1>
137
+ <p>You can close this window and return to your terminal.</p>
138
+ <script>setTimeout(() => window.close(), 2000);</script>
139
+ </body>
140
+ </html>
141
+ `);
142
+ server.close();
143
+ resolve(code);
144
+ return;
145
+ }
146
+ // Unknown request
147
+ res.writeHead(404);
148
+ res.end('Not found');
149
+ });
150
+ server.listen(port, () => {
151
+ console.log(`[tq-oauth] OAuth callback server listening on port ${port}`);
152
+ });
153
+ // Timeout after 2 minutes
154
+ const timeout = setTimeout(() => {
155
+ server.close();
156
+ reject(new Error('Authorization timeout - no response received'));
157
+ }, 120000);
158
+ server.on('close', () => {
159
+ clearTimeout(timeout);
160
+ });
161
+ });
162
+ }
163
+ /**
164
+ * Perform full OAuth authorization flow
165
+ * Opens browser for user to authorize, waits for callback
166
+ */
167
+ async authorize() {
168
+ const { verifier, challenge } = this.generatePKCE();
169
+ const state = crypto.randomBytes(16).toString('hex');
170
+ const redirectUri = `http://localhost:${this.config.redirectPort}/oauth/callback`;
171
+ // Build authorization URL (uses versioned API route)
172
+ const authUrl = new URL('/v1/oauth/authorize', this.config.authServerUrl);
173
+ authUrl.searchParams.set('response_type', 'code');
174
+ authUrl.searchParams.set('client_id', this.config.clientId);
175
+ authUrl.searchParams.set('redirect_uri', redirectUri);
176
+ authUrl.searchParams.set('scope', this.config.scopes.join(' '));
177
+ authUrl.searchParams.set('state', state);
178
+ authUrl.searchParams.set('code_challenge', challenge);
179
+ authUrl.searchParams.set('code_challenge_method', 'S256');
180
+ // Open browser for user authorization
181
+ console.log('[tq-oauth] Opening browser for authorization...');
182
+ console.log(`[tq-oauth] If browser doesn't open, visit: ${authUrl.toString()}`);
183
+ // Dynamic import of 'open' package (ESM)
184
+ // unref() the child process so it doesn't keep the event loop alive
185
+ const open = (await import('open')).default;
186
+ const cp = await open(authUrl.toString());
187
+ cp.unref();
188
+ // Wait for callback with authorization code
189
+ const code = await this.waitForCallback(this.config.redirectPort, state);
190
+ // Exchange code for tokens
191
+ await this.exchangeCode(code, verifier, redirectUri);
192
+ // Fresh authorization clears any prior rejection
193
+ this.refreshRejected = false;
194
+ console.log('[tq-oauth] Authorization successful!');
195
+ }
196
+ /**
197
+ * Perform OAuth 2.0 Device Authorization Grant (RFC 8628).
198
+ * Works in headless/remote terminals — no browser redirect needed.
199
+ * Displays a URL and code for the user to authorize on any device.
200
+ */
201
+ async authorizeDevice() {
202
+ // 1. Request device code
203
+ const deviceUrl = new URL('/v1/oauth/device/authorize', this.config.authServerUrl);
204
+ const deviceRes = await fetch(deviceUrl.toString(), {
205
+ method: 'POST',
206
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
207
+ body: new URLSearchParams({
208
+ client_id: this.config.clientId,
209
+ scope: this.config.scopes.join(' '),
210
+ }),
211
+ });
212
+ if (!deviceRes.ok) {
213
+ const body = await deviceRes.text();
214
+ throw new Error(`Device authorization request failed (${deviceRes.status}): ${body}`);
215
+ }
216
+ const device = (await deviceRes.json());
217
+ // 2. Display code and URL (stderr to keep stdout clean for piped output)
218
+ console.error(`\nVisit: ${device.verification_uri}`);
219
+ console.error(`Enter code: ${device.user_code}\n`);
220
+ // 3. Poll for token
221
+ let interval = device.interval;
222
+ const deadline = Date.now() + device.expires_in * 1000;
223
+ const tokenUrl = new URL('/v1/oauth/token', this.config.authServerUrl);
224
+ while (Date.now() < deadline) {
225
+ await new Promise((r) => setTimeout(r, interval * 1000));
226
+ const res = await fetch(tokenUrl.toString(), {
227
+ method: 'POST',
228
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
229
+ body: new URLSearchParams({
230
+ grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
231
+ device_code: device.device_code,
232
+ client_id: this.config.clientId,
233
+ }),
234
+ });
235
+ if (res.ok) {
236
+ const tokens = (await res.json());
237
+ this.setTokens(tokens);
238
+ this.refreshRejected = false;
239
+ return;
240
+ }
241
+ const bodyText = await res.text();
242
+ let error;
243
+ try {
244
+ error = JSON.parse(bodyText).error;
245
+ }
246
+ catch {
247
+ throw new Error(`Device flow polling failed (${res.status}): ${bodyText}`);
248
+ }
249
+ switch (error) {
250
+ case 'authorization_pending':
251
+ continue;
252
+ case 'slow_down':
253
+ interval += 5;
254
+ continue;
255
+ case 'expired_token':
256
+ throw new Error('Device code expired. Please try again.');
257
+ case 'access_denied':
258
+ throw new Error('Authorization denied by user.');
259
+ default:
260
+ throw new Error(`Device flow error: ${error}`);
261
+ }
262
+ }
263
+ throw new Error('Device code expired. Please try again.');
264
+ }
265
+ /**
266
+ * Exchange authorization code for access and refresh tokens
267
+ */
268
+ async exchangeCode(code, codeVerifier, redirectUri) {
269
+ const tokenUrl = new URL('/v1/oauth/token', this.config.authServerUrl);
270
+ const body = new URLSearchParams({
271
+ grant_type: 'authorization_code',
272
+ code,
273
+ redirect_uri: redirectUri,
274
+ client_id: this.config.clientId,
275
+ code_verifier: codeVerifier,
276
+ });
277
+ const response = await fetch(tokenUrl.toString(), {
278
+ method: 'POST',
279
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
280
+ body: body.toString(),
281
+ });
282
+ if (!response.ok) {
283
+ const errorBody = await response.text();
284
+ throw new Error(`Token exchange failed: ${response.status} ${response.statusText} - ${errorBody}`);
285
+ }
286
+ const tokens = (await response.json());
287
+ this.setTokens(tokens);
288
+ }
289
+ /**
290
+ * Refresh access token using refresh token.
291
+ *
292
+ * Two layers of coordination:
293
+ * 1. In-process: `refreshPromise` dedups concurrent callers within
294
+ * this OAuthClient instance.
295
+ * 2. Cross-process: a file lock keyed on apiBaseUrl serializes
296
+ * refreshes across MCP server processes. On lock acquire, we
297
+ * re-read the on-disk token — if another process already
298
+ * refreshed while we waited, adopt their tokens and skip the
299
+ * network call. This avoids the "N processes all POST with the
300
+ * same refresh_token, all but one get invalid_grant" race that
301
+ * would otherwise exhaust consecutiveRefreshFailures and trigger
302
+ * clearTokens().
303
+ */
304
+ async refresh() {
305
+ // If a refresh is already in flight in THIS process, piggyback on it
306
+ if (this.refreshPromise) {
307
+ return this.refreshPromise;
308
+ }
309
+ // Pre-flight checks (synchronous — safe before dedup)
310
+ if (!this.refreshToken) {
311
+ // Last resort: try restoring from backup before giving up
312
+ if (restoreTokensFromBackup()) {
313
+ this.loadStoredTokens();
314
+ if (this.refreshToken) {
315
+ console.error('[tq-oauth] Recovered refresh token from backup, retrying...');
316
+ return this.refresh();
317
+ }
318
+ }
319
+ throw new Error('No refresh token available - authorization required');
320
+ }
321
+ if (this.refreshRejected) {
322
+ throw new Error('Refresh token was rejected - re-authorization required');
323
+ }
324
+ // Create the deduplication promise — wraps lock acquire + conditional
325
+ // refresh + release in a single await-able unit.
326
+ this.refreshPromise = this.refreshWithLock().finally(() => {
327
+ this.refreshPromise = null;
328
+ });
329
+ return this.refreshPromise;
330
+ }
331
+ /**
332
+ * Acquire the cross-process refresh lock, re-check disk state, and
333
+ * either adopt freshly-rotated tokens from disk or call the refresh
334
+ * endpoint. Always releases the lock.
335
+ */
336
+ async refreshWithLock() {
337
+ const lockPath = getLockPath(this.config.authServerUrl);
338
+ const tokenBeforeLock = this.accessToken;
339
+ const lock = await acquireFileLock(lockPath).catch((err) => {
340
+ // Lock acquisition failed outright — fall back to unlocked refresh.
341
+ // Better to race than to block indefinitely if the lock subsystem
342
+ // itself is broken (e.g. disk full, permissions, etc.).
343
+ console.error('[tq-oauth] File lock acquire failed; proceeding without lock:', err instanceof Error ? err.message : String(err));
344
+ return null;
345
+ });
346
+ if (lock?.brokeStaleLock) {
347
+ console.error('[tq-oauth] Broke stale refresh lock (previous holder likely crashed)');
348
+ }
349
+ try {
350
+ // Check if another process refreshed while we waited for the lock
351
+ const onDisk = loadTokens();
352
+ if (onDisk &&
353
+ onDisk.accessToken &&
354
+ onDisk.accessToken !== tokenBeforeLock &&
355
+ onDisk.expiresAt > Date.now()) {
356
+ // Another process won the race — adopt their tokens instead of
357
+ // rotating ours (which would now 400 with invalid_grant since
358
+ // our refresh_token was already consumed by the winner).
359
+ this.accessToken = onDisk.accessToken;
360
+ this.refreshToken = onDisk.refreshToken;
361
+ this.expiresAt = onDisk.expiresAt;
362
+ this.refreshRejected = false;
363
+ this.consecutiveRefreshFailures = 0;
364
+ return;
365
+ }
366
+ // Nobody else refreshed — do it ourselves
367
+ await this.refreshInternal();
368
+ }
369
+ finally {
370
+ lock?.release();
371
+ }
372
+ }
373
+ /**
374
+ * Internal refresh implementation — called only once per dedup window.
375
+ * All concurrent callers share the same promise via refresh().
376
+ */
377
+ async refreshInternal() {
378
+ const tokenUrl = new URL('/v1/oauth/token', this.config.authServerUrl);
379
+ const body = new URLSearchParams({
380
+ grant_type: 'refresh_token',
381
+ refresh_token: this.refreshToken,
382
+ client_id: this.config.clientId,
383
+ });
384
+ let response;
385
+ try {
386
+ response = await fetch(tokenUrl.toString(), {
387
+ method: 'POST',
388
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
389
+ body: body.toString(),
390
+ });
391
+ }
392
+ catch (fetchError) {
393
+ // Network error — backend is unreachable. NEVER clear tokens for this.
394
+ throw new Error(`Token refresh failed (network error: ${fetchError instanceof Error ? fetchError.message : String(fetchError)}) - will retry`);
395
+ }
396
+ if (!response.ok) {
397
+ if (response.status === 400 || response.status === 401) {
398
+ this.consecutiveRefreshFailures++;
399
+ console.error(`[tq-oauth] Refresh rejected (${response.status}), ` +
400
+ `failure ${this.consecutiveRefreshFailures}/${CLEAR_TOKENS_AFTER_FAILURES}`);
401
+ // Only clear tokens after CLEAR_TOKENS_AFTER_FAILURES consecutive
402
+ // 400/401 responses. A single failure could be a transient backend
403
+ // issue (restart, DB connection drop) where the refresh token is
404
+ // still valid.
405
+ if (this.consecutiveRefreshFailures >= CLEAR_TOKENS_AFTER_FAILURES) {
406
+ console.error('[tq-oauth] Clearing tokens after ' +
407
+ `${this.consecutiveRefreshFailures} consecutive failures`);
408
+ this.accessToken = null;
409
+ this.refreshToken = null;
410
+ this.expiresAt = 0;
411
+ this.refreshRejected = true;
412
+ clearTokens();
413
+ this.consecutiveRefreshFailures = 0;
414
+ throw new Error('Refresh token rejected by server after multiple attempts - re-authorization required');
415
+ }
416
+ // Not enough consecutive failures yet — clear in-memory access token
417
+ // but keep the file on disk so next retry uses the same refresh token.
418
+ this.accessToken = null;
419
+ this.expiresAt = 0;
420
+ throw new Error(`Token refresh failed (${response.status}) - will retry ` +
421
+ `(${CLEAR_TOKENS_AFTER_FAILURES - this.consecutiveRefreshFailures} attempts remaining)`);
422
+ }
423
+ // 5xx or other non-auth errors — transient, don't count as rejections
424
+ throw new Error(`Token refresh failed (${response.status}) - will retry`);
425
+ }
426
+ // Success — reset failure counter and rejected flag
427
+ this.consecutiveRefreshFailures = 0;
428
+ this.refreshRejected = false;
429
+ const tokens = (await response.json());
430
+ this.setTokens(tokens);
431
+ }
432
+ /**
433
+ * Store tokens in memory and persist to disk.
434
+ *
435
+ * Non-refresh grants (authorization_code, device_code, password) carry
436
+ * `tokens.user.workspace_id` — the workspace the user just logged into.
437
+ * Refresh grants omit it. When present we persist it to ~/.tq/workspace.json
438
+ * so MCP clients pick up the selected workspace without a separate round-trip.
439
+ */
440
+ setTokens(tokens) {
441
+ this.accessToken = tokens.access_token;
442
+ this.refreshToken = tokens.refresh_token;
443
+ // Expire 1 minute early to avoid edge cases
444
+ this.expiresAt = Date.now() + tokens.expires_in * 1000 - 60000;
445
+ // Persist to disk (keyed by apiBaseUrl in the token map)
446
+ saveTokens({
447
+ accessToken: this.accessToken,
448
+ refreshToken: this.refreshToken,
449
+ expiresAt: this.expiresAt,
450
+ apiBaseUrl: this.config.authServerUrl,
451
+ });
452
+ if (tokens.user?.workspace_id) {
453
+ try {
454
+ saveWorkspace(tokens.user.workspace_id);
455
+ }
456
+ catch (err) {
457
+ console.error('[tq-oauth] Failed to persist workspace:', err);
458
+ }
459
+ }
460
+ }
461
+ /**
462
+ * Get a valid access token, refreshing if needed
463
+ * Note: Does NOT auto-authorize - throws if no tokens available
464
+ */
465
+ async getAccessToken() {
466
+ // No access token but refresh token available — try refresh
467
+ if (!this.accessToken && this.refreshToken) {
468
+ await this.refresh();
469
+ return this.accessToken;
470
+ }
471
+ // No token at all - need to authorize
472
+ if (!this.accessToken) {
473
+ throw new Error('No access token available - authorization required');
474
+ }
475
+ // Token expired - try to refresh
476
+ if (Date.now() >= this.expiresAt) {
477
+ await this.refresh();
478
+ }
479
+ return this.accessToken;
480
+ }
481
+ /**
482
+ * Get access token without auto-refresh (for checking current state)
483
+ */
484
+ getAccessTokenSync() {
485
+ return this.accessToken;
486
+ }
487
+ /**
488
+ * Check if client has valid tokens
489
+ */
490
+ isAuthorized() {
491
+ return this.accessToken !== null && Date.now() < this.expiresAt;
492
+ }
493
+ /**
494
+ * Check if tokens exist (may be expired)
495
+ */
496
+ hasTokens() {
497
+ return this.accessToken !== null;
498
+ }
499
+ /**
500
+ * Attempt a proactive (background) refresh without accumulating toward
501
+ * the nuclear clearTokens() threshold. If refresh fails, the failure
502
+ * counter is reset so background timer ticks can never delete the
503
+ * token file. Only critical-path refreshes (from getAccessToken /
504
+ * ensureAuth) should count toward the threshold.
505
+ *
506
+ * @returns true if refresh succeeded, false otherwise
507
+ */
508
+ async refreshProactive() {
509
+ try {
510
+ await this.refresh();
511
+ return true;
512
+ }
513
+ catch {
514
+ // Reset counter — proactive failures must never accumulate toward
515
+ // the CLEAR_TOKENS_AFTER_FAILURES threshold that deletes the file.
516
+ this.consecutiveRefreshFailures = 0;
517
+ return false;
518
+ }
519
+ }
520
+ /**
521
+ * Clear all tokens (logout). Also clears the persisted workspace selection
522
+ * so the next login starts from a clean state on shared machines.
523
+ */
524
+ clearTokens() {
525
+ this.accessToken = null;
526
+ this.refreshToken = null;
527
+ this.expiresAt = 0;
528
+ clearTokens();
529
+ clearWorkspace();
530
+ }
531
+ /**
532
+ * Get token expiration time
533
+ */
534
+ getExpiresAt() {
535
+ return this.expiresAt;
536
+ }
537
+ /**
538
+ * Get current refresh token (for storage)
539
+ */
540
+ getRefreshToken() {
541
+ return this.refreshToken;
542
+ }
543
+ /**
544
+ * Get time until expiration in milliseconds
545
+ */
546
+ getTimeUntilExpiry() {
547
+ return this.expiresAt - Date.now();
548
+ }
549
+ /**
550
+ * Check if token is expiring soon (within given minutes)
551
+ */
552
+ isExpiringSoon(withinMinutes = 5) {
553
+ const threshold = withinMinutes * 60 * 1000;
554
+ const timeUntilExpiry = this.getTimeUntilExpiry();
555
+ return timeUntilExpiry > 0 && timeUntilExpiry < threshold;
556
+ }
557
+ /**
558
+ * Check if refresh was definitively rejected (token revoked/invalid).
559
+ * When true, no further refresh attempts will be made until
560
+ * re-authorization or fresh tokens are loaded.
561
+ */
562
+ isRefreshRejected() {
563
+ return this.refreshRejected;
564
+ }
565
+ /**
566
+ * Reload tokens from storage (useful after external refresh)
567
+ */
568
+ reloadTokens() {
569
+ this.loadStoredTokens();
570
+ }
571
+ }
572
+ //# sourceMappingURL=oauth-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-client.js","sourceRoot":"","sources":["../src/oauth-client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AACjC,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAC1B,OAAO,EACL,UAAU,EACV,UAAU,EACV,WAAW,EACX,uBAAuB,GACxB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAQzC;;GAEG;AACH;;;;GAIG;AACH,MAAM,2BAA2B,GAAG,CAAC,CAAC;AAEtC,MAAM,OAAO,WAAW;IACd,MAAM,CAAc;IACpB,WAAW,GAAkB,IAAI,CAAC;IAClC,YAAY,GAAkB,IAAI,CAAC;IACnC,SAAS,GAAW,CAAC,CAAC;IACtB,0BAA0B,GAAW,CAAC,CAAC;IAC/C;;;;OAIG;IACK,eAAe,GAAY,KAAK,CAAC;IACzC;;;;;;OAMG;IACK,cAAc,GAAyB,IAAI,CAAC;IAEpD,YAAY,MAAmD;QAC7D,yDAAyD;QACzD,MAAM,QAAQ,GAAG,UAAU,EAAE,CAAC;QAC9B,MAAM,QAAQ,GAAyB;YACrC,aAAa,EAAE,QAAQ,CAAC,MAAM;YAC9B,YAAY,EAAE,IAAI;YAClB,MAAM,EAAE,CAAC,UAAU,EAAE,WAAW,CAAC;SAClC,CAAC;QAEF,IAAI,CAAC,MAAM,GAAG;YACZ,GAAG,QAAQ;YACX,GAAG,MAAM;SACK,CAAC;QAEjB,8CAA8C;QAC9C,IAAI,CAAC,gBAAgB,EAAE,CAAC;IAC1B,CAAC;IAED;;OAEG;IACK,gBAAgB;QACtB,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;QAC5B,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC;YACtC,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC;YACxC,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;QACpC,CAAC;IACH,CAAC;IAED;;OAEG;IACH,oBAAoB,CAAC,MAAoB;QACvC,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC;QACtC,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC;QACxC,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;QAClC,6DAA6D;QAC7D,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC;IAC/B,CAAC;IAED;;;OAGG;IACK,YAAY;QAClB,gDAAgD;QAChD,MAAM,QAAQ,GAAG,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QAE9D,8CAA8C;QAC9C,MAAM,SAAS,GAAG,MAAM;aACrB,UAAU,CAAC,QAAQ,CAAC;aACpB,MAAM,CAAC,QAAQ,CAAC;aAChB,MAAM,CAAC,WAAW,CAAC,CAAC;QAEvB,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;IACjC,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,eAAe,CAC3B,IAAY,EACZ,aAAqB;QAErB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;gBAC5C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAI,EAAE,oBAAoB,IAAI,EAAE,CAAC,CAAC;gBAC1D,MAAM,IAAI,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;gBAC1C,MAAM,KAAK,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;gBAC5C,MAAM,KAAK,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;gBAE5C,2CAA2C;gBAC3C,IAAI,KAAK,KAAK,aAAa,EAAE,CAAC;oBAC5B,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC,CAAC;oBACpD,GAAG,CAAC,GAAG,CAAC;;;;;;;WAOP,CAAC,CAAC;oBACH,MAAM,CAAC,KAAK,EAAE,CAAC;oBACf,MAAM,CAAC,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC,CAAC;oBAC7C,OAAO;gBACT,CAAC;gBAED,IAAI,KAAK,EAAE,CAAC;oBACV,MAAM,gBAAgB,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;oBACnE,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC,CAAC;oBACpD,GAAG,CAAC,GAAG,CAAC;;;;qBAIG,gBAAgB,IAAI,KAAK;;;WAGnC,CAAC,CAAC;oBACH,MAAM,CAAC,KAAK,EAAE,CAAC;oBACf,MAAM,CAAC,IAAI,KAAK,CAAC,gBAAgB,IAAI,KAAK,CAAC,CAAC,CAAC;oBAC7C,OAAO;gBACT,CAAC;gBAED,IAAI,IAAI,EAAE,CAAC;oBACT,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC,CAAC;oBACpD,GAAG,CAAC,GAAG,CAAC;;;;;;;;WAQP,CAAC,CAAC;oBACH,MAAM,CAAC,KAAK,EAAE,CAAC;oBACf,OAAO,CAAC,IAAI,CAAC,CAAC;oBACd,OAAO;gBACT,CAAC;gBAED,kBAAkB;gBAClB,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;gBACnB,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;YACvB,CAAC,CAAC,CAAC;YAEH,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;gBACvB,OAAO,CAAC,GAAG,CACT,sDAAsD,IAAI,EAAE,CAC7D,CAAC;YACJ,CAAC,CAAC,CAAC;YAEH,0BAA0B;YAC1B,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC9B,MAAM,CAAC,KAAK,EAAE,CAAC;gBACf,MAAM,CAAC,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC,CAAC;YACpE,CAAC,EAAE,MAAM,CAAC,CAAC;YAEX,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;gBACtB,YAAY,CAAC,OAAO,CAAC,CAAC;YACxB,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,SAAS;QACb,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;QACpD,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACrD,MAAM,WAAW,GAAG,oBAAoB,IAAI,CAAC,MAAM,CAAC,YAAY,iBAAiB,CAAC;QAElF,qDAAqD;QACrD,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,qBAAqB,EAAE,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;QAC1E,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;QAClD,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC5D,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;QACtD,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAChE,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACzC,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,gBAAgB,EAAE,SAAS,CAAC,CAAC;QACtD,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,uBAAuB,EAAE,MAAM,CAAC,CAAC;QAE1D,sCAAsC;QACtC,OAAO,CAAC,GAAG,CAAC,iDAAiD,CAAC,CAAC;QAC/D,OAAO,CAAC,GAAG,CACT,8CAA8C,OAAO,CAAC,QAAQ,EAAE,EAAE,CACnE,CAAC;QAEF,yCAAyC;QACzC,oEAAoE;QACpE,MAAM,IAAI,GAAG,CAAC,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;QAC5C,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC1C,EAAE,CAAC,KAAK,EAAE,CAAC;QAEX,4CAA4C;QAC5C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;QAEzE,2BAA2B;QAC3B,MAAM,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC;QAErD,iDAAiD;QACjD,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC;QAC7B,OAAO,CAAC,GAAG,CAAC,sCAAsC,CAAC,CAAC;IACtD,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,eAAe;QACnB,yBAAyB;QACzB,MAAM,SAAS,GAAG,IAAI,GAAG,CACvB,4BAA4B,EAC5B,IAAI,CAAC,MAAM,CAAC,aAAa,CAC1B,CAAC;QAEF,MAAM,SAAS,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE;YAClD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,mCAAmC,EAAE;YAChE,IAAI,EAAE,IAAI,eAAe,CAAC;gBACxB,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;gBAC/B,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;aACpC,CAAC;SACH,CAAC,CAAC;QAEH,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC;YAClB,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,IAAI,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CACb,wCAAwC,SAAS,CAAC,MAAM,MAAM,IAAI,EAAE,CACrE,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAG,CAAC,MAAM,SAAS,CAAC,IAAI,EAAE,CAAgC,CAAC;QAEvE,yEAAyE;QACzE,OAAO,CAAC,KAAK,CAAC,iBAAiB,MAAM,CAAC,gBAAgB,EAAE,CAAC,CAAC;QAC1D,OAAO,CAAC,KAAK,CAAC,eAAe,MAAM,CAAC,SAAS,IAAI,CAAC,CAAC;QAEnD,oBAAoB;QACpB,IAAI,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;QAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,UAAU,GAAG,IAAI,CAAC;QACvD,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,iBAAiB,EAAE,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;QAEvE,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;YAC7B,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC;YAEzD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE;gBAC3C,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,mCAAmC,EAAE;gBAChE,IAAI,EAAE,IAAI,eAAe,CAAC;oBACxB,UAAU,EAAE,8CAA8C;oBAC1D,WAAW,EAAE,MAAM,CAAC,WAAW;oBAC/B,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;iBAChC,CAAC;aACH,CAAC,CAAC;YAEH,IAAI,GAAG,CAAC,EAAE,EAAE,CAAC;gBACX,MAAM,MAAM,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAkB,CAAC;gBACnD,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;gBACvB,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC;gBAC7B,OAAO;YACT,CAAC;YAED,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;YAClC,IAAI,KAAa,CAAC;YAClB,IAAI,CAAC;gBACH,KAAK,GAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAuB,CAAC,KAAK,CAAC;YAC5D,CAAC;YAAC,MAAM,CAAC;gBACP,MAAM,IAAI,KAAK,CACb,+BAA+B,GAAG,CAAC,MAAM,MAAM,QAAQ,EAAE,CAC1D,CAAC;YACJ,CAAC;YAED,QAAQ,KAAK,EAAE,CAAC;gBACd,KAAK,uBAAuB;oBAC1B,SAAS;gBACX,KAAK,WAAW;oBACd,QAAQ,IAAI,CAAC,CAAC;oBACd,SAAS;gBACX,KAAK,eAAe;oBAClB,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;gBAC5D,KAAK,eAAe;oBAClB,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;gBACnD;oBACE,MAAM,IAAI,KAAK,CAAC,sBAAsB,KAAK,EAAE,CAAC,CAAC;YACnD,CAAC;QACH,CAAC;QAED,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAC5D,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,YAAY,CACxB,IAAY,EACZ,YAAoB,EACpB,WAAmB;QAEnB,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,iBAAiB,EAAE,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;QAEvE,MAAM,IAAI,GAAG,IAAI,eAAe,CAAC;YAC/B,UAAU,EAAE,oBAAoB;YAChC,IAAI;YACJ,YAAY,EAAE,WAAW;YACzB,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;YAC/B,aAAa,EAAE,YAAY;SAC5B,CAAC,CAAC;QAEH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE;YAChD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,mCAAmC,EAAE;YAChE,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE;SACtB,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YACxC,MAAM,IAAI,KAAK,CACb,0BAA0B,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,MAAM,SAAS,EAAE,CAClF,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAkB,CAAC;QACxD,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACzB,CAAC;IAED;;;;;;;;;;;;;;OAcG;IACH,KAAK,CAAC,OAAO;QACX,qEAAqE;QACrE,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACxB,OAAO,IAAI,CAAC,cAAc,CAAC;QAC7B,CAAC;QAED,sDAAsD;QACtD,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;YACvB,0DAA0D;YAC1D,IAAI,uBAAuB,EAAE,EAAE,CAAC;gBAC9B,IAAI,CAAC,gBAAgB,EAAE,CAAC;gBACxB,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;oBACtB,OAAO,CAAC,KAAK,CACX,6DAA6D,CAC9D,CAAC;oBACF,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC;gBACxB,CAAC;YACH,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;QACzE,CAAC;QAED,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;QAC5E,CAAC;QAED,sEAAsE;QACtE,iDAAiD;QACjD,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE;YACxD,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC7B,CAAC,CAAC,CAAC;QAEH,OAAO,IAAI,CAAC,cAAc,CAAC;IAC7B,CAAC;IAED;;;;OAIG;IACK,KAAK,CAAC,eAAe;QAC3B,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;QACxD,MAAM,eAAe,GAAG,IAAI,CAAC,WAAW,CAAC;QACzC,MAAM,IAAI,GAAG,MAAM,eAAe,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;YAClE,oEAAoE;YACpE,kEAAkE;YAClE,wDAAwD;YACxD,OAAO,CAAC,KAAK,CACX,+DAA+D,EAC/D,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CACjD,CAAC;YACF,OAAO,IAAI,CAAC;QACd,CAAC,CAAC,CAAC;QAEH,IAAI,IAAI,EAAE,cAAc,EAAE,CAAC;YACzB,OAAO,CAAC,KAAK,CACX,sEAAsE,CACvE,CAAC;QACJ,CAAC;QAED,IAAI,CAAC;YACH,kEAAkE;YAClE,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;YAC5B,IACE,MAAM;gBACN,MAAM,CAAC,WAAW;gBAClB,MAAM,CAAC,WAAW,KAAK,eAAe;gBACtC,MAAM,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,EAC7B,CAAC;gBACD,+DAA+D;gBAC/D,8DAA8D;gBAC9D,yDAAyD;gBACzD,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC;gBACtC,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC;gBACxC,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;gBAClC,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC;gBAC7B,IAAI,CAAC,0BAA0B,GAAG,CAAC,CAAC;gBACpC,OAAO;YACT,CAAC;YAED,0CAA0C;YAC1C,MAAM,IAAI,CAAC,eAAe,EAAE,CAAC;QAC/B,CAAC;gBAAS,CAAC;YACT,IAAI,EAAE,OAAO,EAAE,CAAC;QAClB,CAAC;IACH,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,eAAe;QAC3B,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,iBAAiB,EAAE,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;QAEvE,MAAM,IAAI,GAAG,IAAI,eAAe,CAAC;YAC/B,UAAU,EAAE,eAAe;YAC3B,aAAa,EAAE,IAAI,CAAC,YAAa;YACjC,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;SAChC,CAAC,CAAC;QAEH,IAAI,QAAkB,CAAC;QACvB,IAAI,CAAC;YACH,QAAQ,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE;gBAC1C,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,mCAAmC,EAAE;gBAChE,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE;aACtB,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,UAAU,EAAE,CAAC;YACpB,uEAAuE;YACvE,MAAM,IAAI,KAAK,CACb,wCAAwC,UAAU,YAAY,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,gBAAgB,CAC9H,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBACvD,IAAI,CAAC,0BAA0B,EAAE,CAAC;gBAElC,OAAO,CAAC,KAAK,CACX,gCAAgC,QAAQ,CAAC,MAAM,KAAK;oBAClD,WAAW,IAAI,CAAC,0BAA0B,IAAI,2BAA2B,EAAE,CAC9E,CAAC;gBAEF,kEAAkE;gBAClE,mEAAmE;gBACnE,iEAAiE;gBACjE,eAAe;gBACf,IAAI,IAAI,CAAC,0BAA0B,IAAI,2BAA2B,EAAE,CAAC;oBACnE,OAAO,CAAC,KAAK,CACX,mCAAmC;wBACjC,GAAG,IAAI,CAAC,0BAA0B,uBAAuB,CAC5D,CAAC;oBACF,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;oBACxB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;oBACzB,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC;oBACnB,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;oBAC5B,WAAW,EAAE,CAAC;oBACd,IAAI,CAAC,0BAA0B,GAAG,CAAC,CAAC;oBACpC,MAAM,IAAI,KAAK,CACb,sFAAsF,CACvF,CAAC;gBACJ,CAAC;gBAED,qEAAqE;gBACrE,uEAAuE;gBACvE,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;gBACxB,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC;gBACnB,MAAM,IAAI,KAAK,CACb,yBAAyB,QAAQ,CAAC,MAAM,iBAAiB;oBACvD,IAAI,2BAA2B,GAAG,IAAI,CAAC,0BAA0B,sBAAsB,CAC1F,CAAC;YACJ,CAAC;YAED,sEAAsE;YACtE,MAAM,IAAI,KAAK,CAAC,yBAAyB,QAAQ,CAAC,MAAM,gBAAgB,CAAC,CAAC;QAC5E,CAAC;QAED,oDAAoD;QACpD,IAAI,CAAC,0BAA0B,GAAG,CAAC,CAAC;QACpC,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC;QAC7B,MAAM,MAAM,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAkB,CAAC;QACxD,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACzB,CAAC;IAED;;;;;;;OAOG;IACK,SAAS,CAAC,MAAqB;QACrC,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,YAAY,CAAC;QACvC,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC,aAAa,CAAC;QACzC,4CAA4C;QAC5C,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,UAAU,GAAG,IAAI,GAAG,KAAK,CAAC;QAE/D,yDAAyD;QACzD,UAAU,CAAC;YACT,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,aAAa;SACtC,CAAC,CAAC;QAEH,IAAI,MAAM,CAAC,IAAI,EAAE,YAAY,EAAE,CAAC;YAC9B,IAAI,CAAC;gBACH,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAC1C,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,KAAK,CAAC,yCAAyC,EAAE,GAAG,CAAC,CAAC;YAChE,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,cAAc;QAClB,4DAA4D;QAC5D,IAAI,CAAC,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YAC3C,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;YACrB,OAAO,IAAI,CAAC,WAAY,CAAC;QAC3B,CAAC;QAED,sCAAsC;QACtC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;QACxE,CAAC;QAED,iCAAiC;QACjC,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACjC,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QACvB,CAAC;QAED,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;IAED;;OAEG;IACH,kBAAkB;QAChB,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;IAED;;OAEG;IACH,YAAY;QACV,OAAO,IAAI,CAAC,WAAW,KAAK,IAAI,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC;IAClE,CAAC;IAED;;OAEG;IACH,SAAS;QACP,OAAO,IAAI,CAAC,WAAW,KAAK,IAAI,CAAC;IACnC,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK,CAAC,gBAAgB;QACpB,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;YACrB,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,kEAAkE;YAClE,mEAAmE;YACnE,IAAI,CAAC,0BAA0B,GAAG,CAAC,CAAC;YACpC,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,WAAW;QACT,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QACxB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC;QACnB,WAAW,EAAE,CAAC;QACd,cAAc,EAAE,CAAC;IACnB,CAAC;IAED;;OAEG;IACH,YAAY;QACV,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED;;OAEG;IACH,eAAe;QACb,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAED;;OAEG;IACH,kBAAkB;QAChB,OAAO,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACrC,CAAC;IAED;;OAEG;IACH,cAAc,CAAC,gBAAwB,CAAC;QACtC,MAAM,SAAS,GAAG,aAAa,GAAG,EAAE,GAAG,IAAI,CAAC;QAC5C,MAAM,eAAe,GAAG,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAClD,OAAO,eAAe,GAAG,CAAC,IAAI,eAAe,GAAG,SAAS,CAAC;IAC5D,CAAC;IAED;;;;OAIG;IACH,iBAAiB;QACf,OAAO,IAAI,CAAC,eAAe,CAAC;IAC9B,CAAC;IAED;;OAEG;IACH,YAAY;QACV,IAAI,CAAC,gBAAgB,EAAE,CAAC;IAC1B,CAAC;CACF"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=oauth-client.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-client.test.d.ts","sourceRoot":"","sources":["../src/oauth-client.test.ts"],"names":[],"mappings":""}