Repository navigation
feat(provider): add RFC 8628 device-flow OAuth for custom gateways - #34785
Closed
fijimunkii wants to merge 1 commit into
Closed
fijimunkii wants to merge 1 commit into
fijimunkii wants to merge 1 commit into
Conversation
Contributor
|
Thanks for updating your PR! It now meets our contributing guidelines. 👍 |
Custom OpenAI-compatible providers can now declare RFC 8628 device-flow
OAuth endpoints under `options` and get the same browser-based SSO UX
that built-in providers (xAI, Copilot) already provide. opencode prints
a verification URL + user code, polls the token endpoint, and persists
the resulting access/refresh token pair to auth.json. Refresh is
proactive (120s skew) and single-flight per provider instance.
Config:
```json
{
"provider": {
"my-gateway": {
"npm": "@ai-sdk/openai-compatible",
"options": {
"baseURL": "https://my-gateway.example.com/v1",
"deviceAuthorizationUrl": "https://my-gateway.example.com/oauth/device_authorization",
"tokenUrl": "https://my-gateway.example.com/oauth/token"
}
}
}
}
```
Optional fields: `oauthClientId` (default "opencode"), `oauthScope`
(omitted from POST body when unset).
Implementation ports the device-code + refresh logic from `xai.ts` into
a parameterized `oauth-device.ts` plugin factory. `internalPlugins()`
derives one plugin instance per config entry that has both
`deviceAuthorizationUrl` and `tokenUrl`.
Tested end-to-end against an internal LiteLLM gateway exposing RFC 8628
endpoints.
Localized docs (17 translated providers.mdx copies) intentionally
skipped — no translation workflow exists in-repo; happy to follow up
if maintainers want them.
Co-Authored-By: Claude Opus 4.7 <[email protected]>
fijimunkii
force-pushed
the
feat/custom-oauth-device-flow
branch
from
July 1, 2026 19:32
4de37d9 to
18e1512
Compare
Contributor
|
Automated PR Cleanup Thank you for contributing to opencode. Due to the high volume of PRs from users and AI agents, we periodically close older PRs using automated criteria so maintainers can focus review time on the most active and community-supported contributions. This PR was closed because it matched the following cleanup criteria:
PRs created within the last month are not affected by this cleanup. If you believe this PR was closed incorrectly, or if you are still actively working on it, please leave a comment explaining why it should be reopened. A maintainer can review and reopen it if appropriate. Thanks again for taking the time to contribute. |
1 task done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Issue for this PR
Closes # (no associated issue — opening as a feature contribution per CONTRIBUTING.md)
Type of change
What does this PR do?
Adds a generic RFC 8628 device-flow OAuth provider type so any custom OpenAI-compatible gateway can expose browser-based SSO with the same UX as built-in providers (xAI, Copilot).
Users declare
deviceAuthorizationUrl+tokenUrlunder a provider'soptionsblock inopencode.json. opencode detects these and auto-registers a device-flow plugin for that provider — no CLI changes, no per-gateway code.{ "provider": { "my-gateway": { "npm": "@ai-sdk/openai-compatible", "options": { "baseURL": "https://my-gateway.example.com/v1", "deviceAuthorizationUrl": "https://my-gateway.example.com/oauth/device_authorization", "tokenUrl": "https://my-gateway.example.com/oauth/token" } } } }Optional:
options.oauthClientId(default"opencode"),options.oauthScope(omitted from POST body when unset).Implementation:
packages/opencode/src/plugin/oauth-device.ts— parameterized factoryOAuthDeviceProviderPluginporting device-code + refresh logic fromxai.ts. Proactive refresh with 120s skew, single-flight per provider instance via module-levelrefreshPromisesmap, rotated tokens persisted toauth.jsonviainput.client.auth.set.packages/opencode/src/plugin/index.ts—internalPlugins()now takescfgand derives one plugin instance per config entry that has bothdeviceAuthorizationUrlandtokenUrl.packages/core/src/v1/config/provider.ts— four optional fields onoptions:deviceAuthorizationUrl,tokenUrl,oauthClientId,oauthScope.packages/web/src/content/docs/providers.mdx— new "Custom OAuth (device flow)" section.Requirements on the OAuth server side: standard RFC 8628 endpoints (
device_authorizationreturnsdevice_code/user_code/verification_uri; token endpoint acceptsgrant_type=urn:ietf:params:oauth:grant-type:device_code, handlesauthorization_pending/slow_down/access_denied/expired_token, returnsaccess_token+refresh_token).token_typeSHOULD beBearer.How did you verify your code works?
cd packages/opencode && bun test test/plugin/oauth-device.test.ts— 24/24 pass. CoversrequestDeviceCode(default client_id, custom client_id, scope omission, error surfacing),pollDeviceCodeToken(success,slow_down/authorization_pendingbackoff, terminal errors, timeout, NaN-interval defense), and loader behavior (non-oauth passthrough, dummy bearer injection, header override, auth-type flip, proactive refresh + persistence, single-flight dedup, cross-instance isolation).cd packages/opencode && bun test test/plugin/xai.test.ts— 26/26 pass (regression).bun turbo typecheck --force --filter=@opencode-ai/core --filter=@opencode-ai/server— clean.opencode auth logincompletes the browser flow, access token is injected asAuthorization: Bearer …on gateway requests, proactive refresh fires before expiry, and chat completions succeed.Screenshots / recordings
N/A — no UI changes. Login flow reuses the existing device-flow picker.
Checklist