@vybestack/llxprt-code-auth 0.10.0-nightly.260613.1adad3b34
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/.last_build +0 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/src/auth-precedence-resolver.d.ts +147 -0
- package/dist/src/auth-precedence-resolver.js +542 -0
- package/dist/src/auth-precedence-resolver.js.map +1 -0
- package/dist/src/flows/anthropic-device-flow.d.ts +57 -0
- package/dist/src/flows/anthropic-device-flow.js +231 -0
- package/dist/src/flows/anthropic-device-flow.js.map +1 -0
- package/dist/src/flows/codex-device-flow.d.ts +114 -0
- package/dist/src/flows/codex-device-flow.js +437 -0
- package/dist/src/flows/codex-device-flow.js.map +1 -0
- package/dist/src/flows/qwen-device-flow.d.ts +45 -0
- package/dist/src/flows/qwen-device-flow.js +183 -0
- package/dist/src/flows/qwen-device-flow.js.map +1 -0
- package/dist/src/index.d.ts +34 -0
- package/dist/src/index.js +26 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/interfaces/debug-logger.d.ts +31 -0
- package/dist/src/interfaces/debug-logger.js +6 -0
- package/dist/src/interfaces/debug-logger.js.map +1 -0
- package/dist/src/interfaces/index.d.ts +18 -0
- package/dist/src/interfaces/index.js +10 -0
- package/dist/src/interfaces/index.js.map +1 -0
- package/dist/src/interfaces/provider-key-storage.d.ts +26 -0
- package/dist/src/interfaces/provider-key-storage.js +6 -0
- package/dist/src/interfaces/provider-key-storage.js.map +1 -0
- package/dist/src/interfaces/runtime-context.d.ts +37 -0
- package/dist/src/interfaces/runtime-context.js +6 -0
- package/dist/src/interfaces/runtime-context.js.map +1 -0
- package/dist/src/interfaces/secure-store.d.ts +47 -0
- package/dist/src/interfaces/secure-store.js +6 -0
- package/dist/src/interfaces/secure-store.js.map +1 -0
- package/dist/src/interfaces/settings-service.d.ts +25 -0
- package/dist/src/interfaces/settings-service.js +6 -0
- package/dist/src/interfaces/settings-service.js.map +1 -0
- package/dist/src/keyring-token-store.d.ts +96 -0
- package/dist/src/keyring-token-store.js +391 -0
- package/dist/src/keyring-token-store.js.map +1 -0
- package/dist/src/oauth-errors.d.ts +173 -0
- package/dist/src/oauth-errors.js +465 -0
- package/dist/src/oauth-errors.js.map +1 -0
- package/dist/src/precedence.d.ts +115 -0
- package/dist/src/precedence.js +278 -0
- package/dist/src/precedence.js.map +1 -0
- package/dist/src/proxy/framing.d.ts +35 -0
- package/dist/src/proxy/framing.js +86 -0
- package/dist/src/proxy/framing.js.map +1 -0
- package/dist/src/proxy/proxy-provider-key-storage.d.ts +23 -0
- package/dist/src/proxy/proxy-provider-key-storage.js +41 -0
- package/dist/src/proxy/proxy-provider-key-storage.js.map +1 -0
- package/dist/src/proxy/proxy-socket-client.d.ts +43 -0
- package/dist/src/proxy/proxy-socket-client.js +219 -0
- package/dist/src/proxy/proxy-socket-client.js.map +1 -0
- package/dist/src/proxy/proxy-token-store.d.ts +39 -0
- package/dist/src/proxy/proxy-token-store.js +87 -0
- package/dist/src/proxy/proxy-token-store.js.map +1 -0
- package/dist/src/token-merge.d.ts +16 -0
- package/dist/src/token-merge.js +13 -0
- package/dist/src/token-merge.js.map +1 -0
- package/dist/src/token-sanitization.d.ts +16 -0
- package/dist/src/token-sanitization.js +10 -0
- package/dist/src/token-sanitization.js.map +1 -0
- package/dist/src/token-store.d.ts +93 -0
- package/dist/src/token-store.js +7 -0
- package/dist/src/token-store.js.map +1 -0
- package/dist/src/types.d.ts +204 -0
- package/dist/src/types.js +86 -0
- package/dist/src/types.js.map +1 -0
- package/package.json +42 -0
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Vybestack LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { randomBytes, createHash } from 'crypto';
|
|
7
|
+
import { CodexOAuthTokenSchema, CodexTokenResponseSchema, } from '../types.js';
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
/**
|
|
10
|
+
* Codex-specific OAuth configuration
|
|
11
|
+
* Exported for reuse by CLI and other consumers to ensure single source of truth
|
|
12
|
+
*/
|
|
13
|
+
export const CODEX_CONFIG = {
|
|
14
|
+
clientId: 'app_EMoamEEZ73f0CkXaXp7hrann',
|
|
15
|
+
issuer: 'https://auth.openai.com',
|
|
16
|
+
tokenEndpoint: 'https://auth.openai.com/oauth/token',
|
|
17
|
+
authorizationEndpoint: 'https://auth.openai.com/oauth/authorize',
|
|
18
|
+
deviceAuthUserCodeEndpoint: 'https://auth.openai.com/api/accounts/deviceauth/usercode',
|
|
19
|
+
deviceAuthTokenEndpoint: 'https://auth.openai.com/api/accounts/deviceauth/token',
|
|
20
|
+
deviceAuthCallbackUri: 'https://auth.openai.com/deviceauth/callback',
|
|
21
|
+
scopes: ['openid', 'profile', 'email', 'offline_access'],
|
|
22
|
+
originator: 'codex_cli_rs',
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* JWT payload schema for account_id extraction
|
|
26
|
+
* Handles multiple possible locations of account_id in the JWT
|
|
27
|
+
*/
|
|
28
|
+
const JwtPayloadSchema = z.object({
|
|
29
|
+
'https://api.openai.com/auth': z
|
|
30
|
+
.object({
|
|
31
|
+
chatgpt_account_id: z.string().optional(),
|
|
32
|
+
account_id: z.string().optional(),
|
|
33
|
+
})
|
|
34
|
+
.optional(),
|
|
35
|
+
account_id: z.string().optional(),
|
|
36
|
+
});
|
|
37
|
+
/**
|
|
38
|
+
* Codex OAuth PKCE flow implementation
|
|
39
|
+
* Implements OAuth 2.0 Authorization Code flow with PKCE for Codex authentication
|
|
40
|
+
*/
|
|
41
|
+
export class CodexDeviceFlow {
|
|
42
|
+
logger;
|
|
43
|
+
codeVerifiers = new Map();
|
|
44
|
+
static NO_OP_LOGGER = {
|
|
45
|
+
debug: () => { },
|
|
46
|
+
error: () => { },
|
|
47
|
+
warn: () => { },
|
|
48
|
+
log: () => { },
|
|
49
|
+
};
|
|
50
|
+
constructor(options) {
|
|
51
|
+
this.logger = options?.logger ?? CodexDeviceFlow.NO_OP_LOGGER;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Build authorization URL for browser-based OAuth flow
|
|
55
|
+
* @param redirectUri Callback URL for OAuth redirect
|
|
56
|
+
* @param state Random state parameter for CSRF protection
|
|
57
|
+
* @returns Authorization URL to open in browser
|
|
58
|
+
*/
|
|
59
|
+
buildAuthorizationUrl(redirectUri, state) {
|
|
60
|
+
this.logger.debug(() => `[FLOW] buildAuthorizationUrl() called with redirectUri=${redirectUri}, state=${state.substring(0, 8)}...`);
|
|
61
|
+
const { verifier, challenge } = this.generatePKCE();
|
|
62
|
+
this.logger.debug(() => `[FLOW] PKCE generated: verifier length=${verifier.length}, challenge length=${challenge.length}`);
|
|
63
|
+
this.codeVerifiers.set(state, verifier);
|
|
64
|
+
this.logger.debug(() => `[FLOW] Stored PKCE verifier for state, codeVerifiers.size=${this.codeVerifiers.size}`);
|
|
65
|
+
// Manually construct query string to use %20 for spaces (not +)
|
|
66
|
+
// This ensures proper parsing with decodeURIComponent
|
|
67
|
+
// Include all required params per shell-scripts/codex-oauth.sh
|
|
68
|
+
const params = [
|
|
69
|
+
`response_type=code`,
|
|
70
|
+
`client_id=${encodeURIComponent(CODEX_CONFIG.clientId)}`,
|
|
71
|
+
`redirect_uri=${encodeURIComponent(redirectUri)}`,
|
|
72
|
+
`scope=${encodeURIComponent(CODEX_CONFIG.scopes.join(' '))}`,
|
|
73
|
+
`code_challenge=${encodeURIComponent(challenge)}`,
|
|
74
|
+
`code_challenge_method=S256`,
|
|
75
|
+
`id_token_add_organizations=true`,
|
|
76
|
+
`codex_cli_simplified_flow=true`,
|
|
77
|
+
`state=${encodeURIComponent(state)}`,
|
|
78
|
+
`originator=${encodeURIComponent(CODEX_CONFIG.originator)}`,
|
|
79
|
+
`theme=dark`,
|
|
80
|
+
].join('&');
|
|
81
|
+
this.logger.debug('[FLOW] Built authorization URL with PKCE S256');
|
|
82
|
+
return `${CODEX_CONFIG.authorizationEndpoint}?${params}`;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Exchange authorization code for OAuth tokens
|
|
86
|
+
* @param authCode Authorization code from OAuth callback
|
|
87
|
+
* @param redirectUri Callback URL (must match the one used in authorization request)
|
|
88
|
+
* @param state State parameter from OAuth callback
|
|
89
|
+
* @returns Validated CodexOAuthToken with account_id
|
|
90
|
+
* @throws Error if code verifier not found for state or token exchange fails
|
|
91
|
+
*/
|
|
92
|
+
async exchangeCodeForToken(authCode, redirectUri, state) {
|
|
93
|
+
this.logger.debug(() => `[FLOW] exchangeCodeForToken() called with code=${authCode.substring(0, 10)}..., redirectUri=${redirectUri}, state=${state.substring(0, 8)}...`);
|
|
94
|
+
const codeVerifier = this.codeVerifiers.get(state);
|
|
95
|
+
if (!codeVerifier) {
|
|
96
|
+
this.logger.debug(() => `[FLOW] PKCE verifier NOT FOUND for state! Available states: ${Array.from(this.codeVerifiers.keys())
|
|
97
|
+
.map((k) => k.substring(0, 8))
|
|
98
|
+
.join(', ')}`);
|
|
99
|
+
throw new Error(`PKCE code verifier not found for state: ${state}`);
|
|
100
|
+
}
|
|
101
|
+
this.logger.debug(() => `[FLOW] Found PKCE verifier for state, length=${codeVerifier.length}`);
|
|
102
|
+
const tokenResponse = await this.performTokenExchange(authCode, redirectUri, codeVerifier);
|
|
103
|
+
const codexToken = this.buildCodexToken(tokenResponse);
|
|
104
|
+
this.codeVerifiers.delete(state);
|
|
105
|
+
this.logger.debug(() => `[FLOW] Cleaned up PKCE verifier, remaining: ${this.codeVerifiers.size}`);
|
|
106
|
+
return codexToken;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* POST to the token endpoint and return a validated CodexTokenResponse.
|
|
110
|
+
* @throws Error if the HTTP request fails or the response is not OK
|
|
111
|
+
*/
|
|
112
|
+
async performTokenExchange(authCode, redirectUri, codeVerifier) {
|
|
113
|
+
this.logger.debug(() => `[FLOW] Making token exchange request to ${CODEX_CONFIG.tokenEndpoint}`);
|
|
114
|
+
const response = await fetch(CODEX_CONFIG.tokenEndpoint, {
|
|
115
|
+
method: 'POST',
|
|
116
|
+
headers: {
|
|
117
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
118
|
+
Accept: 'application/json',
|
|
119
|
+
},
|
|
120
|
+
body: new URLSearchParams({
|
|
121
|
+
grant_type: 'authorization_code',
|
|
122
|
+
code: authCode,
|
|
123
|
+
redirect_uri: redirectUri,
|
|
124
|
+
client_id: CODEX_CONFIG.clientId,
|
|
125
|
+
code_verifier: codeVerifier,
|
|
126
|
+
}).toString(),
|
|
127
|
+
});
|
|
128
|
+
this.logger.debug(() => `[FLOW] Token exchange response status: ${response.status}`);
|
|
129
|
+
if (!response.ok) {
|
|
130
|
+
const errorText = await response.text();
|
|
131
|
+
this.logger.debug(`[FLOW] Token exchange FAILED: ${errorText}`);
|
|
132
|
+
throw new Error(`Token exchange failed: ${response.status} ${errorText}`);
|
|
133
|
+
}
|
|
134
|
+
const data = await response.json();
|
|
135
|
+
this.logger.debug(() => `[FLOW] Token response received, keys: ${Object.keys(data).join(', ')}`);
|
|
136
|
+
const tokenResponse = CodexTokenResponseSchema.parse(data);
|
|
137
|
+
this.logger.debug(() => `[FLOW] Token response validated: has_id_token=${!!tokenResponse.id_token}, has_refresh_token=${!!tokenResponse.refresh_token}, expires_in=${tokenResponse.expires_in}`);
|
|
138
|
+
return tokenResponse;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Validate token response, extract account_id, and build a CodexOAuthToken.
|
|
142
|
+
* @throws Error if id_token is missing or account_id cannot be extracted
|
|
143
|
+
*/
|
|
144
|
+
buildCodexToken(tokenResponse) {
|
|
145
|
+
// Extract account_id from id_token JWT
|
|
146
|
+
this.logger.debug('[FLOW] Extracting account_id from id_token...');
|
|
147
|
+
const accountId = tokenResponse.id_token
|
|
148
|
+
? this.extractAccountIdFromIdToken(tokenResponse.id_token)
|
|
149
|
+
: this.throwMissingAccountId();
|
|
150
|
+
this.logger.debug(() => `[FLOW] Extracted account_id: ${accountId.substring(0, 8)}...`);
|
|
151
|
+
// Build validated Codex token - use Unix timestamp in SECONDS (not milliseconds)
|
|
152
|
+
const now = Math.floor(Date.now() / 1000);
|
|
153
|
+
// expires_in 0 is invalid, treat nullish/NaN/0 as default 1 hour
|
|
154
|
+
const rawExpiresIn = tokenResponse.expires_in;
|
|
155
|
+
const expiresIn = typeof rawExpiresIn === 'number' &&
|
|
156
|
+
!Number.isNaN(rawExpiresIn) &&
|
|
157
|
+
rawExpiresIn !== 0
|
|
158
|
+
? rawExpiresIn
|
|
159
|
+
: 3600;
|
|
160
|
+
const expiry = now + expiresIn;
|
|
161
|
+
this.logger.debug(() => `[FLOW] Building CodexOAuthToken with expiry=${expiry} (in ${expiresIn}s)`);
|
|
162
|
+
const codexToken = CodexOAuthTokenSchema.parse({
|
|
163
|
+
access_token: tokenResponse.access_token,
|
|
164
|
+
token_type: tokenResponse.token_type,
|
|
165
|
+
expiry,
|
|
166
|
+
refresh_token: tokenResponse.refresh_token,
|
|
167
|
+
account_id: accountId,
|
|
168
|
+
id_token: tokenResponse.id_token,
|
|
169
|
+
});
|
|
170
|
+
this.logger.debug(() => `[FLOW] Token exchange successful! account_id=${accountId.substring(0, 8)}..., token_type=${codexToken.token_type}`);
|
|
171
|
+
return codexToken;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Refresh an expired access token using refresh token
|
|
175
|
+
* @param refreshToken Valid refresh token
|
|
176
|
+
* @returns New CodexOAuthToken with updated expiry
|
|
177
|
+
* @throws Error if refresh fails or id_token missing
|
|
178
|
+
*/
|
|
179
|
+
async refreshToken(refreshToken) {
|
|
180
|
+
this.logger.debug('Refreshing expired token');
|
|
181
|
+
const response = await fetch(CODEX_CONFIG.tokenEndpoint, {
|
|
182
|
+
method: 'POST',
|
|
183
|
+
headers: {
|
|
184
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
185
|
+
Accept: 'application/json',
|
|
186
|
+
},
|
|
187
|
+
body: new URLSearchParams({
|
|
188
|
+
grant_type: 'refresh_token',
|
|
189
|
+
refresh_token: refreshToken,
|
|
190
|
+
client_id: CODEX_CONFIG.clientId,
|
|
191
|
+
}).toString(),
|
|
192
|
+
});
|
|
193
|
+
if (!response.ok) {
|
|
194
|
+
throw new Error(`Token refresh failed: ${response.status}`);
|
|
195
|
+
}
|
|
196
|
+
const data = await response.json();
|
|
197
|
+
const tokenResponse = CodexTokenResponseSchema.parse(data);
|
|
198
|
+
// Extract account_id from new id_token if available
|
|
199
|
+
// For refresh flows, OpenAI may not always return a new id_token,
|
|
200
|
+
// so we allow undefined here (caller should preserve original account_id)
|
|
201
|
+
const accountId = tokenResponse.id_token
|
|
202
|
+
? this.extractAccountIdFromIdToken(tokenResponse.id_token)
|
|
203
|
+
: undefined;
|
|
204
|
+
// Use Unix timestamp in SECONDS
|
|
205
|
+
const now = Math.floor(Date.now() / 1000);
|
|
206
|
+
// expires_in 0 is invalid, treat nullish/NaN/0 as default 1 hour
|
|
207
|
+
const rawExpiresIn = tokenResponse.expires_in;
|
|
208
|
+
const expiresIn = typeof rawExpiresIn === 'number' &&
|
|
209
|
+
!Number.isNaN(rawExpiresIn) &&
|
|
210
|
+
rawExpiresIn !== 0
|
|
211
|
+
? rawExpiresIn
|
|
212
|
+
: 3600;
|
|
213
|
+
const expiry = now + expiresIn;
|
|
214
|
+
return CodexOAuthTokenSchema.parse({
|
|
215
|
+
access_token: tokenResponse.access_token,
|
|
216
|
+
token_type: tokenResponse.token_type,
|
|
217
|
+
expiry,
|
|
218
|
+
refresh_token: tokenResponse.refresh_token ?? refreshToken,
|
|
219
|
+
account_id: accountId,
|
|
220
|
+
id_token: tokenResponse.id_token,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Extract account_id from id_token JWT without external libraries
|
|
225
|
+
* JWT format: header.payload.signature (base64url encoded)
|
|
226
|
+
* @param idToken JWT id_token from OAuth response
|
|
227
|
+
* @returns account_id extracted from JWT claims
|
|
228
|
+
* @throws Error if JWT format invalid or account_id not found
|
|
229
|
+
*/
|
|
230
|
+
extractAccountIdFromIdToken(idToken) {
|
|
231
|
+
const parts = idToken.split('.');
|
|
232
|
+
if (parts.length !== 3) {
|
|
233
|
+
throw new Error('Invalid JWT format: expected 3 parts');
|
|
234
|
+
}
|
|
235
|
+
// Decode payload (middle part) from base64url
|
|
236
|
+
const payload = parts[1];
|
|
237
|
+
const decoded = Buffer.from(payload, 'base64url').toString('utf-8');
|
|
238
|
+
// Parse and validate with Zod
|
|
239
|
+
const parsedPayload = JSON.parse(decoded);
|
|
240
|
+
const validated = JwtPayloadSchema.parse(parsedPayload);
|
|
241
|
+
// Extract account_id from OpenAI-specific claim or root
|
|
242
|
+
/* eslint-disable @typescript-eslint/prefer-nullish-coalescing -- intentional falsy coalescing: empty string account_id from JWT claims should fall through to next option */
|
|
243
|
+
const accountId = validated['https://api.openai.com/auth']?.chatgpt_account_id ||
|
|
244
|
+
validated['https://api.openai.com/auth']?.account_id ||
|
|
245
|
+
validated.account_id;
|
|
246
|
+
/* eslint-enable @typescript-eslint/prefer-nullish-coalescing */
|
|
247
|
+
if (!accountId) {
|
|
248
|
+
throw new Error('No account_id found in id_token JWT claims');
|
|
249
|
+
}
|
|
250
|
+
return accountId;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Helper to throw error when id_token is missing
|
|
254
|
+
* @throws Error indicating id_token required
|
|
255
|
+
*/
|
|
256
|
+
throwMissingAccountId() {
|
|
257
|
+
throw new Error('Cannot extract account_id: id_token missing from token response');
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Request a user code for device authorization flow (browserless authentication)
|
|
261
|
+
* @returns Device code response with user_code and device_auth_id
|
|
262
|
+
*/
|
|
263
|
+
async requestDeviceCode() {
|
|
264
|
+
this.logger.debug(() => `[DEVICE] Requesting user code from ${CODEX_CONFIG.deviceAuthUserCodeEndpoint}`);
|
|
265
|
+
this.logger.debug(() => `[DEVICE] Using client_id: ${CODEX_CONFIG.clientId}`);
|
|
266
|
+
const response = await globalThis.fetch(CODEX_CONFIG.deviceAuthUserCodeEndpoint, {
|
|
267
|
+
method: 'POST',
|
|
268
|
+
headers: {
|
|
269
|
+
'Content-Type': 'application/json',
|
|
270
|
+
},
|
|
271
|
+
body: JSON.stringify({
|
|
272
|
+
client_id: CODEX_CONFIG.clientId,
|
|
273
|
+
}),
|
|
274
|
+
});
|
|
275
|
+
if (!response.ok) {
|
|
276
|
+
if (response.status === 404) {
|
|
277
|
+
throw new Error('Device code authorization is not enabled for this Codex server');
|
|
278
|
+
}
|
|
279
|
+
const errorText = await response.text();
|
|
280
|
+
this.logger.debug(() => `[DEVICE] User code request FAILED: ${errorText}`);
|
|
281
|
+
throw new Error(`User code request failed: ${response.status} ${errorText}`);
|
|
282
|
+
}
|
|
283
|
+
const data = await response.json();
|
|
284
|
+
// Log only non-sensitive keys to avoid exposing auth data
|
|
285
|
+
this.logger.debug(() => `[DEVICE] User code response keys: ${Object.keys(data).join(', ')}`);
|
|
286
|
+
// Parse the response
|
|
287
|
+
const UserCodeResponseSchema = z.object({
|
|
288
|
+
device_auth_id: z.string(),
|
|
289
|
+
user_code: z.string(),
|
|
290
|
+
interval: z
|
|
291
|
+
.union([z.number(), z.string()])
|
|
292
|
+
.transform((val) => typeof val === 'string' ? parseInt(val.trim(), 10) : val)
|
|
293
|
+
.optional()
|
|
294
|
+
.default(5),
|
|
295
|
+
});
|
|
296
|
+
const result = UserCodeResponseSchema.parse(data);
|
|
297
|
+
this.logger.debug(`[DEVICE] Received user code: ${result.user_code}`);
|
|
298
|
+
return result;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Poll for token using device authorization
|
|
302
|
+
* @param deviceAuthId Device authorization ID
|
|
303
|
+
* @param userCode User code from device flow
|
|
304
|
+
* @param intervalSeconds Polling interval in seconds
|
|
305
|
+
* @returns Authorization code and PKCE codes for token exchange
|
|
306
|
+
*/
|
|
307
|
+
async pollForDeviceToken(deviceAuthId, userCode, intervalSeconds = 5) {
|
|
308
|
+
this.logger.debug('[DEVICE] Starting device token polling');
|
|
309
|
+
const maxWaitMs = 15 * 60 * 1000; // 15 minutes
|
|
310
|
+
const startTime = Date.now();
|
|
311
|
+
const intervalMs = intervalSeconds * 1000;
|
|
312
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Device-flow polling intentionally loops until timeout, authorization, or terminal error.
|
|
313
|
+
while (true) {
|
|
314
|
+
if (Date.now() - startTime >= maxWaitMs) {
|
|
315
|
+
throw new Error('Device authorization timed out after 15 minutes');
|
|
316
|
+
}
|
|
317
|
+
this.logger.debug(() => `[DEVICE] Polling ${CODEX_CONFIG.deviceAuthTokenEndpoint} with device_auth_id=${deviceAuthId}, user_code=${userCode}`);
|
|
318
|
+
const response = await globalThis.fetch(CODEX_CONFIG.deviceAuthTokenEndpoint, {
|
|
319
|
+
method: 'POST',
|
|
320
|
+
headers: {
|
|
321
|
+
'Content-Type': 'application/json',
|
|
322
|
+
},
|
|
323
|
+
body: JSON.stringify({
|
|
324
|
+
device_auth_id: deviceAuthId,
|
|
325
|
+
user_code: userCode,
|
|
326
|
+
}),
|
|
327
|
+
});
|
|
328
|
+
const responseText = await response.text();
|
|
329
|
+
// Log status only, not the full response body which may contain sensitive data
|
|
330
|
+
this.logger.debug(() => `[DEVICE] Poll response status: ${response.status}`);
|
|
331
|
+
if (response.ok) {
|
|
332
|
+
let data;
|
|
333
|
+
try {
|
|
334
|
+
data = JSON.parse(responseText);
|
|
335
|
+
}
|
|
336
|
+
catch (parseError) {
|
|
337
|
+
this.logger.debug(() => `[DEVICE] Failed to parse response as JSON: ${parseError}`);
|
|
338
|
+
throw new Error(`Failed to parse device token response: ${responseText}`);
|
|
339
|
+
}
|
|
340
|
+
// Log only non-sensitive keys to avoid exposing auth data
|
|
341
|
+
this.logger.debug(() => `[DEVICE] Token polling successful, keys: ${Object.keys(data).join(', ')}`);
|
|
342
|
+
// Parse the successful response - includes authorization_code for token exchange
|
|
343
|
+
const CodeSuccessResponseSchema = z.object({
|
|
344
|
+
authorization_code: z.string(),
|
|
345
|
+
code_verifier: z.string(),
|
|
346
|
+
code_challenge: z.string(),
|
|
347
|
+
});
|
|
348
|
+
return CodeSuccessResponseSchema.parse(data);
|
|
349
|
+
}
|
|
350
|
+
// 403 or 404 means still waiting for user authorization
|
|
351
|
+
if (response.status === 403 || response.status === 404) {
|
|
352
|
+
this.logger.debug(() => `[DEVICE] User hasn't authorized yet (${response.status}), waiting ${intervalMs}ms...`);
|
|
353
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
// Any other status is an error
|
|
357
|
+
this.logger.debug(`[DEVICE] Token polling FAILED: ${responseText}`);
|
|
358
|
+
throw new Error(`Device authorization failed: ${response.status} ${responseText}`);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Complete device authorization flow by exchanging authorization code for tokens
|
|
363
|
+
* @param authorizationCode Authorization code from polling response
|
|
364
|
+
* @param codeVerifier PKCE code verifier from polling response
|
|
365
|
+
* @param redirectUri OAuth redirect URI
|
|
366
|
+
* @returns Complete OAuth token with access_token, refresh_token, etc.
|
|
367
|
+
*/
|
|
368
|
+
async completeDeviceAuth(authorizationCode, codeVerifier, redirectUri) {
|
|
369
|
+
this.logger.debug(() => '[DEVICE] Completing device authorization with code exchange');
|
|
370
|
+
this.logger.debug(() => `[DEVICE] authCode=${authorizationCode.substring(0, 15)}..., redirectUri=${redirectUri}`);
|
|
371
|
+
// For device flow, we have the code_verifier directly from OpenAI's response
|
|
372
|
+
// instead of looking it up from our PKCE state map
|
|
373
|
+
this.logger.debug(() => `[DEVICE] Making token exchange request to ${CODEX_CONFIG.tokenEndpoint}`);
|
|
374
|
+
const response = await fetch(CODEX_CONFIG.tokenEndpoint, {
|
|
375
|
+
method: 'POST',
|
|
376
|
+
headers: {
|
|
377
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
378
|
+
Accept: 'application/json',
|
|
379
|
+
},
|
|
380
|
+
body: new URLSearchParams({
|
|
381
|
+
grant_type: 'authorization_code',
|
|
382
|
+
code: authorizationCode,
|
|
383
|
+
redirect_uri: redirectUri,
|
|
384
|
+
client_id: CODEX_CONFIG.clientId,
|
|
385
|
+
code_verifier: codeVerifier,
|
|
386
|
+
}).toString(),
|
|
387
|
+
});
|
|
388
|
+
this.logger.debug(() => `[DEVICE] Token exchange response status: ${response.status}`);
|
|
389
|
+
if (!response.ok) {
|
|
390
|
+
const errorText = await response.text();
|
|
391
|
+
this.logger.debug(`[DEVICE] Token exchange FAILED: ${errorText}`);
|
|
392
|
+
throw new Error(`Token exchange failed: ${response.status} ${errorText}`);
|
|
393
|
+
}
|
|
394
|
+
const data = await response.json();
|
|
395
|
+
this.logger.debug(() => `[DEVICE] Token response received, keys: ${Object.keys(data).join(', ')}`);
|
|
396
|
+
// Validate with Zod schema
|
|
397
|
+
const tokenResponse = CodexTokenResponseSchema.parse(data);
|
|
398
|
+
this.logger.debug(() => `[DEVICE] Token response validated: has_id_token=${!!tokenResponse.id_token}, has_refresh_token=${!!tokenResponse.refresh_token}, expires_in=${tokenResponse.expires_in}`);
|
|
399
|
+
// Extract account_id from id_token JWT
|
|
400
|
+
this.logger.debug('[DEVICE] Extracting account_id from id_token...');
|
|
401
|
+
const accountId = tokenResponse.id_token
|
|
402
|
+
? this.extractAccountIdFromIdToken(tokenResponse.id_token)
|
|
403
|
+
: this.throwMissingAccountId();
|
|
404
|
+
this.logger.debug(() => `[DEVICE] Extracted account_id: ${accountId.substring(0, 8)}...`);
|
|
405
|
+
// Build and return the CodexOAuthToken
|
|
406
|
+
const now = Date.now();
|
|
407
|
+
// expires_in 0 is invalid, treat nullish/NaN/0 as default 1 hour
|
|
408
|
+
const rawExpiresIn = tokenResponse.expires_in;
|
|
409
|
+
const expiresIn = typeof rawExpiresIn === 'number' &&
|
|
410
|
+
!Number.isNaN(rawExpiresIn) &&
|
|
411
|
+
rawExpiresIn !== 0
|
|
412
|
+
? rawExpiresIn
|
|
413
|
+
: 3600;
|
|
414
|
+
const token = CodexOAuthTokenSchema.parse({
|
|
415
|
+
access_token: tokenResponse.access_token,
|
|
416
|
+
refresh_token: tokenResponse.refresh_token,
|
|
417
|
+
id_token: tokenResponse.id_token,
|
|
418
|
+
token_type: tokenResponse.token_type,
|
|
419
|
+
expiry: Math.floor(now / 1000) + expiresIn,
|
|
420
|
+
account_id: accountId,
|
|
421
|
+
});
|
|
422
|
+
this.logger.debug(() => '[DEVICE] Device authorization completed successfully');
|
|
423
|
+
return token;
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Generate PKCE code verifier and challenge
|
|
427
|
+
* @returns Object containing verifier and challenge strings
|
|
428
|
+
*/
|
|
429
|
+
generatePKCE() {
|
|
430
|
+
// Generate 64 random bytes for verifier (matches shell script and Rust CLI)
|
|
431
|
+
const verifier = randomBytes(64).toString('base64url');
|
|
432
|
+
// Create SHA-256 hash of verifier for challenge (S256 method)
|
|
433
|
+
const challenge = createHash('sha256').update(verifier).digest('base64url');
|
|
434
|
+
return { verifier, challenge };
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
//# sourceMappingURL=codex-device-flow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex-device-flow.js","sourceRoot":"","sources":["../../../src/flows/codex-device-flow.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Vybestack LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { type DeviceCodeResponse, type OAuthToken } from '../types.js';
|
|
7
|
+
/**
|
|
8
|
+
* Configuration for Qwen device flow authentication
|
|
9
|
+
*/
|
|
10
|
+
export interface DeviceFlowConfig {
|
|
11
|
+
clientId: string;
|
|
12
|
+
authorizationEndpoint: string;
|
|
13
|
+
tokenEndpoint: string;
|
|
14
|
+
scopes: string[];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Qwen OAuth device flow implementation
|
|
18
|
+
*/
|
|
19
|
+
export declare class QwenDeviceFlow {
|
|
20
|
+
private config;
|
|
21
|
+
private pkceVerifier;
|
|
22
|
+
constructor(config: DeviceFlowConfig);
|
|
23
|
+
/**
|
|
24
|
+
* Initiates the device authorization flow
|
|
25
|
+
* @returns Promise resolving to device code response
|
|
26
|
+
*/
|
|
27
|
+
initiateDeviceFlow(): Promise<DeviceCodeResponse>;
|
|
28
|
+
/**
|
|
29
|
+
* Polls the authorization server for an access token
|
|
30
|
+
* @param deviceCode Device code from initiation response
|
|
31
|
+
* @returns Promise resolving to OAuth token
|
|
32
|
+
*/
|
|
33
|
+
pollForToken(deviceCode: string): Promise<OAuthToken>;
|
|
34
|
+
/**
|
|
35
|
+
* Refreshes an expired access token
|
|
36
|
+
* @param refreshToken Valid refresh token
|
|
37
|
+
* @returns Promise resolving to new OAuth token
|
|
38
|
+
*/
|
|
39
|
+
refreshToken(refreshToken: string): Promise<OAuthToken>;
|
|
40
|
+
/**
|
|
41
|
+
* Generates PKCE code verifier and challenge
|
|
42
|
+
* @returns Object containing verifier and challenge strings
|
|
43
|
+
*/
|
|
44
|
+
private generatePKCE;
|
|
45
|
+
}
|