fix: scope managed OAuth credentials - #399
Conversation
🦋 Changeset detectedLatest commit: 78f5c1a The changes in this PR will be included in the next version bump. This PR includes changesets to release 4 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
commit: |
7c8ee72 to
6a5d882
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7c8ee72530
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const oauthHost = persistedOAuthHost(options.oauthHost); | ||
| return { | ||
| storage: 'file', | ||
| key: options.key, | ||
| oauthHost, |
There was a problem hiding this comment.
Persist the OAuth host for scoped credential refs
When a user logs into a non-default API baseUrl while using the default OAuth host, this omits oauthHost from the persisted ref even though the generated key was hashed with the default host. In a later shell that sets KIMI_CODE_OAUTH_HOST/KIMI_OAUTH_HOST, oauthHostFor() falls back to that env host for the same stored key, so refresh/runtime token lookup uses credentials scoped for one auth server against another auth server. Persist the host for scoped refs, or make explicit refs independent of the current environment.
Useful? React with 👍 / 👎.
| baseUrl: options.baseUrl ?? this.configBaseUrlForLogin(auth.baseUrl), | ||
| oauthRef: options.oauthRef ?? this.configOAuthRefForLogin(auth.oauthRef, options), |
There was a problem hiding this comment.
Recompute stale legacy OAuth refs on login
For users who already have a pre-fix non-default managed config, base_url can point at a dev/staging API while oauth.key is still the legacy oauth/kimi-code. A plain /login with no env/CLI overrides now passes that stale config ref through here, and the toolkit treats options.oauthRef.key as authoritative instead of deriving the scoped key from the configured baseUrl, so provisioning writes the legacy key back and the prod/dev credential slot remains shared. Detect legacy/mismatched refs and recompute from the configured endpoints during login.
Useful? React with 👍 / 👎.
| return this.toolkit.getManagedUsage(providerName, { | ||
| oauthRef: auth.oauthRef, | ||
| baseUrl: auth.baseUrl, |
There was a problem hiding this comment.
Honor env endpoint overrides for managed usage
When KIMI_CODE_BASE_URL is set to temporarily target another managed environment while the config still contains a persisted base_url, login() deliberately ignores the configured base URL, but usage and feedback now always pass auth.baseUrl from the config. In that environment override scenario these calls still hit the old configured /usages and /feedback endpoints with the wrong scoped token, so they can report the wrong account or fail authorization; apply the same env-precedence rule used for login before passing baseUrl.
Useful? React with 👍 / 👎.
| (await options.harness.auth.getCachedAccessToken( | ||
| KIMI_CODE_PROVIDER_NAME, | ||
| options.config.providers[KIMI_CODE_PROVIDER_NAME]?.oauth, |
There was a problem hiding this comment.
Don't capture a stale OAuth ref for telemetry
This closes over the config object loaded at startup, but /login and /logout can replace the managed provider and its scoped OAuth ref during the same TUI/CLI process. After switching from one managed environment to another, later telemetry flushes still call getCachedAccessToken with the old ref instead of letting the auth facade read the current config, so events can be sent with the previous environment's token or lose auth entirely; resolve the ref lazily rather than capturing options.config here.
Useful? React with 👍 / 👎.
6a5d882 to
3e25839
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3e25839ce9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
|
||
| private expectedOAuthRef(oauthRef: OAuthRef | undefined, baseUrl?: string | undefined): OAuthRef { | ||
| return resolveKimiCodeOAuthRef({ | ||
| oauthHost: this.envOAuthHost() ?? oauthRef?.oauthHost, |
There was a problem hiding this comment.
Match login's OAuth host when base URL is overridden
When only KIMI_CODE_BASE_URL is set over a config that already has oauth.oauthHost, runtime auth recomputes the credential ref with the old configured OAuth host here, but login() discards the configured ref on the same env override and derives the slot from the default/env OAuth host. In that scenario /login writes one scoped token, while status/usage/session auth look in a different slot and can still report logged out or fail authorization unless KIMI_CODE_OAUTH_HOST is also set.
Useful? React with 👍 / 👎.
| return this.toolkit.tokenProvider( | ||
| providerName, | ||
| oauthRef ?? this.resolveRuntimeManagedAuth(providerName).oauthRef, |
There was a problem hiding this comment.
Normalize configured OAuth refs for runtime auth
For an existing pre-scoping config whose base_url is non-default but whose oauth.key is still oauth/kimi-code, ProviderManager passes that explicit ref into this resolver, so the new configOAuthRefForRuntime repair path is skipped. A normal session can therefore call the non-default API with the legacy production token until the user runs /login; normalize explicit managed refs against the configured provider before building the token provider.
Useful? React with 👍 / 👎.
| models, | ||
| baseUrl: managedProvider.baseUrl, | ||
| oauthKey: managedProvider.oauth.key, | ||
| oauthHost: managedProvider.oauth.oauthHost, |
There was a problem hiding this comment.
Persist refreshed service OAuth refs
Passing oauthHost into applyManagedKimiCodeConfig updates the provider and the managed service refs in the local config, but the patch sent to setConfig below still omits services. Because config patches are deep-merged, a refresh that repairs a scoped ref (for example an old custom-base-url config missing oauth_host) persists the provider ref while moonshot_search/moonshot_fetch keep stale OAuth refs and later resolve tokens against the wrong host; include services in this patch too.
Useful? React with 👍 / 👎.
| baseUrl: options.baseUrl, | ||
| oauthKey, | ||
| oauthHost, | ||
| preserveDefaultModel: hadToken, |
There was a problem hiding this comment.
Preserve defaults when migrating legacy login slots
When an existing non-default managed config still points at the legacy oauth/kimi-code slot, the facade now recomputes the scoped slot before login; if that new slot is empty, hadToken is false even though the user already has the old managed config/token. A plain /login migration then provisions with preserveDefaultModel: false and can reset the user's current default model to the first managed model, whereas the legacy-slot login path used to preserve it; base preservation on the existing managed config or stale-token migration case rather than only the new slot.
Useful? React with 👍 / 👎.
3e25839 to
5433bad
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5433badeb6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const result = await this.toolkit.logout( | ||
| providerName, | ||
| this.resolveRuntimeManagedAuth(providerName).oauthRef, | ||
| ); |
There was a problem hiding this comment.
Preserve config when logging out an env override
When KIMI_CODE_BASE_URL/KIMI_CODE_OAUTH_HOST are set temporarily over an existing managed config, resolveRuntimeManagedAuth() returns the env-scoped OAuth ref, so this logs out the override token, but KimiOAuthToolkit.logout() still unconditionally runs the config adapter's remove() for managed:kimi-code. A user who only meant to clear the temporary environment credential therefore loses the persisted provider/models for the configured environment while that configured token remains untouched; use the persisted config ref for config removal, or skip config removal when the runtime ref was produced by env overrides.
Useful? React with 👍 / 👎.
| const result = await this.toolkit.logout( | ||
| providerName, | ||
| this.resolveRuntimeManagedAuth(providerName).oauthRef, | ||
| ); |
There was a problem hiding this comment.
Delete stale legacy tokens during scoped logout
For an existing pre-scoping config whose base_url is non-default but whose persisted oauth.key is still oauth/kimi-code, this call first normalizes the ref to the new scoped slot before logout. In that migration case the actual legacy token file is left behind while the config is removed, so /logout appears to succeed but the old production credential can still be reused later; when repairing a stale ref for logout, also delete the original configured slot or pass it through before removing config.
Useful? React with 👍 / 👎.
| const auth = this.resolveManagedAuth(providerName); | ||
| const baseUrl = this.configBaseUrlForRuntime(auth.baseUrl); | ||
| return this.configOAuthRefForRuntime(oauthRef ?? auth.oauthRef, baseUrl); |
There was a problem hiding this comment.
Keep tool service endpoints aligned with env tokens
When KIMI_CODE_BASE_URL is set over a persisted managed config, this path normalizes every managed OAuth ref to the env-scoped token, including the moonshot_search/moonshot_fetch service refs passed from packages/agent-core/src/rpc/core-impl.ts where the service baseUrl still comes directly from config. In that environment-override session the web search/fetch tools therefore call the old configured /search and /fetch endpoints with the env environment's token; either update the service base URLs under the same override or avoid overriding service refs independently of their endpoints.
Useful? React with 👍 / 👎.
5433bad to
9ce662b
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9ce662b7bb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| applyManagedKimiCodeConfig(asManaged(config), { | ||
| models, | ||
| baseUrl: managedProvider.baseUrl, | ||
| baseUrl, |
There was a problem hiding this comment.
Avoid persisting temporary env endpoints on refresh
When KIMI_CODE_BASE_URL is set only to run against a temporary managed environment, baseUrl here comes from managedRuntimeBaseUrl() rather than the persisted provider. If the env environment returns a different model list, this path calls applyManagedKimiCodeConfig() and then setConfig(), permanently rewriting the user's provider (and generated service endpoints) to the env URL after the process exits. Use the env URL only for the fetch/token lookup, or avoid writing the managed provider config while an env override is active.
Useful? React with 👍 / 👎.
| const oauthHost = this.oauthHostFor(options.oauthRef, options.oauthHost); | ||
| const oauthKey = options.oauthRef?.key ?? this.defaultOAuthKey(options.baseUrl, oauthHost); | ||
| const manager = this.managerFor(name, oauthKey, oauthHost); |
There was a problem hiding this comment.
Derive toolkit login slots from env base URLs
When a caller uses KimiOAuthToolkit directly with KIMI_CODE_BASE_URL set and no explicit baseUrl, provisioning still fetches models from that env API via fetchManagedKimiCodeModels, but this computes oauthKey with options.baseUrl as undefined, so the token is written to the default production slot while the generated config points at the env base URL. A later runtime derives the scoped key from that persisted baseUrl and cannot find the token; include the env base URL in the default key/ref derivation or require the same resolved base URL to be passed through login.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 78f5c1adca
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const accessToken = await this.ensureFresh(name, { | ||
| oauthRef: options.oauthRef ?? this.defaultOAuthRef(options.baseUrl), | ||
| }); | ||
| const result = await fetchManagedUsage(managedUsageUrl(options.baseUrl), accessToken); |
There was a problem hiding this comment.
Derive usage tokens from the env base URL
When KIMI_CODE_BASE_URL is set and a direct KimiOAuthToolkit caller omits options.baseUrl, the request URL below still targets the env endpoint via managedUsageUrl(undefined), but this line derives the token ref with defaultOAuthRef(undefined), i.e. the default production credential slot. After this change, a login/provision call that used the env or explicit base URL writes a scoped token, so getManagedUsage() can hit the env /usages endpoint with the wrong cached token or report logged out; resolve the same env base URL before deriving the fallback ref (and apply the same pattern to feedback).
Useful? React with 👍 / 👎.
| return resolveKimiCodeRuntimeAuth({ | ||
| configuredBaseUrl: auth.baseUrl, | ||
| configuredOAuthRef: oauthRef ?? auth.oauthRef, | ||
| }).oauthRef; |
There was a problem hiding this comment.
Preserve explicit service OAuth refs
When a moonshot_search/moonshot_fetch service is explicitly configured with its own scoped oauth ref, core-impl.ts passes that service ref to this resolver, but this normalization recomputes it against the managed model provider's baseUrl rather than the service's endpoint. If the service points at a different managed environment than the model provider, the service ref is discarded and the tool calls that service with the provider's token; leave explicit refs unchanged unless they are the provider ref, or normalize them with the matching service base URL.
Useful? React with 👍 / 👎.
* fix: scope managed OAuth credentials * refactor: centralize managed OAuth auth resolution
Related Issue
No linked issue.
Problem
Running Kimi Code login with a non-default OAuth host or API base URL could leave managed Kimi Code config pointing at that environment while OAuth tokens still shared the default credential slot. A later shell could reuse the wrong token and show a misleading API key error when model provisioning returned unauthorized.
What changed
/models401/403 responses as OAuth unauthorized and retry via force-refresh/device login.Checklist
gen-changesetsskill, or this PR needs no changeset.gen-docsskill, or this PR needs no doc update.