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 ...@@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [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 ## [0.9.7] - 2026-04-03
### Fixed ### Fixed
......
...@@ -156,6 +156,11 @@ AISBF supports OAuth2 authentication for several providers: ...@@ -156,6 +156,11 @@ AISBF supports OAuth2 authentication for several providers:
- Device Authorization Grant OAuth2 flow - Device Authorization Grant OAuth2 flow
- Seamless integration with Kilocode services - 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:** **Setup Instructions:**
1. Start AISBF: `aisbf` 1. Start AISBF: `aisbf`
2. Access dashboard: `http://localhost:17765/dashboard` 2. Access dashboard: `http://localhost:17765/dashboard`
......
...@@ -24,9 +24,10 @@ Access the dashboard at `http://localhost:17765/dashboard` (default credentials: ...@@ -24,9 +24,10 @@ Access the dashboard at `http://localhost:17765/dashboard` (default credentials:
## Key Features ## 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 - **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 - **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 - **Rotation Models**: Weighted load balancing across multiple providers with automatic failover
- **Autoselect Models**: AI-powered model selection based on content analysis and request characteristics - **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) - **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. ...@@ -134,6 +135,7 @@ See [`PYPI.md`](PYPI.md) for detailed instructions on publishing to PyPI.
- Kiro (Amazon Q Developer / AWS CodeWhisperer) - Kiro (Amazon Q Developer / AWS CodeWhisperer)
- Kiro-cli (Amazon Q Developer CLI authentication) - Kiro-cli (Amazon Q Developer CLI authentication)
- Kilocode (OAuth2 Device Authorization Grant) - Kilocode (OAuth2 Device Authorization Grant)
- Codex (OAuth2 Device Authorization Grant - OpenAI protocol)
### Kiro-cli Provider Support ### Kiro-cli Provider Support
...@@ -218,6 +220,50 @@ AISBF supports Kilo Code as a provider using OAuth2 Device Authorization Grant: ...@@ -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. 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 ## Configuration
### SSL/TLS Configuration ### SSL/TLS Configuration
......
This diff is collapsed.
...@@ -39,6 +39,7 @@ from .claude import ClaudeProviderHandler ...@@ -39,6 +39,7 @@ from .claude import ClaudeProviderHandler
from .kiro import KiroProviderHandler from .kiro import KiroProviderHandler
from .kilo import KiloProviderHandler from .kilo import KiloProviderHandler
from .ollama import OllamaProviderHandler from .ollama import OllamaProviderHandler
from .codex import CodexProviderHandler
from ..config import config from ..config import config
...@@ -50,7 +51,8 @@ PROVIDER_HANDLERS = { ...@@ -50,7 +51,8 @@ PROVIDER_HANDLERS = {
'kiro': KiroProviderHandler, 'kiro': KiroProviderHandler,
'claude': ClaudeProviderHandler, 'claude': ClaudeProviderHandler,
'kilo': KiloProviderHandler, '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 @@ ...@@ -322,6 +322,21 @@
"capabilities": ["t2t", "vision", "function_calling"] "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): ...@@ -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__": if __name__ == "__main__":
main() main()
...@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" ...@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "aisbf" 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" description = "AISBF - AI Service Broker Framework || AI Should Be Free - A modular proxy server for managing multiple AI provider integrations"
readme = "README.md" readme = "README.md"
license = "GPL-3.0-or-later" license = "GPL-3.0-or-later"
...@@ -49,7 +49,7 @@ Documentation = "https://git.nexlab.net/nexlab/aisbf.git" ...@@ -49,7 +49,7 @@ Documentation = "https://git.nexlab.net/nexlab/aisbf.git"
[tool.setuptools] [tool.setuptools]
packages = ["aisbf", "aisbf.auth", "aisbf.providers", "aisbf.providers.kiro"] 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"] py-modules = ["cli"]
[tool.setuptools.package-data] [tool.setuptools.package-data]
......
...@@ -49,7 +49,7 @@ class InstallCommand(_install): ...@@ -49,7 +49,7 @@ class InstallCommand(_install):
setup( setup(
name="aisbf", name="aisbf",
version="0.9.7", version="0.9.8",
author="AISBF Contributors", author="AISBF Contributors",
author_email="stefy@nexlab.net", 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", 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( ...@@ -122,6 +122,7 @@ setup(
'aisbf/providers/claude.py', 'aisbf/providers/claude.py',
'aisbf/providers/kilo.py', 'aisbf/providers/kilo.py',
'aisbf/providers/ollama.py', 'aisbf/providers/ollama.py',
'aisbf/providers/codex.py',
]), ]),
# aisbf.providers.kiro subpackage # aisbf.providers.kiro subpackage
('share/aisbf/aisbf/providers/kiro', [ ('share/aisbf/aisbf/providers/kiro', [
...@@ -139,6 +140,7 @@ setup( ...@@ -139,6 +140,7 @@ setup(
'aisbf/auth/kiro.py', 'aisbf/auth/kiro.py',
'aisbf/auth/claude.py', 'aisbf/auth/claude.py',
'aisbf/auth/kilo.py', 'aisbf/auth/kilo.py',
'aisbf/auth/codex.py',
]), ]),
# Install dashboard templates # Install dashboard templates
('share/aisbf/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