From f667da64b3a241c04452c4f0e0ddcc3e08d138ec Mon Sep 17 00:00:00 2001 From: yeongpin Date: Sat, 12 Apr 2025 17:28:11 +0800 Subject: [PATCH] Add token refresh functionality and improve token extraction - Introduced a new `get_user_token.py` file to handle token refresh logic using the Chinese server API. - Updated `config.py` to include new token settings for refresh server URL and enable refresh option. - Refactored `oauth_auth.py` to utilize the new token extraction method, enhancing error handling and user feedback. - Added localization strings for token refresh messages in both English and Chinese to improve user experience. --- config.py | 4 ++ get_user_token.py | 112 +++++++++++++++++++++++++++++++++++++++++++++ locales/en.json | 12 +++++ locales/zh_cn.json | 12 +++++ oauth_auth.py | 25 ++-------- 5 files changed, 145 insertions(+), 20 deletions(-) create mode 100644 get_user_token.py diff --git a/config.py b/config.py index c020d3f..400353d 100644 --- a/config.py +++ b/config.py @@ -102,6 +102,10 @@ def setup_config(translator=None): 'show_selection_alert': False, # 默认不显示选择提示弹窗 'timeout': 120, 'max_attempts': 3 + }, + 'Token': { + 'refresh_server': 'https://token.cursorpro.com.cn', + 'enable_refresh': True } } diff --git a/get_user_token.py b/get_user_token.py new file mode 100644 index 0000000..6ccbffb --- /dev/null +++ b/get_user_token.py @@ -0,0 +1,112 @@ +import requests +import json +import time +from colorama import Fore, Style +import os +from config import get_config + +# Define emoji constants +EMOJI = { + 'START': '🚀', + 'OAUTH': '🔑', + 'SUCCESS': '✅', + 'ERROR': '❌', + 'WAIT': '⏳', + 'INFO': 'ℹ️', + 'WARNING': '⚠️' +} + +def refresh_token(token, translator=None): + """Refresh the token using the Chinese server API + + Args: + token (str): The full WorkosCursorSessionToken cookie value + translator: Optional translator object + + Returns: + str: The refreshed access token or original token if refresh fails + """ + try: + config = get_config(translator) + # Get refresh_server URL from config or use default + refresh_server = config.get('Token', 'refresh_server', fallback='https://token.cursorpro.com.cn') + + # Ensure the token is URL encoded properly + if '%3A%3A' not in token and '::' in token: + # Replace :: with URL encoded version if needed + token = token.replace('::', '%3A%3A') + + # Make the request to the refresh server + url = f"{refresh_server}/reftoken?token={token}" + + print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('token.refreshing') if translator else 'Refreshing token...'}{Style.RESET_ALL}") + + response = requests.get(url, timeout=30) + + if response.status_code == 200: + try: + data = response.json() + + if data.get('code') == 0 and data.get('msg') == "获取成功": + access_token = data.get('data', {}).get('accessToken') + days_left = data.get('data', {}).get('days_left', 0) + expire_time = data.get('data', {}).get('expire_time', 'Unknown') + + if access_token: + print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('token.refresh_success', days=days_left, expire=expire_time) if translator else f'Token refreshed successfully! Valid for {days_left} days (expires: {expire_time})'}{Style.RESET_ALL}") + return access_token + else: + print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('token.no_access_token') if translator else 'No access token in response'}{Style.RESET_ALL}") + else: + error_msg = data.get('msg', 'Unknown error') + print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('token.refresh_failed', error=error_msg) if translator else f'Token refresh failed: {error_msg}'}{Style.RESET_ALL}") + except json.JSONDecodeError: + print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('token.invalid_response') if translator else 'Invalid JSON response from refresh server'}{Style.RESET_ALL}") + else: + print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('token.server_error', status=response.status_code) if translator else f'Refresh server error: HTTP {response.status_code}'}{Style.RESET_ALL}") + + except requests.exceptions.Timeout: + print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('token.request_timeout') if translator else 'Request to refresh server timed out'}{Style.RESET_ALL}") + except requests.exceptions.ConnectionError: + print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('token.connection_error') if translator else 'Connection error to refresh server'}{Style.RESET_ALL}") + except Exception as e: + print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('token.unexpected_error', error=str(e)) if translator else f'Unexpected error during token refresh: {str(e)}'}{Style.RESET_ALL}") + + # Return original token if refresh fails + return token.split('%3A%3A')[-1] if '%3A%3A' in token else token.split('::')[-1] if '::' in token else token + +def get_token_from_cookie(cookie_value, translator=None): + """Extract and process token from cookie value + + Args: + cookie_value (str): The WorkosCursorSessionToken cookie value + translator: Optional translator object + + Returns: + str: The processed token + """ + try: + # Try to refresh the token with the API first + refreshed_token = refresh_token(cookie_value, translator) + + # If refresh succeeded and returned a different token, use it + if refreshed_token and refreshed_token != cookie_value: + return refreshed_token + + # If refresh failed or returned same token, use traditional extraction method + if '%3A%3A' in cookie_value: + return cookie_value.split('%3A%3A')[-1] + elif '::' in cookie_value: + return cookie_value.split('::')[-1] + else: + return cookie_value + + except Exception as e: + print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('token.extraction_error', error=str(e)) if translator else f'Error extracting token: {str(e)}'}{Style.RESET_ALL}") + # Fall back to original behavior + if '%3A%3A' in cookie_value: + return cookie_value.split('%3A%3A')[-1] + elif '::' in cookie_value: + return cookie_value.split('::')[-1] + else: + return cookie_value \ No newline at end of file diff --git a/locales/en.json b/locales/en.json index dee8cbe..4e40521 100644 --- a/locales/en.json +++ b/locales/en.json @@ -752,5 +752,17 @@ "title": "Bypass Token Limit Tool", "description": "This tool modifies the workbench.desktop.main.js file to bypass the token limit", "press_enter": "Press Enter to continue..." + }, + "token": { + "refreshing": "Refreshing token...", + "refresh_success": "Token refreshed successfully! Valid for {days} days (expires: {expire})", + "no_access_token": "No access token in response", + "refresh_failed": "Token refresh failed: {error}", + "invalid_response": "Invalid JSON response from refresh server", + "server_error": "Refresh server error: HTTP {status}", + "request_timeout": "Request to refresh server timed out", + "connection_error": "Connection error to refresh server", + "unexpected_error": "Unexpected error during token refresh: {error}", + "extraction_error": "Error extracting token: {error}" } } \ No newline at end of file diff --git a/locales/zh_cn.json b/locales/zh_cn.json index 2aac9f2..7c934e1 100644 --- a/locales/zh_cn.json +++ b/locales/zh_cn.json @@ -730,5 +730,17 @@ "title": "绕过 Token 限制工具", "description": "此工具修改 workbench.desktop.main.js 文件以绕过 token 限制", "press_enter": "按回车键继续..." + }, + "token": { + "refreshing": "正在刷新令牌...", + "refresh_success": "令牌刷新成功!有效期 {days} 天(到期时间: {expire})", + "no_access_token": "响应中没有访问令牌", + "refresh_failed": "令牌刷新失败: {error}", + "invalid_response": "刷新服务器返回无效的 JSON 响应", + "server_error": "刷新服务器错误: HTTP {status}", + "request_timeout": "刷新服务器请求超时", + "connection_error": "连接刷新服务器错误", + "unexpected_error": "令牌刷新过程中出现意外错误: {error}", + "extraction_error": "提取令牌时出错: {error}" } } \ No newline at end of file diff --git a/oauth_auth.py b/oauth_auth.py index 1eace60..f70e243 100644 --- a/oauth_auth.py +++ b/oauth_auth.py @@ -10,6 +10,7 @@ from cursor_auth import CursorAuth from utils import get_random_wait_time, get_default_browser_path from config import get_config import platform +from get_user_token import get_token_from_cookie # Initialize colorama init() @@ -587,13 +588,7 @@ class OAuthHandler: for cookie in cookies: if cookie.get("name") == "WorkosCursorSessionToken": value = cookie.get("value", "") - token = None - - if "::" in value: - token = value.split("::")[-1] - elif "%3A%3A" in value: - token = value.split("%3A%3A")[-1] - + token = get_token_from_cookie(value, self.translator) if token: # Get email from settings page print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.authentication_successful_getting_account_info') if self.translator else 'Authentication successful, getting account info...'}{Style.RESET_ALL}") @@ -798,11 +793,7 @@ class OAuthHandler: for cookie in cookies: if cookie.get("name") == "WorkosCursorSessionToken": value = cookie.get("value", "") - if "::" in value: - token = value.split("::")[-1] - elif "%3A%3A" in value: - token = value.split("%3A%3A")[-1] - + token = get_token_from_cookie(value, self.translator) if token: print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.authentication_successful') if self.translator else 'Authentication successful!'}{Style.RESET_ALL}") # Navigate to settings page @@ -865,10 +856,7 @@ class OAuthHandler: for cookie in cookies: if cookie.get("name") == "WorkosCursorSessionToken": value = cookie.get("value", "") - if "::" in value: - token = value.split("::")[-1] - elif "%3A%3A" in value: - token = value.split("%3A%3A")[-1] + token = get_token_from_cookie(value, self.translator) if token: # Get email and check usage here too try: @@ -965,10 +953,7 @@ class OAuthHandler: if name == "WorkosCursorSessionToken": try: value = cookie.get("value", "") - if "::" in value: - token = value.split("::")[-1] - elif "%3A%3A" in value: - token = value.split("%3A%3A")[-1] + token = get_token_from_cookie(value, self.translator) except Exception as e: print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.token_extraction_error', error=str(e)) if self.translator else f'Token extraction error: {str(e)}'}{Style.RESET_ALL}") elif name == "cursor_email":