mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-19 09:33:53 +00:00
fix: unexpected re-auth when auth-token is expired (#549)
This commit is contained in:
@@ -35,9 +35,6 @@ const QWEN_OAUTH_GRANT_TYPE = 'urn:ietf:params:oauth:grant-type:device_code';
|
||||
const QWEN_DIR = '.qwen';
|
||||
const QWEN_CREDENTIAL_FILENAME = 'oauth_creds.json';
|
||||
|
||||
// Token Configuration
|
||||
const TOKEN_REFRESH_BUFFER_MS = 30 * 1000; // 30 seconds
|
||||
|
||||
/**
|
||||
* PKCE (Proof Key for Code Exchange) utilities
|
||||
* Implements RFC 7636 - Proof Key for Code Exchange by OAuth Public Clients
|
||||
@@ -94,6 +91,21 @@ export interface ErrorData {
|
||||
error_description: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom error class to indicate that credentials should be cleared
|
||||
* This is thrown when a 400 error occurs during token refresh, indicating
|
||||
* that the refresh token is expired or invalid
|
||||
*/
|
||||
export class CredentialsClearRequiredError extends Error {
|
||||
constructor(
|
||||
message: string,
|
||||
public originalError?: unknown,
|
||||
) {
|
||||
super(message);
|
||||
this.name = 'CredentialsClearRequiredError';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Qwen OAuth2 credentials interface
|
||||
*/
|
||||
@@ -255,20 +267,16 @@ export class QwenOAuth2Client implements IQwenOAuth2Client {
|
||||
|
||||
async getAccessToken(): Promise<{ token?: string }> {
|
||||
try {
|
||||
// Use shared manager to get valid credentials with cross-session synchronization
|
||||
// Always use shared manager for consistency - this prevents race conditions
|
||||
// between local credential state and shared state
|
||||
const credentials = await this.sharedManager.getValidCredentials(this);
|
||||
return { token: credentials.access_token };
|
||||
} catch (error) {
|
||||
console.warn('Failed to get access token from shared manager:', error);
|
||||
|
||||
// Only return cached token if it's still valid, don't refresh uncoordinated
|
||||
// This prevents the cross-session token invalidation issue
|
||||
if (this.credentials.access_token && this.isTokenValid()) {
|
||||
return { token: this.credentials.access_token };
|
||||
}
|
||||
|
||||
// If we can't get valid credentials through shared manager, fail gracefully
|
||||
// All token refresh operations should go through the SharedTokenManager
|
||||
// Don't use fallback to local credentials to prevent race conditions
|
||||
// All token management should go through SharedTokenManager for consistency
|
||||
// This ensures single source of truth and prevents cross-session issues
|
||||
return { token: undefined };
|
||||
}
|
||||
}
|
||||
@@ -402,11 +410,12 @@ export class QwenOAuth2Client implements IQwenOAuth2Client {
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.text();
|
||||
// Handle 401 errors which might indicate refresh token expiry
|
||||
// Handle 400 errors which might indicate refresh token expiry
|
||||
if (response.status === 400) {
|
||||
await clearQwenCredentials();
|
||||
throw new Error(
|
||||
throw new CredentialsClearRequiredError(
|
||||
"Refresh token expired or invalid. Please use '/auth' to re-authenticate.",
|
||||
{ status: response.status, response: errorData },
|
||||
);
|
||||
}
|
||||
throw new Error(
|
||||
@@ -442,14 +451,6 @@ export class QwenOAuth2Client implements IQwenOAuth2Client {
|
||||
|
||||
return responseData;
|
||||
}
|
||||
|
||||
private isTokenValid(): boolean {
|
||||
if (!this.credentials.expiry_date) {
|
||||
return false;
|
||||
}
|
||||
// Check if token expires within the refresh buffer time
|
||||
return Date.now() < this.credentials.expiry_date - TOKEN_REFRESH_BUFFER_MS;
|
||||
}
|
||||
}
|
||||
|
||||
export enum QwenOAuth2Event {
|
||||
|
||||
Reference in New Issue
Block a user