refactor: unify external auth resolution#31421
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c1b77736c0
ℹ️ 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".
|
@codex review this |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 31e538d06e
ℹ️ 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".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f8db315033
ℹ️ 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".
|
@codex review this |
|
Codex Review: Didn't find any major issues. Nice work! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
85053fa to
ce1f9e4
Compare
| ))); | ||
| } | ||
|
|
||
| login_with_chatgpt_auth_tokens( |
There was a problem hiding this comment.
This was saving tokens to ephemeral storage directly and had normal reload pick them up instead of having external auth bridge be the source of truth.
| save_auth( | ||
| &self.codex_home, | ||
| &auth_dot_json, | ||
| AuthCredentialsStoreMode::Ephemeral, |
There was a problem hiding this comment.
Why is this hardcoded to ephemeral?
There was a problem hiding this comment.
Deleted, this was to support old reload path through ephemeral store.
There was a problem hiding this comment.
Actually, brought it back with a comment. There is come nasty synchronization that happens over statics that I don't want to unravel in this PR.
|
@codex review this |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b31594dc16
ℹ️ 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".
| remote_control_handle, | ||
| plugin_startup_tasks, | ||
| } = args; | ||
| auth_manager.set_external_auth(Arc::new(ExternalAuthRefreshBridge { |
There was a problem hiding this comment.
At this point in the MessageProcessor:new() constructor, auth_manager.set_external_auth() was called before a bunch of calls to auth_manager.clone(), giving out references of Arc<AuthManager> to other objects. This seemed like a potentially useful invariant.
But in the PR as written, now the set_external_auth() call does not happen until login_chatgpt_auth_tokens_response(), correct? Does that mean there is some time where a bunch of objects have references to Arc<AuthManager> before set_external_auth() has been called, and if so, is that OK?
There was a problem hiding this comment.
I guess my higher level question is: can we make external_auth: Arc<dyn ExternalAuth>> a required param to AuthManager::new() so it is no longer Option internally?
There was a problem hiding this comment.
Hmm, I guess the problem is that we start the app server even if the user is logged out and then ClientRequest::LoginAccount is a request type that it accepts, which is what ultimately sets external_auth?
There was a problem hiding this comment.
No, you can create AuthManager without ExternalAuth. Setting ExternalAuth is like logging in so having AuthManager without ExternalAuth is like having auth manager without it being logged in.
There was a problem hiding this comment.
Ah, I see, and then there are cases like let manager = AuthManager::external_bearer_only(auth_config); where external_auth is set to Some in the constructor...
| /// we can assume that the source already refreshed it. Otherwise, ask the | ||
| /// token authority to refresh. | ||
| pub async fn refresh_token(&self) -> Result<(), RefreshTokenError> { | ||
| let _refresh_guard = self.refresh_lock.acquire().await.map_err(|_| { |
There was a problem hiding this comment.
Ah, I see the semaphore is used here and the implementation of this method and refresh_token_from_authority() stay the same in this PR, but both docstrings are updated...
|
|
||
| /// Refreshes auth in response to a manager-driven refresh attempt. | ||
| /// Refreshes auth and makes the returned value current for future `resolve()` calls. | ||
| fn refresh(&self, context: ExternalAuthRefreshContext) -> ExternalAuthFuture<'_, CodexAuth>; |
There was a problem hiding this comment.
Maybe too complicated, but if this took &mut self instead of &self, then callers would be responsible for ensuring refresh() calls were serialized.
There was a problem hiding this comment.
I think it's too complicated, the AuthManager ownership is too distributed and it's always inside an Arc. I don't know how we'll ever get to the state where we can take mut self
| .await | ||
| } | ||
| CodexAuth::ApiKey(_) | ||
| | CodexAuth::ChatgptAuthTokens(_) |
There was a problem hiding this comment.
Confirming: nothing special for this case?
There was a problem hiding this comment.
Yep, we don't expect to manually refresh ChatgptAuthTokens
Summary
External auth had two paths: provider-command credentials were resolved through
ExternalAuth, while app-provided ChatGPT credentials were installed separately and only used the provider for refresh.ExternalAuthalso declared an auth mode independently from theCodexAuthvalue it returned, so the declaration and credentials could disagree.This change makes the provider-owned
CodexAuthauthoritative for initial resolution, credential kind, and refresh.ExternalAuth::auth_modeand require providers to return their current auth fromresolveAuthManager::set_external_authProvider-command auth still follows its configured cache interval. App-provided ChatGPT auth still asks the parent app once after a
401and retries the request once.Stacked on #31355.
Testing
just test -p codex-loginjust test -p codex-models-managercodex-app-servertests for external login/logout, unauthorized refresh, and workspace mismatch