diff --git a/auth.js b/auth.js index 16d0da8..475267c 100644 --- a/auth.js +++ b/auth.js @@ -10,6 +10,7 @@ import { getRefreshConfig, requestRefreshToken } from './refresh-client.js'; let currentApiKey = null; let currentRefreshToken = null; let lastRefreshTime = null; +let tokenExpiresAt = null; // Token 过期时间戳 (ms) let clientId = null; let authSource = null; // 'env' or 'file' or 'factory_key' or 'client' or 'multi_account' let authFilePath = null; @@ -21,6 +22,7 @@ let refreshInFlight = null; // 刷新锁,避免并发刷新 const REFRESH_URL = 'https://api.workos.com/user_management/authenticate'; const REFRESH_INTERVAL_HOURS = 6; // Refresh every 6 hours const TOKEN_VALID_HOURS = 8; // Token valid for 8 hours +const REFRESH_BUFFER_MS = 30 * 60 * 1000; // 提前 30 分钟刷新 /** * Generate a ULID (Universally Unique Lexicographically Sortable Identifier) @@ -123,9 +125,25 @@ function loadAuthConfig() { authSource = 'file'; authFilePath = factoryAuthPath; - // Also load access_token if available + // Also load access_token if available and not expired if (authData.access_token) { - currentApiKey = authData.access_token.trim(); + const expiresAt = authData.expires_at ? new Date(authData.expires_at).getTime() : null; + const now = Date.now(); + + if (expiresAt && expiresAt > now + REFRESH_BUFFER_MS) { + // Token 还有效(且距离过期超过30分钟) + currentApiKey = authData.access_token.trim(); + tokenExpiresAt = expiresAt; + lastRefreshTime = authData.last_updated ? new Date(authData.last_updated).getTime() : now; + logInfo(`Loaded valid token from file, expires at: ${new Date(expiresAt).toISOString()}`); + } else if (expiresAt) { + // Token 已过期或即将过期 + logInfo(`Stored token expired or expiring soon (expires_at: ${authData.expires_at}), will refresh`); + } else { + // 没有过期时间记录,按旧逻辑处理 + currentApiKey = authData.access_token.trim(); + logInfo('Loaded token from file (no expiry info, will check on first use)'); + } } return { type: 'refresh', value: authData.refresh_token.trim() }; @@ -162,20 +180,22 @@ async function refreshApiKey() { logInfo('Refreshing API key...'); try { - const proxyAgentInfo = getNextProxyAgent(REFRESH_URL); - const refreshConfig = getRefreshConfig(); - const data = await requestRefreshToken({ - refreshUrl: REFRESH_URL, - refreshToken: currentRefreshToken, - clientId, - proxyAgentInfo, - ...refreshConfig - }); + const proxyAgentInfo = getNextProxyAgent(REFRESH_URL); + const refreshConfig = getRefreshConfig(); + const data = await requestRefreshToken({ + refreshUrl: REFRESH_URL, + refreshToken: currentRefreshToken, + clientId, + proxyAgentInfo, + ...refreshConfig + }); // Update tokens currentApiKey = data.access_token; currentRefreshToken = data.refresh_token; lastRefreshTime = Date.now(); + // 设置过期时间(默认8小时) + tokenExpiresAt = lastRefreshTime + TOKEN_VALID_HOURS * 60 * 60 * 1000; // Log user info if (data.user) { @@ -188,6 +208,7 @@ async function refreshApiKey() { saveTokens(data.access_token, data.refresh_token); logInfo(`New Refresh-Key: ${currentRefreshToken}`); + logInfo(`Token expires at: ${new Date(tokenExpiresAt).toISOString()}`); logInfo('API key refreshed successfully'); return data.access_token; @@ -206,13 +227,20 @@ async function refreshApiKey() { /** * Save tokens to appropriate file + * @param {string} accessToken - Access token to save + * @param {string} refreshToken - Refresh token to save + * @param {number} expiresInMs - Token validity duration in milliseconds (default: TOKEN_VALID_HOURS) */ -function saveTokens(accessToken, refreshToken) { +function saveTokens(accessToken, refreshToken, expiresInMs = TOKEN_VALID_HOURS * 60 * 60 * 1000) { try { + const now = Date.now(); + const expiresAt = new Date(now + expiresInMs).toISOString(); + const authData = { access_token: accessToken, refresh_token: refreshToken, - last_updated: new Date().toISOString() + expires_at: expiresAt, + last_updated: new Date(now).toISOString() }; // Ensure directory exists @@ -228,6 +256,7 @@ function saveTokens(accessToken, refreshToken) { Object.assign(authData, existingData, { access_token: accessToken, refresh_token: refreshToken, + expires_at: expiresAt, last_updated: authData.last_updated }); } catch (error) { @@ -244,14 +273,27 @@ function saveTokens(accessToken, refreshToken) { } /** - * Check if API key needs refresh (older than 6 hours) + * Check if API key needs refresh + * Uses actual expiration time if available, falls back to time-based check */ function shouldRefresh() { + const now = Date.now(); + + // 如果有过期时间,使用过期时间判断(提前30分钟刷新) + if (tokenExpiresAt) { + const shouldRefreshByExpiry = now + REFRESH_BUFFER_MS >= tokenExpiresAt; + if (shouldRefreshByExpiry) { + logDebug(`Token expiring soon (expires_at: ${new Date(tokenExpiresAt).toISOString()})`); + } + return shouldRefreshByExpiry; + } + + // 回退到基于刷新时间的判断 if (!lastRefreshTime) { return true; } - const hoursSinceRefresh = (Date.now() - lastRefreshTime) / (1000 * 60 * 60); + const hoursSinceRefresh = (now - lastRefreshTime) / (1000 * 60 * 60); return hoursSinceRefresh >= REFRESH_INTERVAL_HOURS; }