Skip to content

refactor: unify external auth resolution#31421

Merged
pakrym-oai merged 10 commits into
mainfrom
pakrym/unify-external-auth-resolution
Jul 8, 2026
Merged

refactor: unify external auth resolution#31421
pakrym-oai merged 10 commits into
mainfrom
pakrym/unify-external-auth-resolution

Conversation

@pakrym-oai

@pakrym-oai pakrym-oai commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

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. ExternalAuth also declared an auth mode independently from the CodexAuth value it returned, so the declaration and credentials could disagree.

This change makes the provider-owned CodexAuth authoritative for initial resolution, credential kind, and refresh.

  • remove ExternalAuth::auth_mode and require providers to return their current auth from resolve
  • route external auth registration, resolution, and unauthorized refresh through AuthManager::set_external_auth
  • keep the last resolved credential with its provider so synchronous consumers and unauthorized recovery observe the credential's actual mode
  • make the app-server bridge own both initial and refreshed ChatGPT credentials, and detach it on logout
  • keep model listing free of auth-refresh side effects, including in offline mode

Provider-command auth still follows its configured cache interval. App-provided ChatGPT auth still asks the parent app once after a 401 and retries the request once.

Stacked on #31355.

Testing

  • just test -p codex-login
  • just test -p codex-models-manager
  • focused codex-app-server tests for external login/logout, unauthorized refresh, and workspace mismatch

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread codex-rs/models-manager/src/manager.rs Outdated
@pakrym-oai

Copy link
Copy Markdown
Collaborator Author

@codex review this

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread codex-rs/models-manager/src/manager.rs Outdated
Comment thread codex-rs/login/src/auth/manager.rs Outdated
Comment thread codex-rs/login/src/auth/manager.rs Outdated
@pakrym-oai

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread codex-rs/app-server/src/request_processors/account_processor.rs
Comment thread codex-rs/login/src/auth/manager.rs Outdated
@pakrym-oai

Copy link
Copy Markdown
Collaborator Author

@codex review this

@chatgpt-codex-connector

Copy link
Copy Markdown
Contributor

Codex Review: Didn't find any major issues. Nice work!

Reviewed commit: fb0e47f322

ℹ️ 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".

Base automatically changed from codex/external-auth-codex-auth-precursor to main July 7, 2026 16:47
@pakrym-oai pakrym-oai force-pushed the pakrym/unify-external-auth-resolution branch from 85053fa to ce1f9e4 Compare July 7, 2026 16:51
)));
}

login_with_chatgpt_auth_tokens(

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread codex-rs/login/src/auth/manager.rs Outdated
Comment thread codex-rs/login/src/auth/manager.rs Outdated
Comment thread codex-rs/login/src/auth/manager.rs Outdated
Comment thread codex-rs/login/src/auth/manager.rs Outdated
save_auth(
&self.codex_home,
&auth_dot_json,
AuthCredentialsStoreMode::Ephemeral,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this hardcoded to ephemeral?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deleted, this was to support old reload path through ephemeral store.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread codex-rs/app-server/src/external_auth.rs
Comment thread codex-rs/login/src/auth/manager.rs Outdated
@pakrym-oai

Copy link
Copy Markdown
Collaborator Author

@codex review this

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread codex-rs/app-server/src/external_auth.rs
Comment thread codex-rs/app-server/src/request_processors/account_processor.rs
@bolinfest bolinfest self-requested a review July 7, 2026 22:41
remote_control_handle,
plugin_startup_tasks,
} = args;
auth_manager.set_external_auth(Arc::new(ExternalAuthRefreshBridge {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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(|_| {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe too complicated, but if this took &mut self instead of &self, then callers would be responsible for ensuring refresh() calls were serialized.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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(_)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirming: nothing special for this case?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, we don't expect to manually refresh ChatgptAuthTokens

@pakrym-oai pakrym-oai merged commit e60af81 into main Jul 8, 2026
63 of 66 checks passed
@pakrym-oai pakrym-oai deleted the pakrym/unify-external-auth-resolution branch July 8, 2026 00:05
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 8, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants