quickbase-js 1.1.0 → 2.0.0-alpha.2
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.
- package/README.md +351 -227
- package/dist/auth/index.d.ts +15 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +29 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/sso-token.d.ts +28 -0
- package/dist/auth/sso-token.d.ts.map +1 -0
- package/dist/auth/sso-token.js +108 -0
- package/dist/auth/sso-token.js.map +1 -0
- package/dist/auth/temp-token.d.ts +31 -0
- package/dist/auth/temp-token.d.ts.map +1 -0
- package/dist/auth/temp-token.js +121 -0
- package/dist/auth/temp-token.js.map +1 -0
- package/dist/auth/token-cache.d.ts +44 -0
- package/dist/auth/token-cache.d.ts.map +1 -0
- package/dist/auth/token-cache.js +84 -0
- package/dist/auth/token-cache.js.map +1 -0
- package/dist/auth/types.d.ts +44 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +5 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/auth/user-token.d.ts +17 -0
- package/dist/auth/user-token.d.ts.map +1 -0
- package/dist/auth/user-token.js +30 -0
- package/dist/auth/user-token.js.map +1 -0
- package/dist/client/index.d.ts +12 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +8 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/pagination.d.ts +131 -0
- package/dist/client/pagination.d.ts.map +1 -0
- package/dist/client/pagination.js +226 -0
- package/dist/client/pagination.js.map +1 -0
- package/dist/client/request.d.ts +40 -0
- package/dist/client/request.d.ts.map +1 -0
- package/dist/client/request.js +194 -0
- package/dist/client/request.js.map +1 -0
- package/dist/client/retry.d.ts +41 -0
- package/dist/client/retry.d.ts.map +1 -0
- package/dist/client/retry.js +122 -0
- package/dist/client/retry.js.map +1 -0
- package/dist/client/throttle.d.ts +46 -0
- package/dist/client/throttle.d.ts.map +1 -0
- package/dist/client/throttle.js +85 -0
- package/dist/client/throttle.js.map +1 -0
- package/dist/core/config.d.ts +13 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +120 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/dates.d.ts +18 -0
- package/dist/core/dates.d.ts.map +1 -0
- package/dist/core/dates.js +58 -0
- package/dist/core/dates.js.map +1 -0
- package/dist/core/errors.d.ts +134 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +185 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/index.d.ts +10 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +9 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/logger.d.ts +41 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +96 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/types.d.ts +154 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +23 -0
- package/dist/core/types.js.map +1 -0
- package/dist/generated/client.d.ts +18 -0
- package/dist/generated/client.d.ts.map +1 -0
- package/dist/generated/client.js +1390 -0
- package/dist/generated/client.js.map +1 -0
- package/dist/generated/types.d.ts +3827 -0
- package/dist/generated/types.d.ts.map +1 -0
- package/dist/generated/types.js +8 -0
- package/dist/generated/types.js.map +1 -0
- package/dist/index.d.ts +89 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +99 -0
- package/dist/index.js.map +1 -0
- package/dist/quickbase.esm.js +7 -0
- package/dist/quickbase.esm.js.map +7 -0
- package/dist/quickbase.min.js +7 -0
- package/dist/quickbase.min.js.map +7 -0
- package/package.json +57 -86
- package/dist/esm/quickbase.d.ts +0 -8094
- package/dist/esm/quickbase.js +0 -8330
- package/dist/esm/quickbase.js.map +0 -1
- package/dist/esm/quickbase.min.js +0 -2
- package/dist/esm/quickbase.min.js.map +0 -1
- package/dist/umd/quickbase.umd.js +0 -8906
- package/dist/umd/quickbase.umd.js.map +0 -1
- package/dist/umd/quickbase.umd.min.js +0 -2
- package/dist/umd/quickbase.umd.min.js.map +0 -1
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication module exports
|
|
3
|
+
*/
|
|
4
|
+
export type { AuthStrategy, AuthContext, CachedToken } from './types.js';
|
|
5
|
+
export { UserTokenStrategy } from './user-token.js';
|
|
6
|
+
export { TempTokenStrategy } from './temp-token.js';
|
|
7
|
+
export { SsoTokenStrategy } from './sso-token.js';
|
|
8
|
+
export { TokenCache } from './token-cache.js';
|
|
9
|
+
import type { AuthConfig } from '../core/types.js';
|
|
10
|
+
import type { AuthStrategy, AuthContext } from './types.js';
|
|
11
|
+
/**
|
|
12
|
+
* Factory function to create the appropriate auth strategy
|
|
13
|
+
*/
|
|
14
|
+
export declare function createAuthStrategy(authConfig: AuthConfig, context: AuthContext): AuthStrategy;
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAK5D;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE,WAAW,GACnB,YAAY,CAiBd"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication module exports
|
|
3
|
+
*/
|
|
4
|
+
export { UserTokenStrategy } from './user-token.js';
|
|
5
|
+
export { TempTokenStrategy } from './temp-token.js';
|
|
6
|
+
export { SsoTokenStrategy } from './sso-token.js';
|
|
7
|
+
export { TokenCache } from './token-cache.js';
|
|
8
|
+
import { UserTokenStrategy } from './user-token.js';
|
|
9
|
+
import { TempTokenStrategy } from './temp-token.js';
|
|
10
|
+
import { SsoTokenStrategy } from './sso-token.js';
|
|
11
|
+
/**
|
|
12
|
+
* Factory function to create the appropriate auth strategy
|
|
13
|
+
*/
|
|
14
|
+
export function createAuthStrategy(authConfig, context) {
|
|
15
|
+
switch (authConfig.type) {
|
|
16
|
+
case 'user-token':
|
|
17
|
+
return new UserTokenStrategy(authConfig.userToken, context);
|
|
18
|
+
case 'temp-token':
|
|
19
|
+
return new TempTokenStrategy(authConfig.userToken, context, {
|
|
20
|
+
appToken: authConfig.appToken,
|
|
21
|
+
tokenLifespanMs: authConfig.tokenLifespanMs,
|
|
22
|
+
});
|
|
23
|
+
case 'sso':
|
|
24
|
+
return new SsoTokenStrategy(authConfig.samlToken, context);
|
|
25
|
+
default:
|
|
26
|
+
throw new Error(`Unknown auth type: ${authConfig.type}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAI9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAChC,UAAsB,EACtB,OAAoB;IAEpB,QAAQ,UAAU,CAAC,IAAI,EAAE,CAAC;QACxB,KAAK,YAAY;YACf,OAAO,IAAI,iBAAiB,CAAC,UAAU,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAE9D,KAAK,YAAY;YACf,OAAO,IAAI,iBAAiB,CAAC,UAAU,CAAC,SAAS,EAAE,OAAO,EAAE;gBAC1D,QAAQ,EAAE,UAAU,CAAC,QAAQ;gBAC7B,eAAe,EAAE,UAAU,CAAC,eAAe;aAC5C,CAAC,CAAC;QAEL,KAAK,KAAK;YACR,OAAO,IAAI,gBAAgB,CAAC,UAAU,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAE7D;YACE,MAAM,IAAI,KAAK,CAAC,sBAAuB,UAA+B,CAAC,IAAI,EAAE,CAAC,CAAC;IACnF,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSO Token Authentication Strategy
|
|
3
|
+
*
|
|
4
|
+
* Exchanges a SAML token for a QuickBase temporary token via OAuth token exchange.
|
|
5
|
+
*/
|
|
6
|
+
import type { AuthStrategy, AuthContext } from './types.js';
|
|
7
|
+
export declare class SsoTokenStrategy implements AuthStrategy {
|
|
8
|
+
private readonly samlToken;
|
|
9
|
+
private readonly realm;
|
|
10
|
+
private readonly baseUrl;
|
|
11
|
+
private readonly fetchApi;
|
|
12
|
+
private readonly logger;
|
|
13
|
+
private readonly cache;
|
|
14
|
+
/**
|
|
15
|
+
* Track pending SSO token exchange to deduplicate concurrent requests
|
|
16
|
+
*/
|
|
17
|
+
private pendingExchange;
|
|
18
|
+
constructor(samlToken: string, context: AuthContext, options?: {
|
|
19
|
+
tokenLifespanMs?: number;
|
|
20
|
+
});
|
|
21
|
+
private get cacheKey();
|
|
22
|
+
getToken(_dbid?: string): Promise<string>;
|
|
23
|
+
private exchangeToken;
|
|
24
|
+
getAuthorizationHeader(token: string): string;
|
|
25
|
+
handleAuthError(_dbid?: string): Promise<boolean>;
|
|
26
|
+
invalidate(_dbid?: string): void;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=sso-token.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sso-token.d.ts","sourceRoot":"","sources":["../../src/auth/sso-token.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAM5D,qBAAa,gBAAiB,YAAW,YAAY;IACnD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAe;IACxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IAEnC;;OAEG;IACH,OAAO,CAAC,eAAe,CAAgC;gBAGrD,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,WAAW,EACpB,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,MAAM,CAAA;KAAE;IAaxC,OAAO,KAAK,QAAQ,GAEnB;IAEK,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YA2BjC,aAAa;IA+C3B,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAIvC,eAAe,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAevD,UAAU,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI;CAIjC"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSO Token Authentication Strategy
|
|
3
|
+
*
|
|
4
|
+
* Exchanges a SAML token for a QuickBase temporary token via OAuth token exchange.
|
|
5
|
+
*/
|
|
6
|
+
import { TokenCache } from './token-cache.js';
|
|
7
|
+
import { DEFAULT_CONFIG } from '../core/types.js';
|
|
8
|
+
import { AuthenticationError } from '../core/errors.js';
|
|
9
|
+
export class SsoTokenStrategy {
|
|
10
|
+
samlToken;
|
|
11
|
+
realm;
|
|
12
|
+
baseUrl;
|
|
13
|
+
fetchApi;
|
|
14
|
+
logger;
|
|
15
|
+
cache;
|
|
16
|
+
/**
|
|
17
|
+
* Track pending SSO token exchange to deduplicate concurrent requests
|
|
18
|
+
*/
|
|
19
|
+
pendingExchange = null;
|
|
20
|
+
constructor(samlToken, context, options) {
|
|
21
|
+
this.samlToken = samlToken;
|
|
22
|
+
this.realm = context.realm;
|
|
23
|
+
this.baseUrl = context.baseUrl;
|
|
24
|
+
this.fetchApi = context.fetchApi;
|
|
25
|
+
this.logger = context.logger;
|
|
26
|
+
// SSO tokens are global, not per-dbid, so we use a single cache key
|
|
27
|
+
this.cache = new TokenCache(options?.tokenLifespanMs ?? DEFAULT_CONFIG.tempTokenLifespanMs);
|
|
28
|
+
}
|
|
29
|
+
get cacheKey() {
|
|
30
|
+
return '__sso__';
|
|
31
|
+
}
|
|
32
|
+
async getToken(_dbid) {
|
|
33
|
+
// Check cache first
|
|
34
|
+
const cachedToken = this.cache.get(this.cacheKey);
|
|
35
|
+
if (cachedToken) {
|
|
36
|
+
this.logger.token('cache-hit');
|
|
37
|
+
return cachedToken;
|
|
38
|
+
}
|
|
39
|
+
this.logger.token('cache-miss');
|
|
40
|
+
// Check if there's already an exchange in progress
|
|
41
|
+
if (this.pendingExchange) {
|
|
42
|
+
this.logger.debug('Waiting for existing SSO token exchange');
|
|
43
|
+
return this.pendingExchange;
|
|
44
|
+
}
|
|
45
|
+
// Exchange SAML token for QuickBase token
|
|
46
|
+
const exchangePromise = this.exchangeToken();
|
|
47
|
+
this.pendingExchange = exchangePromise;
|
|
48
|
+
try {
|
|
49
|
+
return await exchangePromise;
|
|
50
|
+
}
|
|
51
|
+
finally {
|
|
52
|
+
this.pendingExchange = null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async exchangeToken() {
|
|
56
|
+
this.logger.token('fetch');
|
|
57
|
+
const url = `${this.baseUrl}/auth/oauth/token`;
|
|
58
|
+
const body = new URLSearchParams({
|
|
59
|
+
grant_type: 'urn:ietf:params:oauth:grant-type:token-exchange',
|
|
60
|
+
requested_token_type: 'urn:quickbase:params:oauth:token-type:temp_token',
|
|
61
|
+
subject_token: this.samlToken,
|
|
62
|
+
subject_token_type: 'urn:ietf:params:oauth:token-type:saml2',
|
|
63
|
+
});
|
|
64
|
+
const response = await this.fetchApi(url, {
|
|
65
|
+
method: 'POST',
|
|
66
|
+
headers: {
|
|
67
|
+
'QB-Realm-Hostname': `${this.realm}.quickbase.com`,
|
|
68
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
69
|
+
},
|
|
70
|
+
body: body.toString(),
|
|
71
|
+
});
|
|
72
|
+
if (!response.ok) {
|
|
73
|
+
const errorText = await response.text();
|
|
74
|
+
throw new AuthenticationError(`SSO token exchange failed: ${response.status} ${errorText}`, { rayId: response.headers.get('qb-api-ray') || undefined });
|
|
75
|
+
}
|
|
76
|
+
const data = await response.json();
|
|
77
|
+
const token = data.access_token;
|
|
78
|
+
if (!token) {
|
|
79
|
+
throw new AuthenticationError('No access_token in SSO exchange response');
|
|
80
|
+
}
|
|
81
|
+
// Cache the token
|
|
82
|
+
this.cache.set(this.cacheKey, token);
|
|
83
|
+
this.logger.debug('Cached new SSO token');
|
|
84
|
+
return token;
|
|
85
|
+
}
|
|
86
|
+
getAuthorizationHeader(token) {
|
|
87
|
+
return `QB-TEMP-TOKEN ${token}`;
|
|
88
|
+
}
|
|
89
|
+
async handleAuthError(_dbid) {
|
|
90
|
+
// Invalidate the cached token
|
|
91
|
+
this.invalidate();
|
|
92
|
+
// Try to exchange for a new token
|
|
93
|
+
try {
|
|
94
|
+
this.logger.token('refresh');
|
|
95
|
+
await this.getToken();
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
this.logger.error('Failed to refresh SSO token', error);
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
invalidate(_dbid) {
|
|
104
|
+
this.logger.token('expire');
|
|
105
|
+
this.cache.clear();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=sso-token.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sso-token.js","sourceRoot":"","sources":["../../src/auth/sso-token.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Temporary Token Authentication Strategy
|
|
3
|
+
*
|
|
4
|
+
* Fetches database-specific temporary tokens from the QuickBase API.
|
|
5
|
+
* Tokens are cached and automatically refreshed when expired.
|
|
6
|
+
*/
|
|
7
|
+
import type { AuthStrategy, AuthContext } from './types.js';
|
|
8
|
+
export declare class TempTokenStrategy implements AuthStrategy {
|
|
9
|
+
private readonly userToken?;
|
|
10
|
+
private readonly appToken?;
|
|
11
|
+
private readonly realm;
|
|
12
|
+
private readonly baseUrl;
|
|
13
|
+
private readonly fetchApi;
|
|
14
|
+
private readonly logger;
|
|
15
|
+
private readonly cache;
|
|
16
|
+
/**
|
|
17
|
+
* Track pending token fetches to deduplicate concurrent requests
|
|
18
|
+
* This prevents multiple simultaneous API calls for the same dbid
|
|
19
|
+
*/
|
|
20
|
+
private readonly pendingFetches;
|
|
21
|
+
constructor(userToken: string | undefined, context: AuthContext, options?: {
|
|
22
|
+
appToken?: string;
|
|
23
|
+
tokenLifespanMs?: number;
|
|
24
|
+
});
|
|
25
|
+
getToken(dbid?: string): Promise<string>;
|
|
26
|
+
private fetchToken;
|
|
27
|
+
getAuthorizationHeader(token: string): string;
|
|
28
|
+
handleAuthError(dbid?: string): Promise<boolean>;
|
|
29
|
+
invalidate(dbid?: string): void;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=temp-token.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"temp-token.d.ts","sourceRoot":"","sources":["../../src/auth/temp-token.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAM5D,qBAAa,iBAAkB,YAAW,YAAY;IACpD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAe;IACxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IAEnC;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAsC;gBAGnE,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,OAAO,EAAE,WAAW,EACpB,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,eAAe,CAAC,EAAE,MAAM,CAAA;KAAE;IAarD,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YAiChC,UAAU;IA2CxB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAIvC,eAAe,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAoBtD,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI;CAShC"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Temporary Token Authentication Strategy
|
|
3
|
+
*
|
|
4
|
+
* Fetches database-specific temporary tokens from the QuickBase API.
|
|
5
|
+
* Tokens are cached and automatically refreshed when expired.
|
|
6
|
+
*/
|
|
7
|
+
import { TokenCache } from './token-cache.js';
|
|
8
|
+
import { DEFAULT_CONFIG } from '../core/types.js';
|
|
9
|
+
import { AuthenticationError } from '../core/errors.js';
|
|
10
|
+
export class TempTokenStrategy {
|
|
11
|
+
userToken;
|
|
12
|
+
appToken;
|
|
13
|
+
realm;
|
|
14
|
+
baseUrl;
|
|
15
|
+
fetchApi;
|
|
16
|
+
logger;
|
|
17
|
+
cache;
|
|
18
|
+
/**
|
|
19
|
+
* Track pending token fetches to deduplicate concurrent requests
|
|
20
|
+
* This prevents multiple simultaneous API calls for the same dbid
|
|
21
|
+
*/
|
|
22
|
+
pendingFetches = new Map();
|
|
23
|
+
constructor(userToken, context, options) {
|
|
24
|
+
this.userToken = userToken;
|
|
25
|
+
this.appToken = options?.appToken;
|
|
26
|
+
this.realm = context.realm;
|
|
27
|
+
this.baseUrl = context.baseUrl;
|
|
28
|
+
this.fetchApi = context.fetchApi;
|
|
29
|
+
this.logger = context.logger;
|
|
30
|
+
this.cache = new TokenCache(options?.tokenLifespanMs ?? DEFAULT_CONFIG.tempTokenLifespanMs);
|
|
31
|
+
}
|
|
32
|
+
async getToken(dbid) {
|
|
33
|
+
if (!dbid) {
|
|
34
|
+
throw new Error('Database ID (dbid) is required for temporary token authentication');
|
|
35
|
+
}
|
|
36
|
+
// Check cache first
|
|
37
|
+
const cachedToken = this.cache.get(dbid);
|
|
38
|
+
if (cachedToken) {
|
|
39
|
+
this.logger.token('cache-hit', dbid);
|
|
40
|
+
return cachedToken;
|
|
41
|
+
}
|
|
42
|
+
this.logger.token('cache-miss', dbid);
|
|
43
|
+
// Check if there's already a fetch in progress for this dbid
|
|
44
|
+
const pendingFetch = this.pendingFetches.get(dbid);
|
|
45
|
+
if (pendingFetch) {
|
|
46
|
+
this.logger.debug(`Waiting for existing token fetch for dbid: ${dbid}`);
|
|
47
|
+
return pendingFetch;
|
|
48
|
+
}
|
|
49
|
+
// Fetch new token
|
|
50
|
+
const fetchPromise = this.fetchToken(dbid);
|
|
51
|
+
this.pendingFetches.set(dbid, fetchPromise);
|
|
52
|
+
try {
|
|
53
|
+
const token = await fetchPromise;
|
|
54
|
+
return token;
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
this.pendingFetches.delete(dbid);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async fetchToken(dbid) {
|
|
61
|
+
this.logger.token('fetch', dbid);
|
|
62
|
+
const url = `${this.baseUrl}/auth/temporary/${dbid}`;
|
|
63
|
+
const headers = {
|
|
64
|
+
'QB-Realm-Hostname': `${this.realm}.quickbase.com`,
|
|
65
|
+
'Authorization': `QB-USER-TOKEN ${this.userToken}`,
|
|
66
|
+
'Content-Type': 'application/json',
|
|
67
|
+
};
|
|
68
|
+
if (this.appToken) {
|
|
69
|
+
headers['QB-App-Token'] = this.appToken;
|
|
70
|
+
}
|
|
71
|
+
const response = await this.fetchApi(url, {
|
|
72
|
+
method: 'GET',
|
|
73
|
+
headers,
|
|
74
|
+
});
|
|
75
|
+
if (!response.ok) {
|
|
76
|
+
const errorText = await response.text();
|
|
77
|
+
throw new AuthenticationError(`Failed to fetch temporary token for ${dbid}: ${response.status} ${errorText}`, { rayId: response.headers.get('qb-api-ray') || undefined });
|
|
78
|
+
}
|
|
79
|
+
const data = await response.json();
|
|
80
|
+
const token = data.temporaryAuthorization;
|
|
81
|
+
if (!token) {
|
|
82
|
+
throw new AuthenticationError(`No temporary token in response for ${dbid}`);
|
|
83
|
+
}
|
|
84
|
+
// Cache the token
|
|
85
|
+
this.cache.set(dbid, token);
|
|
86
|
+
this.logger.debug(`Cached new token for dbid: ${dbid}`);
|
|
87
|
+
return token;
|
|
88
|
+
}
|
|
89
|
+
getAuthorizationHeader(token) {
|
|
90
|
+
return `QB-TEMP-TOKEN ${token}`;
|
|
91
|
+
}
|
|
92
|
+
async handleAuthError(dbid) {
|
|
93
|
+
if (!dbid) {
|
|
94
|
+
this.logger.debug('Auth error without dbid - cannot refresh temp token');
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
// Invalidate the cached token
|
|
98
|
+
this.invalidate(dbid);
|
|
99
|
+
// Try to fetch a new token
|
|
100
|
+
try {
|
|
101
|
+
this.logger.token('refresh', dbid);
|
|
102
|
+
await this.getToken(dbid);
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
this.logger.error(`Failed to refresh temp token for ${dbid}`, error);
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
invalidate(dbid) {
|
|
111
|
+
if (dbid) {
|
|
112
|
+
this.logger.token('expire', dbid);
|
|
113
|
+
this.cache.delete(dbid);
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
// Invalidate all tokens
|
|
117
|
+
this.cache.clear();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=temp-token.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"temp-token.js","sourceRoot":"","sources":["../../src/auth/temp-token.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token cache with TTL support
|
|
3
|
+
*
|
|
4
|
+
* Manages cached temporary tokens with expiration handling.
|
|
5
|
+
* Tokens are stored per-dbid since QuickBase temp tokens are database-specific.
|
|
6
|
+
*/
|
|
7
|
+
export declare class TokenCache {
|
|
8
|
+
private readonly cache;
|
|
9
|
+
private readonly defaultLifespanMs;
|
|
10
|
+
constructor(defaultLifespanMs: number);
|
|
11
|
+
/**
|
|
12
|
+
* Get a cached token if it exists and is not expired
|
|
13
|
+
*/
|
|
14
|
+
get(dbid: string): string | null;
|
|
15
|
+
/**
|
|
16
|
+
* Store a token with optional custom lifespan
|
|
17
|
+
*/
|
|
18
|
+
set(dbid: string, token: string, lifespanMs?: number): void;
|
|
19
|
+
/**
|
|
20
|
+
* Remove a specific token from cache
|
|
21
|
+
*/
|
|
22
|
+
delete(dbid: string): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Clear all cached tokens
|
|
25
|
+
*/
|
|
26
|
+
clear(): void;
|
|
27
|
+
/**
|
|
28
|
+
* Check if a token exists and is valid
|
|
29
|
+
*/
|
|
30
|
+
has(dbid: string): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Get time until token expires (in ms), or null if not cached
|
|
33
|
+
*/
|
|
34
|
+
getTimeToExpiry(dbid: string): number | null;
|
|
35
|
+
/**
|
|
36
|
+
* Get the number of cached tokens
|
|
37
|
+
*/
|
|
38
|
+
get size(): number;
|
|
39
|
+
/**
|
|
40
|
+
* Remove expired entries from cache
|
|
41
|
+
*/
|
|
42
|
+
private cleanup;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=token-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-cache.d.ts","sourceRoot":"","sources":["../../src/auth/token-cache.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAkC;IACxD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;gBAE/B,iBAAiB,EAAE,MAAM;IAIrC;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAehC;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI;IAK3D;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI7B;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI1B;;OAEG;IACH,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAS5C;;OAEG;IACH,IAAI,IAAI,IAAI,MAAM,CAIjB;IAED;;OAEG;IACH,OAAO,CAAC,OAAO;CAQhB"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token cache with TTL support
|
|
3
|
+
*
|
|
4
|
+
* Manages cached temporary tokens with expiration handling.
|
|
5
|
+
* Tokens are stored per-dbid since QuickBase temp tokens are database-specific.
|
|
6
|
+
*/
|
|
7
|
+
export class TokenCache {
|
|
8
|
+
cache = new Map();
|
|
9
|
+
defaultLifespanMs;
|
|
10
|
+
constructor(defaultLifespanMs) {
|
|
11
|
+
this.defaultLifespanMs = defaultLifespanMs;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Get a cached token if it exists and is not expired
|
|
15
|
+
*/
|
|
16
|
+
get(dbid) {
|
|
17
|
+
const entry = this.cache.get(dbid);
|
|
18
|
+
if (!entry) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
// Check if token is expired (with 10 second buffer)
|
|
22
|
+
if (Date.now() >= entry.expiresAt - 10000) {
|
|
23
|
+
this.cache.delete(dbid);
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
return entry.token;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Store a token with optional custom lifespan
|
|
30
|
+
*/
|
|
31
|
+
set(dbid, token, lifespanMs) {
|
|
32
|
+
const expiresAt = Date.now() + (lifespanMs ?? this.defaultLifespanMs);
|
|
33
|
+
this.cache.set(dbid, { token, expiresAt });
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Remove a specific token from cache
|
|
37
|
+
*/
|
|
38
|
+
delete(dbid) {
|
|
39
|
+
return this.cache.delete(dbid);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Clear all cached tokens
|
|
43
|
+
*/
|
|
44
|
+
clear() {
|
|
45
|
+
this.cache.clear();
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Check if a token exists and is valid
|
|
49
|
+
*/
|
|
50
|
+
has(dbid) {
|
|
51
|
+
return this.get(dbid) !== null;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Get time until token expires (in ms), or null if not cached
|
|
55
|
+
*/
|
|
56
|
+
getTimeToExpiry(dbid) {
|
|
57
|
+
const entry = this.cache.get(dbid);
|
|
58
|
+
if (!entry) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
const ttl = entry.expiresAt - Date.now();
|
|
62
|
+
return ttl > 0 ? ttl : null;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Get the number of cached tokens
|
|
66
|
+
*/
|
|
67
|
+
get size() {
|
|
68
|
+
// Clean up expired entries first
|
|
69
|
+
this.cleanup();
|
|
70
|
+
return this.cache.size;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Remove expired entries from cache
|
|
74
|
+
*/
|
|
75
|
+
cleanup() {
|
|
76
|
+
const now = Date.now();
|
|
77
|
+
for (const [dbid, entry] of this.cache) {
|
|
78
|
+
if (now >= entry.expiresAt) {
|
|
79
|
+
this.cache.delete(dbid);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=token-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-cache.js","sourceRoot":"","sources":["../../src/auth/token-cache.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,MAAM,OAAO,UAAU;IACJ,KAAK,GAAG,IAAI,GAAG,EAAuB,CAAC;IACvC,iBAAiB,CAAS;IAE3C,YAAY,iBAAyB;QACnC,IAAI,CAAC,iBAAiB,GAAG,iBAAiB,CAAC;IAC7C,CAAC;IAED;;OAEG;IACH,GAAG,CAAC,IAAY;QACd,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,IAAI,CAAC;QACd,CAAC;QAED,oDAAoD;QACpD,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,KAAK,CAAC,SAAS,GAAG,KAAK,EAAE,CAAC;YAC1C,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACxB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,KAAK,CAAC,KAAK,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,GAAG,CAAC,IAAY,EAAE,KAAa,EAAE,UAAmB;QAClD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,UAAU,IAAI,IAAI,CAAC,iBAAiB,CAAC,CAAC;QACtE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IAC7C,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,IAAY;QACjB,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,GAAG,CAAC,IAAY;QACd,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC;IACjC,CAAC;IAED;;OAEG;IACH,eAAe,CAAC,IAAY;QAC1B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,GAAG,GAAG,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACzC,OAAO,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;IAC9B,CAAC;IAED;;OAEG;IACH,IAAI,IAAI;QACN,iCAAiC;QACjC,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;IACzB,CAAC;IAED;;OAEG;IACK,OAAO;QACb,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACvC,IAAI,GAAG,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;gBAC3B,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC1B,CAAC;QACH,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication strategy types
|
|
3
|
+
*/
|
|
4
|
+
import type { Logger } from '../core/logger.js';
|
|
5
|
+
/**
|
|
6
|
+
* Interface that all auth strategies must implement
|
|
7
|
+
*/
|
|
8
|
+
export interface AuthStrategy {
|
|
9
|
+
/**
|
|
10
|
+
* Get a valid token for the given database ID
|
|
11
|
+
* @param dbid - The database ID (required for temp tokens)
|
|
12
|
+
*/
|
|
13
|
+
getToken(dbid?: string): Promise<string>;
|
|
14
|
+
/**
|
|
15
|
+
* Get the authorization header value
|
|
16
|
+
*/
|
|
17
|
+
getAuthorizationHeader(token: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* Handle authentication errors (e.g., refresh token on 401)
|
|
20
|
+
* @returns true if token was refreshed and request should be retried
|
|
21
|
+
*/
|
|
22
|
+
handleAuthError(dbid?: string): Promise<boolean>;
|
|
23
|
+
/**
|
|
24
|
+
* Invalidate cached tokens (if applicable)
|
|
25
|
+
*/
|
|
26
|
+
invalidate(dbid?: string): void;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Context passed to auth strategy constructors
|
|
30
|
+
*/
|
|
31
|
+
export interface AuthContext {
|
|
32
|
+
realm: string;
|
|
33
|
+
baseUrl: string;
|
|
34
|
+
fetchApi: typeof fetch;
|
|
35
|
+
logger: Logger;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Token cache entry
|
|
39
|
+
*/
|
|
40
|
+
export interface CachedToken {
|
|
41
|
+
token: string;
|
|
42
|
+
expiresAt: number;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/auth/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAEzC;;OAEG;IACH,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IAE9C;;;OAGG;IACH,eAAe,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEjD;;OAEG;IACH,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,KAAK,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/auth/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User Token Authentication Strategy
|
|
3
|
+
*
|
|
4
|
+
* Simplest auth method - uses a persistent user token directly.
|
|
5
|
+
* Token is passed in the Authorization header as "QB-USER-TOKEN {token}"
|
|
6
|
+
*/
|
|
7
|
+
import type { AuthStrategy, AuthContext } from './types.js';
|
|
8
|
+
export declare class UserTokenStrategy implements AuthStrategy {
|
|
9
|
+
private readonly userToken;
|
|
10
|
+
private readonly logger;
|
|
11
|
+
constructor(userToken: string, context: AuthContext);
|
|
12
|
+
getToken(_dbid?: string): Promise<string>;
|
|
13
|
+
getAuthorizationHeader(token: string): string;
|
|
14
|
+
handleAuthError(_dbid?: string): Promise<boolean>;
|
|
15
|
+
invalidate(_dbid?: string): void;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=user-token.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-token.d.ts","sourceRoot":"","sources":["../../src/auth/user-token.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG5D,qBAAa,iBAAkB,YAAW,YAAY;IACpD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW;IAK7C,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAK/C,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAIvC,eAAe,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAMvD,UAAU,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI;CAGjC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User Token Authentication Strategy
|
|
3
|
+
*
|
|
4
|
+
* Simplest auth method - uses a persistent user token directly.
|
|
5
|
+
* Token is passed in the Authorization header as "QB-USER-TOKEN {token}"
|
|
6
|
+
*/
|
|
7
|
+
export class UserTokenStrategy {
|
|
8
|
+
userToken;
|
|
9
|
+
logger;
|
|
10
|
+
constructor(userToken, context) {
|
|
11
|
+
this.userToken = userToken;
|
|
12
|
+
this.logger = context.logger;
|
|
13
|
+
}
|
|
14
|
+
async getToken(_dbid) {
|
|
15
|
+
// User tokens don't change, just return the configured token
|
|
16
|
+
return this.userToken;
|
|
17
|
+
}
|
|
18
|
+
getAuthorizationHeader(token) {
|
|
19
|
+
return `QB-USER-TOKEN ${token}`;
|
|
20
|
+
}
|
|
21
|
+
async handleAuthError(_dbid) {
|
|
22
|
+
// User tokens can't be refreshed - if auth fails, it's a permanent failure
|
|
23
|
+
this.logger.debug('Auth error with user token - cannot refresh');
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
invalidate(_dbid) {
|
|
27
|
+
// No-op for user tokens - they don't have cached state
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=user-token.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-token.js","sourceRoot":"","sources":["../../src/auth/user-token.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,MAAM,OAAO,iBAAiB;IACX,SAAS,CAAS;IAClB,MAAM,CAAS;IAEhC,YAAY,SAAiB,EAAE,OAAoB;QACjD,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,KAAc;QAC3B,6DAA6D;QAC7D,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED,sBAAsB,CAAC,KAAa;QAClC,OAAO,iBAAiB,KAAK,EAAE,CAAC;IAClC,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,KAAc;QAClC,2EAA2E;QAC3E,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,6CAA6C,CAAC,CAAC;QACjE,OAAO,KAAK,CAAC;IACf,CAAC;IAED,UAAU,CAAC,KAAc;QACvB,uDAAuD;IACzD,CAAC;CACF"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client module exports
|
|
3
|
+
*/
|
|
4
|
+
export type { RequestOptions, RequestExecutorOptions } from './request.js';
|
|
5
|
+
export { executeRequest, createRequestExecutor } from './request.js';
|
|
6
|
+
export type { Throttle } from './throttle.js';
|
|
7
|
+
export { SlidingWindowThrottle, NoOpThrottle, createThrottle } from './throttle.js';
|
|
8
|
+
export type { RetryOptions } from './retry.js';
|
|
9
|
+
export { calculateBackoffDelay, getRetryDelay, extractRateLimitInfo, sleep, isRetryableStatus, executeWithRetry, } from './retry.js';
|
|
10
|
+
export type { PaginationOptions, PaginationMetadata, PaginatedResponse, PaginationType, RequestExecutor, PaginatedRequestExecutor, } from './pagination.js';
|
|
11
|
+
export { PaginatedRequest, createPaginatedRequest, detectPaginationType, hasMorePages, findDataKey, isPaginatedResponse, } from './pagination.js';
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,YAAY,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAC3E,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAErE,YAAY,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpF,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EACL,qBAAqB,EACrB,aAAa,EACb,oBAAoB,EACpB,KAAK,EACL,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,YAAY,CAAC;AAEpB,YAAY,EACV,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,cAAc,EACd,eAAe,EACf,wBAAwB,GACzB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,oBAAoB,EACpB,YAAY,EACZ,WAAW,EACX,mBAAmB,GACpB,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client module exports
|
|
3
|
+
*/
|
|
4
|
+
export { executeRequest, createRequestExecutor } from './request.js';
|
|
5
|
+
export { SlidingWindowThrottle, NoOpThrottle, createThrottle } from './throttle.js';
|
|
6
|
+
export { calculateBackoffDelay, getRetryDelay, extractRateLimitInfo, sleep, isRetryableStatus, executeWithRetry, } from './retry.js';
|
|
7
|
+
export { PaginatedRequest, createPaginatedRequest, detectPaginationType, hasMorePages, findDataKey, isPaginatedResponse, } from './pagination.js';
|
|
8
|
+
//# sourceMappingURL=index.js.map
|