Qwen OAuth authentication plugin for OpenCode with multi-account rotation, proactive token refresh, and automatic API translation.
- Device Flow OAuth - PKCE-secured authentication, works in headless/CI environments
- Multi-Account Support - Store and rotate between multiple Qwen accounts
- Proactive Token Refresh - Automatically refresh tokens before expiry
- Rate Limit Handling - Detects 429 responses, rotates accounts, respects retry-after
- API Translation - Bridges OpenAI Responses API ↔ Chat Completions API
- Streaming Support - Full SSE transformation for real-time responses
Paste this into any LLM agent (Claude Code, OpenCode, Cursor, etc.):
Install the opencode-qwen-auth plugin by following: https://raw.githubusercontent.com/foxswat/opencode-qwen-auth/main/README.md
Run one command to automatically configure OpenCode:
bunx opencode-qwen-auth install
# or
npx opencode-qwen-auth installThis adds the plugin and Qwen provider configuration to your opencode.json.
If you prefer manual setup:
# Using Bun
bun add opencode-qwen-auth
# Using npm
npm install opencode-qwen-authThen add to your opencode.json:
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["opencode-qwen-auth"],
"provider": {
"qwen": {
"npm": "@ai-sdk/openai",
"options": {
"baseURL": "https://portal.qwen.ai/v1",
"compatibility": "strict"
},
"models": {
"qwen3-coder-plus": { "contextWindow": 1048576 },
"qwen3-vl-plus": { "contextWindow": 262144, "attachment": true }
}
}
}
}-
Start OpenCode in your project directory:
opencode
-
Authenticate with Qwen:
/authSelect Qwen OAuth and follow the device flow instructions.
-
Start coding with Qwen models:
/model qwen/qwen3-coder-plus
No configuration required. The plugin works out of the box with sensible defaults.
To customize behavior, create .opencode/qwen.json (project) or ~/.config/opencode/qwen.json (user-level) with only the options you want to override:
| Option | Default | Description |
|---|---|---|
base_url |
https://portal.qwen.ai/v1 |
API endpoint for Qwen requests |
client_id |
(built-in) | OAuth client ID |
oauth_base_url |
https://chat.qwen.ai |
OAuth server URL |
rotation_strategy |
round-robin |
Account rotation: round-robin or sequential |
proactive_refresh |
true |
Refresh tokens before expiry |
refresh_window_seconds |
300 |
Seconds before expiry to trigger refresh |
max_rate_limit_wait_seconds |
300 |
Maximum wait time when rate limited |
quiet_mode |
false |
Suppress informational messages |
All options can be overridden via environment variables:
QWEN_API_BASE_URLQWEN_OAUTH_CLIENT_IDQWEN_OAUTH_BASE_URLQWEN_ROTATION_STRATEGYQWEN_PROACTIVE_REFRESHQWEN_REFRESH_WINDOW_SECONDSQWEN_MAX_RATE_LIMIT_WAIT_SECONDSQWEN_QUIET_MODE
| Model | Context Window | Features |
|---|---|---|
qwen3-coder-plus |
1M tokens | Optimized for coding tasks |
qwen3-vl-plus |
256K tokens | Vision + language multimodal |
Add multiple accounts for higher throughput:
- Run
/authand complete the first login - Run
/authagain to add additional accounts - The plugin automatically rotates between accounts
- round-robin: Cycles through accounts on each request
- sequential: Uses one account until rate limited, then switches
This plugin bridges OpenCode's Responses API format with Qwen's Chat Completions API:
OpenCode → [Responses API] → Plugin → [Chat Completions] → Qwen
↓
OpenCode ← [Responses API] ← Plugin ← [Chat Completions] ← Qwen
| Responses API | Chat Completions API |
|---|---|
input |
messages |
input_text |
text content type |
input_image |
image_url content type |
instructions |
System message |
max_output_tokens |
max_tokens |
Converts SSE events from Chat Completions to Responses API format:
response.createdresponse.output_item.addedresponse.content_part.addedresponse.output_text.deltaresponse.completed
| Data | Location |
|---|---|
| User config | ~/.config/opencode/qwen.json |
| Project config | .opencode/qwen.json |
| Account tokens | ~/.config/opencode/qwen-auth-accounts.json |
Security Note: Tokens are stored with restricted permissions (0600). Ensure appropriate filesystem security.
"invalid_grant" error
- Your refresh token has expired. Run
/authto re-authenticate.
Device code expired
- Complete the browser login within 5 minutes of starting
/auth.
Frequent 429 errors
- Add more accounts with
/auth - Increase
max_rate_limit_wait_secondsin config
To start fresh, delete the accounts file:
rm ~/.config/opencode/qwen-auth-accounts.jsonThis project uses Bun for development.
- Bun 1.0+ (recommended)
- Node.js 20+ (for npm compatibility)
# Install dependencies
bun install
# Build
bun run build
# Run tests
bun test
# Run tests in watch mode
bun test --watch
# Run e2e test (requires authenticated Qwen account)
bun run test:e2e
# Link for local testing
bun linkThe project also works with npm:
npm install
npm run build
npm test- Audio input (
input_audio) is not supported by Qwen and is converted to placeholder text
Apache-2.0
{ // API endpoint (default: https://portal.qwen.ai/v1) "base_url": "https://portal.qwen.ai/v1", // OAuth client ID (default: built-in) "client_id": "your-client-id", // OAuth server URL (default: https://chat.qwen.ai) "oauth_base_url": "https://chat.qwen.ai", // Account rotation: "round-robin" or "sequential" (default: round-robin) "rotation_strategy": "sequential", // Refresh tokens before expiry (default: true) "proactive_refresh": true, // Seconds before expiry to trigger refresh (default: 300) "refresh_window_seconds": 300, // Maximum wait time when rate limited (default: 300) "max_rate_limit_wait_seconds": 300, // Suppress informational messages (default: false) "quiet_mode": true }