Reuse your AI subscriptions. One module, every provider. OAuth PKCE for ChatGPT Plus/Pro, API keys for Claude/Gemini/DeepSeek, device code for Copilot. ~500 LOC, only depends on httpx.
SaferSkills independently audited ai-sub-auth (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Reuse your AI subscriptions. One module, every provider. Code + knowledge reference for AI agents.
English | 中文
You're already paying $20/month for ChatGPT Plus. Why pay again for API access when building your own apps?
ai-sub-auth is a lightweight Python module (~600 lines, only depends on httpx) that bridges your existing AI subscriptions into any application — no proxy servers, no middleware, no bloat. It serves two purposes:
AGENT.md and a Meta Skill Framework) that teaches agents how to integrate AI capabilities into any application.| Pain Point | Details |
|---|---|
| Double payment | AI subscriptions ($20/mo) + API fees ($50–200/mo) for the same models |
| Fragmented auth | Each provider uses different auth flows (OAuth, API keys, device codes) |
| Proxy overhead | Existing solutions require running a separate proxy server 24/7 |
| Token management | Manual token refresh, insecure storage, no concurrency safety |
pip install httpx # only dependencyfrom ai_sub_auth import AI
ai = AI() # auto-detects ChatGPT Plus OAuth token or API key env vars
ai.connect() # OAuth login or API key verification
# Async
result = await ai.chat("Summarize this meeting transcript", system="Be concise.")
# Sync (safe in any context — FastAPI, Jupyter, scripts)
result = ai.chat_sync("Summarize this meeting transcript")
# Multi-turn conversation
result = await ai.chat(messages=[
{"role": "user", "content": "What is quantum computing?"},
{"role": "assistant", "content": "Quantum computing uses..."},
{"role": "user", "content": "How does it differ from classical?"},
])from ai_sub_auth import LLMClient, oauth_login, PROVIDERS
# One-time: login with your ChatGPT Plus/Pro subscription
oauth_login(PROVIDERS["openai_codex"])
# Forever after: just use it
client = LLMClient(PROVIDERS["openai_codex"], model="openai-codex/gpt-4o")
resp = await client.chat("Summarize this meeting transcript")
print(resp.content)Token refresh, secure storage, PKCE — all handled automatically.
Not all AI subscriptions can be bridged into third-party apps. Here's what actually works:
| Provider | Method | Can Bridge Subscription? | Notes |
|---|---|---|---|
| OpenAI ChatGPT Plus/Pro | OAuth PKCE | ✅ Yes — zero extra cost | OpenAI actively supports this via Codex SDK |
| Claude | API Key | ❌ API key only (pay-per-token) | Anthropic banned subscription OAuth (Feb 2026) |
| OpenAI | API Key | ❌ API key only (pay-per-token) | Separate from ChatGPT subscription |
| GitHub Copilot | Device Code | ⚠️ Works but violates ToS | Not recommended for production |
| Google Gemini | API Key | ❌ API key only (pay-per-token) | Google permanently bans accounts for OAuth abuse |
| DeepSeek | API Key | ❌ API key only (pay-per-token) | Standard API key auth |
| OpenRouter | API Key | ❌ API key only (pay-per-token) | One key, all models |
Key insight: OpenAI Codex OAuth is the only path where users can reuse their existing subscription ($20/mo ChatGPT Plus) in third-party apps at zero additional cost. All other providers require separate API keys with pay-per-token billing.
Anti-patterns: Do NOT attempt to bridge Claude Pro/Max or Google Gemini subscriptions via OAuth. Anthropic will block it; Google will permanently ban the user's account.
from ai_sub_auth import AI
# Auto-detects: existing OAuth token → env var API keys
ai = AI()
ai.connect()
# Sync usage (simplest)
result = ai.chat_sync("What is quantum computing?", system="Be concise.")
print(result.content)
# Check subscription status
print(ai.status) # SubscriptionStatus(connected=True, provider_name='OpenAI Codex', ...)import asyncio
from ai_sub_auth import AI
ai = AI(provider="openai_codex")
ai.connect() # First time: browser opens for OAuth login. After: automatic.
async def main():
resp = await ai.chat("What is quantum computing?", system="Be concise.")
print(resp.content)
asyncio.run(main())from ai_sub_auth import AI
ai = AI(api_key="sk-ant-...") # auto-detects Anthropic from key prefix
result = ai.chat_sync("Explain transformers in 3 sentences")
print(result.content)
print(f"Tokens: {result.usage}")import asyncio
from ai_sub_auth import LLMClient, PROVIDERS
async def main():
client = LLMClient(PROVIDERS["google_gemini"], api_key="AI...", model="gemini-2.0-flash")
resp = await client.chat("Write a haiku about Python")
print(resp.content)
asyncio.run(main())import asyncio
from ai_sub_auth import LLMClient, PROVIDERS
async def main():
client = LLMClient(PROVIDERS["openrouter"], api_key="sk-or-...", model="anthropic/claude-sonnet-4-5")
resp = await client.chat("Hello!")
print(resp.content)
asyncio.run(main())┌──────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Your App │────▶│ ai-sub-auth │────▶│ Provider API │
│ │ │ │ │ (OpenAI, etc.) │
│ 3 lines of │ │ ┌─ OAuth PKCE │ │ │
│ code │ │ ├─ Token Store │ │ │
│ │ │ ├─ Auto Refresh │ │ │
│ │ │ └─ Provider Reg │ │ │
└──────────────┘ └──────────────────┘ └─────────────────┘OAuth PKCE flow (Codex):
localhost:1455~/.ai-sub-auth/tokens/ with 0600 permissions| Feature | ai-sub-auth | CLIProxyAPI | ccproxy-api | ProxyPilot |
|---|---|---|---|---|
| Type | Library (import) | Proxy server | Proxy server | Proxy server |
| Running process | None | Required | Required | Required |
| Language | Python | Go | Python | TypeScript |
| Size | ~500 LOC | 1000s LOC | 1000s LOC | 1000s LOC |
| Dependencies | httpx only | Many | Many | Many |
| Integration | import | HTTP proxy | HTTP proxy | HTTP proxy |
from ai_sub_auth import ProviderConfig, AuthMethod, LLMClient, PROVIDERS
my_provider = ProviderConfig(
name="my_llm",
display_name="My LLM Service",
auth_method=AuthMethod.API_KEY,
env_key="MY_LLM_API_KEY",
api_base="https://api.my-llm.com/v1", # OpenAI-compatible endpoint
keywords=("my-llm",),
)
# Register it
PROVIDERS["my_llm"] = my_provider
# Use it
client = LLMClient(my_provider, api_key="...", model="my-model-v1")~/.ai-sub-auth/tokens/ with 0600 file permissions~/.codex/auth.json (no re-login needed)fcntl) and Windows (msvcrt) file lockingai_sub_auth/
├── __init__.py # Public API
├── facade.py # AI Facade — auto-detect, connect, chat (recommended entry point)
├── models.py # OAuthToken, ProviderConfig, AuthMethod, LLMResponse
├── exceptions.py # AuthError, TokenExpiredError, ProviderNotFoundError
├── providers.py # Provider registry (Codex, Claude, Gemini, etc.)
├── oauth_flow.py # OAuth PKCE flow + token refresh
├── token_store.py # Secure file-based token storage
├── api_client.py # Unified LLMClient
└── skills.py # Meta Skill Framework (8 skills + 3-Suggestion Protocol)For AI Agents: See AGENT.md for the full integration guide.This module includes a Meta Skill Framework that helps AI agents (Claude Code, OpenClaw, Cursor, etc.) automatically discover how to integrate AI features into any application.
Every useful AI feature is a composition of exactly 8 universal skills:
SUMMARIZE many → few Reduce volume, preserve meaning
GENERATE few → many Produce new content from intent
ANALYZE raw → insight Find patterns in data
TRANSFORM form A → form B Convert between formats/styles
CLASSIFY items → buckets Assign categories or priorities
EVALUATE content → score Judge quality against criteria
CONVERSE user ↔ AI Multi-turn contextual dialogue
EXTRACT noise → signal Pull structure from chaosWhen an agent encounters any app, it follows: SCAN → MATCH → RANK → PRESENT → IMPLEMENT
from ai_sub_auth import AppProfile, suggest_for_app
profile = AppProfile(
domain="note-taking",
verbs=["create notes", "search", "tag", "link"],
nouns=["notes", "tags", "folders", "backlinks"],
roles=["user"],
existing_ai=[],
)
suggestions = suggest_for_app(profile)
for s in suggestions:
print(f"{s.skill.name}: {s.reason} [{s.effort}]")This produces exactly 3 diverse, ranked suggestions — at least one quick win, at least one high-ceiling opportunity. See AGENT.md for the complete protocol and implementation patterns.
Built by studying and distilling patterns from:
MIT
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.