feat: 增强 Token 过期验证机制
- 添加 tokenExpiresAt 状态变量追踪实际过期时间 - saveTokens() 保存 expires_at 字段到文件 - loadAuthConfig() 启动时验证 token 是否过期 - shouldRefresh() 优先使用实际过期时间判断 - 提前 30 分钟刷新避免临界问题 - 修复 refreshApiKey() 中的代码缩进问题 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
72
auth.js
72
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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user