Commit 4bba04b4 authored by Your Name's avatar Your Name

feat: Add Codex provider with OAuth2 Device Authorization Grant support (v0.9.8)

- New codex provider type using OpenAI-compatible protocol
- OAuth2 authentication via Device Authorization Grant flow
- Provider handler in aisbf/providers/codex.py
- OAuth2 handler in aisbf/auth/codex.py
- Dashboard integration with authentication UI
- Token refresh with automatic retry
- API key exchange from ID token
- Updated version to 0.9.8 in setup.py and pyproject.toml
- Updated CHANGELOG.md, README.md, PYPI.md with Codex documentation
- Added codex provider configuration to config/providers.json
parent 1491d963
......@@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [0.9.8] - 2026-04-04
### Added
- **Codex Provider (OAuth2)**: Full support for OpenAI Codex using OAuth2 Device Authorization Grant
- New `codex` provider type with OpenAI-compatible API protocol
- OAuth2 authentication via `aisbf/auth/codex.py` with device code flow
- Provider handler in `aisbf/providers/codex.py` extending OpenAI protocol
- Dashboard integration with authentication UI (device code flow)
- Token refresh with automatic retry
- API key exchange from ID token for direct API access
- Credentials stored in `~/.aisbf/codex_credentials.json`
- Uses OpenAI's OAuth2 endpoints (`https://auth.openai.com`)
- No localhost callback port needed (device code flow like Kilo)
- Dashboard endpoints: `/dashboard/codex/auth/start`, `/dashboard/codex/auth/poll`, `/dashboard/codex/auth/status`, `/dashboard/codex/auth/logout`
### Changed
- **Version Bump**: Updated version to 0.9.8 in setup.py and pyproject.toml
## [0.9.7] - 2026-04-03
### Fixed
......
......@@ -156,6 +156,11 @@ AISBF supports OAuth2 authentication for several providers:
- Device Authorization Grant OAuth2 flow
- Seamless integration with Kilocode services
### Codex (OpenAI)
- Device Authorization Grant OAuth2 flow (same protocol as OpenAI)
- Automatic token refresh and API key exchange
- Dashboard integration for easy authentication
**Setup Instructions:**
1. Start AISBF: `aisbf`
2. Access dashboard: `http://localhost:17765/dashboard`
......
......@@ -24,9 +24,10 @@ Access the dashboard at `http://localhost:17765/dashboard` (default credentials:
## Key Features
- **Multi-Provider Support**: Unified interface for Google, OpenAI, Anthropic, Ollama, Kiro (Amazon Q Developer), Kiro-cli, Claude Code (OAuth2), and Kilocode (OAuth2)
- **Multi-Provider Support**: Unified interface for Google, OpenAI, Anthropic, Ollama, Kiro (Amazon Q Developer), Kiro-cli, Claude Code (OAuth2), Kilocode (OAuth2), and Codex (OAuth2)
- **Claude OAuth2 Authentication**: Full OAuth2 PKCE flow for Claude Code with automatic token refresh and Chrome extension for remote servers
- **Kilocode OAuth2 Authentication**: OAuth2 Device Authorization Grant for Kilo Code with automatic token refresh
- **Codex OAuth2 Authentication**: OAuth2 Device Authorization Grant for OpenAI Codex with automatic token refresh and API key exchange
- **Rotation Models**: Weighted load balancing across multiple providers with automatic failover
- **Autoselect Models**: AI-powered model selection based on content analysis and request characteristics
- **Semantic Classification**: Fast hybrid BM25 + semantic model selection using sentence transformers (optional)
......@@ -134,6 +135,7 @@ See [`PYPI.md`](PYPI.md) for detailed instructions on publishing to PyPI.
- Kiro (Amazon Q Developer / AWS CodeWhisperer)
- Kiro-cli (Amazon Q Developer CLI authentication)
- Kilocode (OAuth2 Device Authorization Grant)
- Codex (OAuth2 Device Authorization Grant - OpenAI protocol)
### Kiro-cli Provider Support
......@@ -218,6 +220,50 @@ AISBF supports Kilo Code as a provider using OAuth2 Device Authorization Grant:
```
See [`KILO_OAUTH2.md`](KILO_OAUTH2.md) for detailed setup instructions.
### Codex OAuth2 Authentication
AISBF supports OpenAI Codex as a provider using OAuth2 Device Authorization Grant:
#### Features
- Full OAuth2 Device Authorization Grant flow (same protocol as OpenAI)
- Automatic token refresh with refresh token rotation
- API key exchange from ID token for direct API access
- Dashboard integration with authentication UI
- No localhost callback port needed (device code flow)
- Credentials stored in `~/.aisbf/codex_credentials.json`
#### Setup
1. Add codex provider to configuration (via dashboard or `~/.aisbf/providers.json`)
2. Click "Authenticate with Codex (Device Code)" in dashboard
3. Complete device authorization flow at `https://auth.openai.com/codex/device`
4. Use codex models via API: `codex/<model>`
#### Configuration Example
```json
{
"providers": {
"codex": {
"id": "codex",
"name": "Codex (OpenAI OAuth2)",
"endpoint": "https://api.openai.com/v1",
"type": "codex",
"api_key_required": false,
"codex_config": {
"credentials_file": "~/.aisbf/codex_credentials.json",
"issuer": "https://auth.openai.com"
},
"models": [
{
"name": "gpt-4o",
"context_size": 128000
}
]
}
}
}
```
## Configuration
### SSL/TLS Configuration
......
This diff is collapsed.
......@@ -39,6 +39,7 @@ from .claude import ClaudeProviderHandler
from .kiro import KiroProviderHandler
from .kilo import KiloProviderHandler
from .ollama import OllamaProviderHandler
from .codex import CodexProviderHandler
from ..config import config
......@@ -50,7 +51,8 @@ PROVIDER_HANDLERS = {
'kiro': KiroProviderHandler,
'claude': ClaudeProviderHandler,
'kilo': KiloProviderHandler,
'kilocode': KiloProviderHandler # Kilocode provider with OAuth2 support
'kilocode': KiloProviderHandler, # Kilocode provider with OAuth2 support
'codex': CodexProviderHandler # Codex provider with OAuth2 support (OpenAI protocol)
}
......
This diff is collapsed.
This diff is collapsed.
......@@ -322,6 +322,21 @@
"capabilities": ["t2t", "vision", "function_calling"]
}
]
},
"codex": {
"id": "codex",
"name": "Codex (OpenAI OAuth2)",
"endpoint": "https://api.openai.com/v1",
"type": "codex",
"api_key_required": false,
"nsfw": false,
"privacy": false,
"rate_limit": 0,
"codex_config": {
"_comment": "Uses OAuth2 Device Authorization Grant (Codex CLI compatible)",
"credentials_file": "~/.aisbf/codex_credentials.json",
"issuer": "https://auth.openai.com"
}
}
}
}
......@@ -6568,5 +6568,198 @@ async def dashboard_kilo_auth_logout(request: Request):
)
# Codex OAuth2 authentication endpoints
@app.post("/dashboard/codex/auth/start")
async def dashboard_codex_auth_start(request: Request):
"""Start Codex OAuth2 Device Authorization Grant flow"""
auth_check = require_dashboard_auth(request)
if auth_check:
return auth_check
try:
data = await request.json()
provider_key = data.get('provider_key')
credentials_file = data.get('credentials_file', '~/.aisbf/codex_credentials.json')
issuer = data.get('issuer', 'https://auth.openai.com')
if not provider_key:
return JSONResponse(
status_code=400,
content={"success": False, "error": "Provider key is required"}
)
# Import CodexOAuth2
from aisbf.auth.codex import CodexOAuth2
# Create auth instance
auth = CodexOAuth2(credentials_file=credentials_file, issuer=issuer)
# Initiate device authorization (async method)
device_auth = await auth.authenticate_with_device_flow()
if not device_auth:
return JSONResponse(
status_code=500,
content={"success": False, "error": "Failed to initiate device authorization"}
)
# Store device code in session for polling
request.session['codex_device_code'] = device_auth.get('user_code')
request.session['codex_provider'] = provider_key
request.session['codex_credentials_file'] = credentials_file
request.session['codex_issuer'] = issuer
return JSONResponse({
"success": True,
"user_code": device_auth.get('user_code'),
"verification_uri": device_auth.get('verification_uri', f'{issuer}/codex/device'),
"expires_in": 900, # 15 minutes
"interval": 5,
"message": f"Please visit {device_auth.get('verification_uri', f'{issuer}/codex/device')} and enter code: {device_auth.get('user_code')}"
})
except Exception as e:
logger.error(f"Error starting Codex auth: {e}")
return JSONResponse(
status_code=500,
content={"success": False, "error": str(e)}
)
@app.post("/dashboard/codex/auth/poll")
async def dashboard_codex_auth_poll(request: Request):
"""Poll Codex OAuth2 device authorization status"""
auth_check = require_dashboard_auth(request)
if auth_check:
return auth_check
try:
# Check if authentication was completed
credentials_file = request.session.get('codex_credentials_file', '~/.aisbf/codex_credentials.json')
issuer = request.session.get('codex_issuer', 'https://auth.openai.com')
# Import CodexOAuth2
from aisbf.auth.codex import CodexOAuth2
# Create auth instance
auth = CodexOAuth2(credentials_file=credentials_file, issuer=issuer)
# Check if authenticated
if auth.is_authenticated():
# Clear session
request.session.pop('codex_device_code', None)
request.session.pop('codex_provider', None)
request.session.pop('codex_credentials_file', None)
request.session.pop('codex_issuer', None)
return JSONResponse({
"success": True,
"status": "approved",
"message": "Authentication completed successfully"
})
else:
return JSONResponse({
"success": True,
"status": "pending",
"message": "Waiting for user authorization"
})
except Exception as e:
logger.error(f"Error polling Codex auth: {e}")
return JSONResponse(
status_code=500,
content={"success": False, "status": "error", "error": str(e)}
)
@app.post("/dashboard/codex/auth/status")
async def dashboard_codex_auth_status(request: Request):
"""Check Codex authentication status"""
auth_check = require_dashboard_auth(request)
if auth_check:
return auth_check
try:
data = await request.json()
provider_key = data.get('provider_key')
credentials_file = data.get('credentials_file', '~/.aisbf/codex_credentials.json')
if not provider_key:
return JSONResponse(
status_code=400,
content={"authenticated": False, "error": "Provider key is required"}
)
# Import CodexOAuth2
from aisbf.auth.codex import CodexOAuth2
# Create auth instance
auth = CodexOAuth2(credentials_file=credentials_file)
# Check if authenticated
if auth.is_authenticated():
# Try to get a valid token (will refresh if needed)
token = auth.get_valid_token()
if token:
# Get user email from ID token
email = auth.get_user_email()
return JSONResponse({
"authenticated": True,
"email": email
})
return JSONResponse({
"authenticated": False
})
except Exception as e:
logger.error(f"Error checking Codex auth status: {e}")
return JSONResponse(
status_code=500,
content={"authenticated": False, "error": str(e)}
)
@app.post("/dashboard/codex/auth/logout")
async def dashboard_codex_auth_logout(request: Request):
"""Logout from Codex OAuth2 (clear stored credentials)"""
auth_check = require_dashboard_auth(request)
if auth_check:
return auth_check
try:
data = await request.json()
provider_key = data.get('provider_key')
credentials_file = data.get('credentials_file', '~/.aisbf/codex_credentials.json')
if not provider_key:
return JSONResponse(
status_code=400,
content={"success": False, "error": "Provider key is required"}
)
# Import CodexOAuth2
from aisbf.auth.codex import CodexOAuth2
# Create auth instance
auth = CodexOAuth2(credentials_file=credentials_file)
# Logout (clear credentials)
auth.logout()
return JSONResponse({
"success": True,
"message": "Logged out successfully"
})
except Exception as e:
logger.error(f"Error logging out from Codex: {e}")
return JSONResponse(
status_code=500,
content={"success": False, "error": str(e)}
)
if __name__ == "__main__":
main()
......@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "aisbf"
version = "0.9.7"
version = "0.9.8"
description = "AISBF - AI Service Broker Framework || AI Should Be Free - A modular proxy server for managing multiple AI provider integrations"
readme = "README.md"
license = "GPL-3.0-or-later"
......@@ -49,7 +49,7 @@ Documentation = "https://git.nexlab.net/nexlab/aisbf.git"
[tool.setuptools]
packages = ["aisbf", "aisbf.auth", "aisbf.providers", "aisbf.providers.kiro"]
# Note: Provider handler modules (base, google, openai, anthropic, claude, kilo, ollama) are in aisbf.providers package
# Note: Provider handler modules (base, google, openai, anthropic, claude, kilo, ollama, codex) are in aisbf.providers package
py-modules = ["cli"]
[tool.setuptools.package-data]
......
......@@ -49,7 +49,7 @@ class InstallCommand(_install):
setup(
name="aisbf",
version="0.9.7",
version="0.9.8",
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",
......@@ -122,6 +122,7 @@ setup(
'aisbf/providers/claude.py',
'aisbf/providers/kilo.py',
'aisbf/providers/ollama.py',
'aisbf/providers/codex.py',
]),
# aisbf.providers.kiro subpackage
('share/aisbf/aisbf/providers/kiro', [
......@@ -139,6 +140,7 @@ setup(
'aisbf/auth/kiro.py',
'aisbf/auth/claude.py',
'aisbf/auth/kilo.py',
'aisbf/auth/codex.py',
]),
# Install dashboard templates
('share/aisbf/templates', [
......
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