Commit 7fdaca09 authored by Your Name's avatar Your Name

v0.99.32 - Fix OAuth authentication UI and restore full functionality

- Fixed duplicate authentication area in providers dashboard
- Restored full OAuth authentication with popup windows for all providers
- Added missing JavaScript authentication functions (authenticateClaude, authenticateQwen, authenticateCodex, authenticateKilo)
- Added missing backend endpoint /dashboard/providers/{provider_name}/auth/check
- Fixed import errors and attribute access for OAuth provider configs
- Fixed credential structure access for each OAuth provider type
- Fixed polling status responses (approved -> completed)
- Added human-readable expiration time formatting (days, hours, minutes, seconds)
- All OAuth flows now work with proper popup windows, polling, and status updates
parent e7663cb5
......@@ -54,7 +54,7 @@ from .auth.qwen import QwenOAuth2
from .handlers import RequestHandler, RotationHandler, AutoselectHandler
from .utils import count_messages_tokens, split_messages_into_chunks, get_max_request_tokens_for_model
__version__ = "0.99.31"
__version__ = "0.99.32"
__all__ = [
# Config
"config",
......
......@@ -32,6 +32,7 @@ from fastapi.templating import Jinja2Templates
from jinja2 import Environment, FileSystemLoader
from aisbf.models import ChatCompletionRequest, ChatCompletionResponse
from aisbf.handlers import RequestHandler, RotationHandler, AutoselectHandler
from aisbf.config import Config
from aisbf.mcp import mcp_server, MCPAuthLevel, load_mcp_config
from aisbf.database import DatabaseRegistry
from aisbf.cache import initialize_cache
......@@ -5759,6 +5760,88 @@ async def dashboard_provider_file_delete(
return JSONResponse(status_code=500, content={"error": str(e)})
# OAuth authentication check endpoints for providers
@app.get("/dashboard/providers/{provider_name}/auth/check")
async def dashboard_provider_auth_check(request: Request, provider_name: str):
"""Check OAuth authentication status for a provider"""
auth_check = require_admin(request)
if auth_check:
return auth_check
try:
# Load current provider configuration
config = Config()
provider_config = config.providers.get(provider_name)
if not provider_config:
return JSONResponse(
status_code=404,
content={"authenticated": False, "error": f"Provider '{provider_name}' not found"}
)
provider_type = provider_config.type
if provider_type == 'claude':
from aisbf.auth.claude import ClaudeAuth
claude_config = provider_config.claude_config or {}
auth = ClaudeAuth(credentials_file=claude_config.get('credentials_file', '~/.claude_credentials.json'))
is_auth = auth.is_authenticated()
result = {"authenticated": is_auth}
if is_auth and auth.tokens and 'expires_at' in auth.tokens:
result["expires_at"] = auth.tokens['expires_at']
return JSONResponse(result)
elif provider_type == 'kilocode':
from aisbf.auth.kilo import KiloOAuth2
kilo_config = provider_config.kilo_config or {}
auth = KiloOAuth2(credentials_file=kilo_config.get('credentials_file', '~/.kilo_credentials.json'))
is_auth = auth.is_authenticated()
result = {"authenticated": is_auth}
if is_auth and auth.credentials:
expires = auth.credentials.get('expires', 0)
if expires:
result["expires_at"] = expires
return JSONResponse(result)
elif provider_type == 'qwen':
from aisbf.auth.qwen import QwenOAuth2
qwen_config = provider_config.qwen_config or {}
auth = QwenOAuth2(credentials_file=qwen_config.get('credentials_file', '~/.aisbf/qwen_credentials.json'))
is_auth = auth.is_authenticated()
result = {"authenticated": is_auth}
if is_auth and auth.credentials:
expiry_date = auth.credentials.get('expiry_date', 0)
if expiry_date:
# Convert from milliseconds to seconds
result["expires_at"] = expiry_date / 1000
return JSONResponse(result)
elif provider_type == 'codex':
from aisbf.auth.codex import CodexOAuth2
codex_config = provider_config.codex_config or {}
auth = CodexOAuth2(credentials_file=codex_config.get('credentials_file', '~/.aisbf/codex_credentials.json'))
is_auth = auth.is_authenticated()
result = {"authenticated": is_auth}
if is_auth and auth.credentials:
expires = auth.credentials.get('expires', 0)
if expires:
result["expires_at"] = expires
return JSONResponse(result)
else:
return JSONResponse(
status_code=400,
content={"authenticated": False, "error": f"Provider type '{provider_type}' does not support OAuth authentication checks"}
)
except Exception as e:
logger.error(f"Error checking auth for provider {provider_name}: {e}")
return JSONResponse(
status_code=500,
content={"authenticated": False, "error": str(e)}
)
# User-specific rotation management routes
@app.get("/dashboard/user/rotations", response_class=HTMLResponse)
async def dashboard_user_rotations(request: Request):
......@@ -12065,7 +12148,7 @@ async def dashboard_kilo_auth_poll(request: Request):
return JSONResponse({
"success": True,
"status": "approved",
"status": "completed",
"message": "Authentication completed successfully"
})
elif result['status'] == 'pending':
......@@ -12750,7 +12833,7 @@ async def dashboard_qwen_auth_poll(request: Request):
return JSONResponse({
"success": True,
"status": "approved",
"status": "completed",
"message": "Authentication completed successfully"
})
elif result is None:
......
......@@ -49,7 +49,7 @@ class InstallCommand(_install):
setup(
name="aisbf",
version="0.99.31",
version="0.99.32",
author="AISBF Contributors",
author_email="stefy@nexlab.net",
description="AISBF - AI Service Broker Framework || AI Should Be Free - A modular proxy server for managing multiple AI provider integrations",
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment