@ace-sdk/core 2.3.2 → 2.4.1
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/dist/auth/device-auth.d.ts +39 -0
- package/dist/auth/device-auth.d.ts.map +1 -0
- package/dist/auth/device-auth.js +219 -0
- package/dist/auth/device-auth.js.map +1 -0
- package/dist/auth/index.d.ts +7 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +9 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/utils.d.ts +49 -0
- package/dist/auth/utils.d.ts.map +1 -0
- package/dist/auth/utils.js +156 -0
- package/dist/auth/utils.js.map +1 -0
- package/dist/client/ace-client.d.ts +4 -0
- package/dist/client/ace-client.d.ts.map +1 -1
- package/dist/client/ace-client.js +36 -5
- package/dist/client/ace-client.js.map +1 -1
- package/dist/client/http.d.ts +103 -0
- package/dist/client/http.d.ts.map +1 -0
- package/dist/client/http.js +290 -0
- package/dist/client/http.js.map +1 -0
- package/dist/config/auth.d.ts +48 -0
- package/dist/config/auth.d.ts.map +1 -0
- package/dist/config/auth.js +255 -0
- package/dist/config/auth.js.map +1 -0
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +9 -2
- package/dist/config/loader.js.map +1 -1
- package/dist/config/migration.js +1 -1
- package/dist/config/migration.js.map +1 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -1
- package/dist/types/auth.d.ts +128 -0
- package/dist/types/auth.d.ts.map +1 -0
- package/dist/types/auth.js +31 -0
- package/dist/types/auth.js.map +1 -0
- package/dist/types/config.d.ts +4 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Device Code Authentication Flow (RFC 8628)
|
|
3
|
+
*
|
|
4
|
+
* Implements the OAuth 2.0 Device Authorization Grant for CLI/terminal clients.
|
|
5
|
+
*
|
|
6
|
+
* Flow:
|
|
7
|
+
* 1. Client requests device code: POST /api/v1/auth/device
|
|
8
|
+
* 2. User visits URL and enters code
|
|
9
|
+
* 3. Client polls for token: POST /api/v1/auth/device/token
|
|
10
|
+
* 4. On success, save credentials to config
|
|
11
|
+
*
|
|
12
|
+
* @package @ace-sdk/core
|
|
13
|
+
*/
|
|
14
|
+
import type { DeviceCodeResponse, TokenResponse, DeviceAuthError, LoginOptions, CurrentUser } from '../types/auth.js';
|
|
15
|
+
import type { ILogger } from '../logger/index.js';
|
|
16
|
+
/**
|
|
17
|
+
* Request device code from server
|
|
18
|
+
* POST /api/v1/auth/device
|
|
19
|
+
*/
|
|
20
|
+
export declare function requestDeviceCode(clientType: string, logger?: ILogger, deviceId?: string): Promise<DeviceCodeResponse>;
|
|
21
|
+
/**
|
|
22
|
+
* Poll for token after user authorizes
|
|
23
|
+
* POST /api/v1/auth/device/token
|
|
24
|
+
*/
|
|
25
|
+
export declare function pollForToken(deviceCode: string, _logger?: ILogger): Promise<TokenResponse | DeviceAuthError>;
|
|
26
|
+
/**
|
|
27
|
+
* Complete device code login flow
|
|
28
|
+
*
|
|
29
|
+
* 1. Request device code
|
|
30
|
+
* 2. Open browser / show code to user
|
|
31
|
+
* 3. Poll for token
|
|
32
|
+
* 4. Save credentials
|
|
33
|
+
*/
|
|
34
|
+
export declare function login(options: LoginOptions, logger?: ILogger): Promise<CurrentUser>;
|
|
35
|
+
/**
|
|
36
|
+
* Logout - clear credentials
|
|
37
|
+
*/
|
|
38
|
+
export declare function logout(logger?: ILogger): void;
|
|
39
|
+
//# sourceMappingURL=device-auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-auth.d.ts","sourceRoot":"","sources":["../../src/auth/device-auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EACV,kBAAkB,EAClB,aAAa,EACb,eAAe,EACf,YAAY,EACZ,WAAW,EACZ,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAsBlD;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,EAClB,MAAM,CAAC,EAAE,OAAO,EAChB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,kBAAkB,CAAC,CAwB7B;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAChC,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,OAAO,GAChB,OAAO,CAAC,aAAa,GAAG,eAAe,CAAC,CAuB1C;AAgBD;;;;;;;GAOG;AACH,wBAAsB,KAAK,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,CA+IzF;AAED;;GAEG;AACH,wBAAgB,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAG7C"}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Device Code Authentication Flow (RFC 8628)
|
|
3
|
+
*
|
|
4
|
+
* Implements the OAuth 2.0 Device Authorization Grant for CLI/terminal clients.
|
|
5
|
+
*
|
|
6
|
+
* Flow:
|
|
7
|
+
* 1. Client requests device code: POST /api/v1/auth/device
|
|
8
|
+
* 2. User visits URL and enters code
|
|
9
|
+
* 3. Client polls for token: POST /api/v1/auth/device/token
|
|
10
|
+
* 4. On success, save credentials to config
|
|
11
|
+
*
|
|
12
|
+
* @package @ace-sdk/core
|
|
13
|
+
*/
|
|
14
|
+
import { saveAuthCredentials, clearAuth, getOrCreateDeviceId } from '../config/auth.js';
|
|
15
|
+
import { getEffectiveToken } from './utils.js';
|
|
16
|
+
import { loadConfig } from '../config/loader.js';
|
|
17
|
+
/**
|
|
18
|
+
* Default server URL
|
|
19
|
+
*/
|
|
20
|
+
const DEFAULT_SERVER_URL = 'https://ace-api.code-engine.app';
|
|
21
|
+
/**
|
|
22
|
+
* Get server URL from config or default
|
|
23
|
+
*/
|
|
24
|
+
function getServerUrl() {
|
|
25
|
+
try {
|
|
26
|
+
const config = loadConfig();
|
|
27
|
+
return config.serverUrl || DEFAULT_SERVER_URL;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return DEFAULT_SERVER_URL;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Request device code from server
|
|
35
|
+
* POST /api/v1/auth/device
|
|
36
|
+
*/
|
|
37
|
+
export async function requestDeviceCode(clientType, logger, deviceId) {
|
|
38
|
+
const serverUrl = getServerUrl();
|
|
39
|
+
const url = `${serverUrl}/api/v1/auth/device`;
|
|
40
|
+
logger?.debug(`Requesting device code from ${url}`);
|
|
41
|
+
const response = await fetch(url, {
|
|
42
|
+
method: 'POST',
|
|
43
|
+
headers: {
|
|
44
|
+
'Content-Type': 'application/json'
|
|
45
|
+
},
|
|
46
|
+
body: JSON.stringify({
|
|
47
|
+
client_type: clientType,
|
|
48
|
+
// v4.5.0: Include device_id for device tracking
|
|
49
|
+
...(deviceId && { device_id: deviceId })
|
|
50
|
+
})
|
|
51
|
+
});
|
|
52
|
+
if (!response.ok) {
|
|
53
|
+
const error = await response.text();
|
|
54
|
+
throw new Error(`Failed to request device code: ${error}`);
|
|
55
|
+
}
|
|
56
|
+
return response.json();
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Poll for token after user authorizes
|
|
60
|
+
* POST /api/v1/auth/device/token
|
|
61
|
+
*/
|
|
62
|
+
export async function pollForToken(deviceCode, _logger) {
|
|
63
|
+
const serverUrl = getServerUrl();
|
|
64
|
+
const url = `${serverUrl}/api/v1/auth/device/token`;
|
|
65
|
+
const response = await fetch(url, {
|
|
66
|
+
method: 'POST',
|
|
67
|
+
headers: {
|
|
68
|
+
'Content-Type': 'application/json'
|
|
69
|
+
},
|
|
70
|
+
body: JSON.stringify({ device_code: deviceCode })
|
|
71
|
+
});
|
|
72
|
+
const rawData = await response.json();
|
|
73
|
+
// Server wraps error responses in 'detail' object
|
|
74
|
+
const data = (rawData.detail || rawData);
|
|
75
|
+
// Check for error response
|
|
76
|
+
if (data.error) {
|
|
77
|
+
return data;
|
|
78
|
+
}
|
|
79
|
+
return data;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Check if response is an error
|
|
83
|
+
*/
|
|
84
|
+
function isDeviceAuthError(response) {
|
|
85
|
+
return 'error' in response;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Sleep for specified milliseconds
|
|
89
|
+
*/
|
|
90
|
+
function sleep(ms) {
|
|
91
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Complete device code login flow
|
|
95
|
+
*
|
|
96
|
+
* 1. Request device code
|
|
97
|
+
* 2. Open browser / show code to user
|
|
98
|
+
* 3. Poll for token
|
|
99
|
+
* 4. Save credentials
|
|
100
|
+
*/
|
|
101
|
+
export async function login(options, logger) {
|
|
102
|
+
const { clientType, onUserCode, onSuccess, onProgress, timeout = 300000, // 5 minutes
|
|
103
|
+
signal, noBrowser = false } = options;
|
|
104
|
+
// Check if already logged in
|
|
105
|
+
const existingToken = getEffectiveToken(logger);
|
|
106
|
+
if (existingToken) {
|
|
107
|
+
logger?.warn('Already logged in. Use logout first to switch accounts.');
|
|
108
|
+
}
|
|
109
|
+
// v4.5.0: Get or create device ID (persists across logins)
|
|
110
|
+
const deviceId = getOrCreateDeviceId(logger);
|
|
111
|
+
onProgress?.('Requesting device code...');
|
|
112
|
+
// Step 1: Request device code (include device_id)
|
|
113
|
+
const deviceCode = await requestDeviceCode(clientType, logger, deviceId);
|
|
114
|
+
logger?.debug(`Device code: ${deviceCode.device_code}`);
|
|
115
|
+
logger?.debug(`User code: ${deviceCode.user_code}`);
|
|
116
|
+
logger?.debug(`Verification URL: ${deviceCode.verification_uri}`);
|
|
117
|
+
// Step 2: Show code to user
|
|
118
|
+
onUserCode?.(deviceCode.user_code, deviceCode.verification_uri_complete || deviceCode.verification_uri);
|
|
119
|
+
// Step 3: Open browser (unless noBrowser or SSH mode)
|
|
120
|
+
if (!noBrowser && deviceCode.verification_uri_complete) {
|
|
121
|
+
onProgress?.('Opening browser...');
|
|
122
|
+
try {
|
|
123
|
+
// Dynamic import for open package
|
|
124
|
+
const open = (await import('open')).default;
|
|
125
|
+
await open(deviceCode.verification_uri_complete);
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
logger?.debug('Could not open browser:', err instanceof Error ? err.message : String(err));
|
|
129
|
+
onProgress?.('Could not open browser automatically');
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// Step 4: Poll for token
|
|
133
|
+
onProgress?.('Waiting for authorization...');
|
|
134
|
+
let interval = deviceCode.interval * 1000; // Convert to ms
|
|
135
|
+
const startTime = Date.now();
|
|
136
|
+
const deviceCodeExpiresAt = startTime + (deviceCode.expires_in * 1000);
|
|
137
|
+
while (true) {
|
|
138
|
+
// Check abort signal
|
|
139
|
+
if (signal?.aborted) {
|
|
140
|
+
throw new Error('Login cancelled');
|
|
141
|
+
}
|
|
142
|
+
// Check timeout
|
|
143
|
+
if (Date.now() > startTime + timeout) {
|
|
144
|
+
throw new Error('Login timed out');
|
|
145
|
+
}
|
|
146
|
+
// Check expiry
|
|
147
|
+
if (Date.now() > deviceCodeExpiresAt) {
|
|
148
|
+
throw new Error('Device code expired. Please try again.');
|
|
149
|
+
}
|
|
150
|
+
// Wait before polling
|
|
151
|
+
await sleep(interval);
|
|
152
|
+
// Poll for token
|
|
153
|
+
const response = await pollForToken(deviceCode.device_code, logger);
|
|
154
|
+
if (isDeviceAuthError(response)) {
|
|
155
|
+
switch (response.error) {
|
|
156
|
+
case 'authorization_pending':
|
|
157
|
+
// User hasn't authorized yet, keep polling
|
|
158
|
+
logger?.debug('Authorization pending...');
|
|
159
|
+
continue;
|
|
160
|
+
case 'slow_down':
|
|
161
|
+
// Slow down polling (+5 seconds per RFC 8628)
|
|
162
|
+
interval += 5000;
|
|
163
|
+
logger?.debug(`Slowing down polling to ${interval / 1000}s`);
|
|
164
|
+
continue;
|
|
165
|
+
case 'expired_token':
|
|
166
|
+
throw new Error('Device code expired. Please try again.');
|
|
167
|
+
case 'access_denied':
|
|
168
|
+
throw new Error('Authorization denied by user.');
|
|
169
|
+
default:
|
|
170
|
+
throw new Error(response.error_description || `Auth error: ${response.error}`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
// Success! Save credentials
|
|
174
|
+
onProgress?.('Saving credentials...');
|
|
175
|
+
const tokenResponse = response;
|
|
176
|
+
// Handle both nested and flat response formats
|
|
177
|
+
// Nested: { user: { user_id, email }, organizations }
|
|
178
|
+
// Flat: { user_id, email, organizations }
|
|
179
|
+
const userData = tokenResponse.user || tokenResponse;
|
|
180
|
+
const orgs = tokenResponse.organizations || [];
|
|
181
|
+
// v4.5.0: Calculate expiration timestamps from seconds
|
|
182
|
+
const now = new Date();
|
|
183
|
+
const tokenExpiresAt = tokenResponse.expires_in
|
|
184
|
+
? new Date(now.getTime() + tokenResponse.expires_in * 1000).toISOString()
|
|
185
|
+
: tokenResponse.token_expires_at;
|
|
186
|
+
const tokenRefreshExpiresAt = tokenResponse.refresh_expires_in
|
|
187
|
+
? new Date(now.getTime() + tokenResponse.refresh_expires_in * 1000).toISOString()
|
|
188
|
+
: tokenResponse.refresh_expires_at;
|
|
189
|
+
saveAuthCredentials({
|
|
190
|
+
token: tokenResponse.access_token,
|
|
191
|
+
user_id: userData.user_id,
|
|
192
|
+
email: userData.email,
|
|
193
|
+
organizations: orgs,
|
|
194
|
+
authenticated_at: new Date().toISOString(),
|
|
195
|
+
// v4.5.0: Token expiration and refresh
|
|
196
|
+
refresh_token: tokenResponse.refresh_token,
|
|
197
|
+
expires_at: tokenExpiresAt,
|
|
198
|
+
refresh_expires_at: tokenRefreshExpiresAt
|
|
199
|
+
}, logger);
|
|
200
|
+
const currentUser = {
|
|
201
|
+
user_id: userData.user_id,
|
|
202
|
+
email: userData.email,
|
|
203
|
+
name: userData.name,
|
|
204
|
+
image_url: userData.image_url,
|
|
205
|
+
organizations: orgs,
|
|
206
|
+
authenticated_at: new Date().toISOString()
|
|
207
|
+
};
|
|
208
|
+
onSuccess?.(currentUser);
|
|
209
|
+
return currentUser;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Logout - clear credentials
|
|
214
|
+
*/
|
|
215
|
+
export function logout(logger) {
|
|
216
|
+
clearAuth(logger);
|
|
217
|
+
logger?.info('Logged out successfully');
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=device-auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-auth.js","sourceRoot":"","sources":["../../src/auth/device-auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Module Index
|
|
3
|
+
* @package @ace-sdk/core
|
|
4
|
+
*/
|
|
5
|
+
export { requestDeviceCode, pollForToken, login, logout } from './device-auth.js';
|
|
6
|
+
export { getEffectiveToken, getEffectiveOrgId, isAuthenticated, isUserAuthenticated, getCurrentUser, getTokenType, maskToken } from './utils.js';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,KAAK,EACL,MAAM,EACP,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,mBAAmB,EACnB,cAAc,EACd,YAAY,EACZ,SAAS,EACV,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Module Index
|
|
3
|
+
* @package @ace-sdk/core
|
|
4
|
+
*/
|
|
5
|
+
// Device code authentication
|
|
6
|
+
export { requestDeviceCode, pollForToken, login, logout } from './device-auth.js';
|
|
7
|
+
// Auth utilities
|
|
8
|
+
export { getEffectiveToken, getEffectiveOrgId, isAuthenticated, isUserAuthenticated, getCurrentUser, getTokenType, maskToken } from './utils.js';
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,6BAA6B;AAC7B,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,KAAK,EACL,MAAM,EACP,MAAM,kBAAkB,CAAC;AAE1B,iBAAiB;AACjB,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,mBAAmB,EACnB,cAAc,EACd,YAAY,EACZ,SAAS,EACV,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication Utilities
|
|
3
|
+
*
|
|
4
|
+
* Helper functions for checking auth state and getting effective credentials.
|
|
5
|
+
*
|
|
6
|
+
* @package @ace-sdk/core
|
|
7
|
+
*/
|
|
8
|
+
import type { CurrentUser, TokenType } from '../types/auth.js';
|
|
9
|
+
import type { ILogger } from '../logger/index.js';
|
|
10
|
+
/**
|
|
11
|
+
* Get effective API token (user token takes priority over legacy org token)
|
|
12
|
+
*
|
|
13
|
+
* Priority:
|
|
14
|
+
* 1. Environment: ACE_API_TOKEN
|
|
15
|
+
* 2. Config: auth.token (user token)
|
|
16
|
+
* 3. Config: apiToken (legacy org token)
|
|
17
|
+
*/
|
|
18
|
+
export declare function getEffectiveToken(logger?: ILogger): string | null;
|
|
19
|
+
/**
|
|
20
|
+
* Get effective organization ID
|
|
21
|
+
*
|
|
22
|
+
* Priority:
|
|
23
|
+
* 1. Environment: ACE_ORG_ID
|
|
24
|
+
* 2. Config: default_org_id
|
|
25
|
+
* 3. First org in auth.organizations
|
|
26
|
+
* 4. Extract from legacy org token
|
|
27
|
+
*/
|
|
28
|
+
export declare function getEffectiveOrgId(logger?: ILogger): string | null;
|
|
29
|
+
/**
|
|
30
|
+
* Check if user is authenticated
|
|
31
|
+
*/
|
|
32
|
+
export declare function isAuthenticated(logger?: ILogger): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Check if using user token (vs legacy org token)
|
|
35
|
+
*/
|
|
36
|
+
export declare function isUserAuthenticated(logger?: ILogger): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Get current user info from config
|
|
39
|
+
*/
|
|
40
|
+
export declare function getCurrentUser(logger?: ILogger): CurrentUser | null;
|
|
41
|
+
/**
|
|
42
|
+
* Get token type of current effective token
|
|
43
|
+
*/
|
|
44
|
+
export declare function getTokenType(logger?: ILogger): TokenType;
|
|
45
|
+
/**
|
|
46
|
+
* Mask token for display (show first 15 chars + ...)
|
|
47
|
+
*/
|
|
48
|
+
export declare function maskToken(token: string): string;
|
|
49
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/auth/utils.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAY,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAEzE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAGlD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAiCjE;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CA0CjE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAGzD;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAI7D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,WAAW,GAAG,IAAI,CA2BnE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAGxD;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAI/C"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication Utilities
|
|
3
|
+
*
|
|
4
|
+
* Helper functions for checking auth state and getting effective credentials.
|
|
5
|
+
*
|
|
6
|
+
* @package @ace-sdk/core
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync, readFileSync } from 'fs';
|
|
9
|
+
import { detectTokenType } from '../types/auth.js';
|
|
10
|
+
import { getXdgConfigPath } from '../config/xdg.js';
|
|
11
|
+
/**
|
|
12
|
+
* Get effective API token (user token takes priority over legacy org token)
|
|
13
|
+
*
|
|
14
|
+
* Priority:
|
|
15
|
+
* 1. Environment: ACE_API_TOKEN
|
|
16
|
+
* 2. Config: auth.token (user token)
|
|
17
|
+
* 3. Config: apiToken (legacy org token)
|
|
18
|
+
*/
|
|
19
|
+
export function getEffectiveToken(logger) {
|
|
20
|
+
// Priority 1: Environment variable
|
|
21
|
+
if (process.env.ACE_API_TOKEN) {
|
|
22
|
+
logger?.debug('Using token from ACE_API_TOKEN');
|
|
23
|
+
return process.env.ACE_API_TOKEN;
|
|
24
|
+
}
|
|
25
|
+
// Load config
|
|
26
|
+
const configPath = getXdgConfigPath();
|
|
27
|
+
if (!existsSync(configPath)) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
const config = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
32
|
+
// Priority 2: User token (new auth block)
|
|
33
|
+
if (config.auth?.token) {
|
|
34
|
+
logger?.debug('Using user token from auth.token');
|
|
35
|
+
return config.auth.token;
|
|
36
|
+
}
|
|
37
|
+
// Priority 3: Legacy org token
|
|
38
|
+
if (config.apiToken) {
|
|
39
|
+
logger?.debug('Using legacy token from apiToken');
|
|
40
|
+
return config.apiToken;
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
logger?.warn('Failed to read token from config');
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Get effective organization ID
|
|
51
|
+
*
|
|
52
|
+
* Priority:
|
|
53
|
+
* 1. Environment: ACE_ORG_ID
|
|
54
|
+
* 2. Config: default_org_id
|
|
55
|
+
* 3. First org in auth.organizations
|
|
56
|
+
* 4. Extract from legacy org token
|
|
57
|
+
*/
|
|
58
|
+
export function getEffectiveOrgId(logger) {
|
|
59
|
+
// Priority 1: Environment variable
|
|
60
|
+
if (process.env.ACE_ORG_ID) {
|
|
61
|
+
logger?.debug('Using org from ACE_ORG_ID');
|
|
62
|
+
return process.env.ACE_ORG_ID;
|
|
63
|
+
}
|
|
64
|
+
// Load config
|
|
65
|
+
const configPath = getXdgConfigPath();
|
|
66
|
+
if (!existsSync(configPath)) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
const config = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
71
|
+
// Priority 2: Explicit default_org_id
|
|
72
|
+
if (config.default_org_id) {
|
|
73
|
+
logger?.debug('Using default org from config');
|
|
74
|
+
return config.default_org_id;
|
|
75
|
+
}
|
|
76
|
+
// Priority 3: First org from user auth
|
|
77
|
+
if (config.auth?.organizations?.length > 0) {
|
|
78
|
+
const firstOrg = config.auth.organizations[0].org_id;
|
|
79
|
+
logger?.debug(`Using first org: ${firstOrg}`);
|
|
80
|
+
return firstOrg;
|
|
81
|
+
}
|
|
82
|
+
// Priority 4: Extract from legacy token
|
|
83
|
+
if (config.apiToken && detectTokenType(config.apiToken) === 'org') {
|
|
84
|
+
// Legacy token format: ace_{orgId8chars}{random}
|
|
85
|
+
const orgId = config.apiToken.substring(4, 12);
|
|
86
|
+
logger?.debug(`Extracted org from legacy token: ${orgId}`);
|
|
87
|
+
return orgId;
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
logger?.warn('Failed to read org from config');
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Check if user is authenticated
|
|
98
|
+
*/
|
|
99
|
+
export function isAuthenticated(logger) {
|
|
100
|
+
const token = getEffectiveToken(logger);
|
|
101
|
+
return !!token;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Check if using user token (vs legacy org token)
|
|
105
|
+
*/
|
|
106
|
+
export function isUserAuthenticated(logger) {
|
|
107
|
+
const token = getEffectiveToken(logger);
|
|
108
|
+
if (!token)
|
|
109
|
+
return false;
|
|
110
|
+
return detectTokenType(token) === 'user';
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Get current user info from config
|
|
114
|
+
*/
|
|
115
|
+
export function getCurrentUser(logger) {
|
|
116
|
+
const configPath = getXdgConfigPath();
|
|
117
|
+
if (!existsSync(configPath)) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
try {
|
|
121
|
+
const config = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
122
|
+
if (!config.auth) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
const auth = config.auth;
|
|
126
|
+
return {
|
|
127
|
+
user_id: auth.user_id,
|
|
128
|
+
email: auth.email,
|
|
129
|
+
organizations: auth.organizations || [],
|
|
130
|
+
default_org_id: config.default_org_id,
|
|
131
|
+
authenticated_at: auth.authenticated_at
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
logger?.warn('Failed to read current user from config');
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Get token type of current effective token
|
|
141
|
+
*/
|
|
142
|
+
export function getTokenType(logger) {
|
|
143
|
+
const token = getEffectiveToken(logger);
|
|
144
|
+
return token ? detectTokenType(token) : 'unknown';
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Mask token for display (show first 15 chars + ...)
|
|
148
|
+
*/
|
|
149
|
+
export function maskToken(token) {
|
|
150
|
+
if (!token)
|
|
151
|
+
return '(none)';
|
|
152
|
+
if (token.length <= 15)
|
|
153
|
+
return token;
|
|
154
|
+
return `${token.substring(0, 15)}...`;
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/auth/utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -18,12 +18,16 @@ import { ILogger } from '../logger/index.js';
|
|
|
18
18
|
export interface AceClientOptions {
|
|
19
19
|
/** Logger for output (optional - defaults to silent) */
|
|
20
20
|
logger?: ILogger;
|
|
21
|
+
/** Enable auto-refresh of expired tokens (v4.5.0, default: true for user tokens) */
|
|
22
|
+
autoRefresh?: boolean;
|
|
21
23
|
}
|
|
22
24
|
export declare class AceClient {
|
|
23
25
|
private config;
|
|
24
26
|
private localCache;
|
|
25
27
|
private memoryCache?;
|
|
26
28
|
private logger;
|
|
29
|
+
private autoRefresh;
|
|
30
|
+
private currentToken;
|
|
27
31
|
private configCache;
|
|
28
32
|
private CONFIG_CACHE_TTL;
|
|
29
33
|
constructor(config: AceConfig | AceContext, options?: AceClientOptions);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ace-client.d.ts","sourceRoot":"","sources":["../../src/client/ace-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,cAAc,EACd,cAAc,EACd,0BAA0B,EAC1B,4BAA4B,EAC5B,gBAAgB,EAChB,iBAAiB,EAEjB,aAAa,EACd,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,iBAAiB,EAAe,MAAM,yBAAyB,CAAC;AACzE,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"ace-client.d.ts","sourceRoot":"","sources":["../../src/client/ace-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,11 +9,15 @@
|
|
|
9
9
|
* @package @ace-sdk/core
|
|
10
10
|
*/
|
|
11
11
|
import { LocalCacheService } from '../cache/local-cache.js';
|
|
12
|
+
import { ensureValidToken } from './http.js';
|
|
13
|
+
import { isUserToken } from '../types/auth.js';
|
|
12
14
|
export class AceClient {
|
|
13
15
|
config;
|
|
14
16
|
localCache;
|
|
15
17
|
memoryCache; // RAM cache
|
|
16
18
|
logger;
|
|
19
|
+
autoRefresh;
|
|
20
|
+
currentToken; // Tracks potentially refreshed token
|
|
17
21
|
// Config cache (v3.6.0+) - 1-hour TTL (configurable)
|
|
18
22
|
configCache = { data: null, timestamp: 0 };
|
|
19
23
|
CONFIG_CACHE_TTL = 60 * 60 * 1000; // 60 minutes (1 hour)
|
|
@@ -26,6 +30,9 @@ export class AceClient {
|
|
|
26
30
|
const projectId = config.projectId;
|
|
27
31
|
const orgId = 'orgId' in config ? config.orgId : this.extractOrgId(apiToken);
|
|
28
32
|
const cacheTtlMinutes = config.cacheTtlMinutes;
|
|
33
|
+
// v4.5.0: Auto-refresh enabled by default for user tokens
|
|
34
|
+
this.currentToken = apiToken;
|
|
35
|
+
this.autoRefresh = options?.autoRefresh !== false && isUserToken(apiToken);
|
|
29
36
|
// Initialize local SQLite cache (optional - graceful fallback for Node 25+)
|
|
30
37
|
try {
|
|
31
38
|
const cacheConfig = {
|
|
@@ -77,14 +84,38 @@ export class AceClient {
|
|
|
77
84
|
this.logger.trace(`Request body: ${truncated}`);
|
|
78
85
|
}
|
|
79
86
|
}
|
|
87
|
+
// v4.5.0: Auto-refresh token if enabled and using user token
|
|
88
|
+
let token = this.currentToken;
|
|
89
|
+
if (this.autoRefresh) {
|
|
90
|
+
try {
|
|
91
|
+
const result = await ensureValidToken(this.config.serverUrl, this.logger);
|
|
92
|
+
token = result.token;
|
|
93
|
+
if (result.wasRefreshed) {
|
|
94
|
+
this.currentToken = token;
|
|
95
|
+
this.logger?.debug('Token refreshed automatically');
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
// If token refresh fails, try with current token anyway
|
|
100
|
+
// (might work if server hasn't invalidated it yet)
|
|
101
|
+
this.logger?.debug('Token refresh failed, using current token');
|
|
102
|
+
}
|
|
103
|
+
}
|
|
80
104
|
const startTime = Date.now();
|
|
105
|
+
// Build headers with X-ACE-Org for user tokens
|
|
106
|
+
const headers = {
|
|
107
|
+
'Content-Type': 'application/json',
|
|
108
|
+
'Authorization': `Bearer ${token}`,
|
|
109
|
+
'X-ACE-Project': this.config.projectId
|
|
110
|
+
};
|
|
111
|
+
// Add X-ACE-Org header for user tokens (selects which org per-request)
|
|
112
|
+
const orgId = 'orgId' in this.config ? this.config.orgId : this.extractOrgId(this.config.apiToken);
|
|
113
|
+
if (orgId && orgId !== 'default') {
|
|
114
|
+
headers['X-ACE-Org'] = orgId;
|
|
115
|
+
}
|
|
81
116
|
const response = await fetch(url, {
|
|
82
117
|
method,
|
|
83
|
-
headers
|
|
84
|
-
'Content-Type': 'application/json',
|
|
85
|
-
'Authorization': `Bearer ${this.config.apiToken}`,
|
|
86
|
-
'X-ACE-Project': this.config.projectId
|
|
87
|
-
},
|
|
118
|
+
headers,
|
|
88
119
|
body: body ? JSON.stringify(body) : undefined
|
|
89
120
|
});
|
|
90
121
|
const duration = Date.now() - startTime;
|