Include originator in authentication URL parameters (#3117)

Associates the client with an authentication session.
This commit is contained in:
pakrym-oai
2025-09-03 16:51:00 -07:00
committed by GitHub
parent ed0d23d560
commit e83c5f429c
7 changed files with 44 additions and 18 deletions

View File

@@ -12,8 +12,8 @@ use codex_protocol::mcp_protocol::AuthMode;
use std::env; use std::env;
use std::path::PathBuf; use std::path::PathBuf;
pub async fn login_with_chatgpt(codex_home: PathBuf) -> std::io::Result<()> { pub async fn login_with_chatgpt(codex_home: PathBuf, originator: String) -> std::io::Result<()> {
let opts = ServerOptions::new(codex_home, CLIENT_ID.to_string()); let opts = ServerOptions::new(codex_home, CLIENT_ID.to_string(), originator);
let server = run_login_server(opts)?; let server = run_login_server(opts)?;
eprintln!( eprintln!(
@@ -27,7 +27,12 @@ pub async fn login_with_chatgpt(codex_home: PathBuf) -> std::io::Result<()> {
pub async fn run_login_with_chatgpt(cli_config_overrides: CliConfigOverrides) -> ! { pub async fn run_login_with_chatgpt(cli_config_overrides: CliConfigOverrides) -> ! {
let config = load_config_or_exit(cli_config_overrides); let config = load_config_or_exit(cli_config_overrides);
match login_with_chatgpt(config.codex_home).await { match login_with_chatgpt(
config.codex_home,
config.responses_originator_header.clone(),
)
.await
{
Ok(_) => { Ok(_) => {
eprintln!("Successfully logged in"); eprintln!("Successfully logged in");
std::process::exit(0); std::process::exit(0);

View File

@@ -30,10 +30,11 @@ pub struct ServerOptions {
pub port: u16, pub port: u16,
pub open_browser: bool, pub open_browser: bool,
pub force_state: Option<String>, pub force_state: Option<String>,
pub originator: String,
} }
impl ServerOptions { impl ServerOptions {
pub fn new(codex_home: PathBuf, client_id: String) -> Self { pub fn new(codex_home: PathBuf, client_id: String, originator: String) -> Self {
Self { Self {
codex_home, codex_home,
client_id: client_id.to_string(), client_id: client_id.to_string(),
@@ -41,6 +42,7 @@ impl ServerOptions {
port: DEFAULT_PORT, port: DEFAULT_PORT,
open_browser: true, open_browser: true,
force_state: None, force_state: None,
originator,
} }
} }
} }
@@ -96,7 +98,14 @@ pub fn run_login_server(opts: ServerOptions) -> io::Result<LoginServer> {
let server = Arc::new(server); let server = Arc::new(server);
let redirect_uri = format!("http://localhost:{actual_port}/auth/callback"); let redirect_uri = format!("http://localhost:{actual_port}/auth/callback");
let auth_url = build_authorize_url(&opts.issuer, &opts.client_id, &redirect_uri, &pkce, &state); let auth_url = build_authorize_url(
&opts.issuer,
&opts.client_id,
&redirect_uri,
&pkce,
&state,
&opts.originator,
);
if opts.open_browser { if opts.open_browser {
let _ = webbrowser::open(&auth_url); let _ = webbrowser::open(&auth_url);
@@ -279,6 +288,7 @@ fn build_authorize_url(
redirect_uri: &str, redirect_uri: &str,
pkce: &PkceCodes, pkce: &PkceCodes,
state: &str, state: &str,
originator: &str,
) -> String { ) -> String {
let query = vec![ let query = vec![
("response_type", "code"), ("response_type", "code"),
@@ -290,6 +300,7 @@ fn build_authorize_url(
("id_token_add_organizations", "true"), ("id_token_add_organizations", "true"),
("codex_cli_simplified_flow", "true"), ("codex_cli_simplified_flow", "true"),
("state", state), ("state", state),
("originator", originator),
]; ];
let qs = query let qs = query
.into_iter() .into_iter()

View File

@@ -100,6 +100,7 @@ async fn end_to_end_login_flow_persists_auth_json() {
port: 0, port: 0,
open_browser: false, open_browser: false,
force_state: Some(state), force_state: Some(state),
originator: "test_originator".to_string(),
}; };
let server = run_login_server(opts).unwrap(); let server = run_login_server(opts).unwrap();
let login_port = server.actual_port; let login_port = server.actual_port;
@@ -158,6 +159,7 @@ async fn creates_missing_codex_home_dir() {
port: 0, port: 0,
open_browser: false, open_browser: false,
force_state: Some(state), force_state: Some(state),
originator: "test_originator".to_string(),
}; };
let server = run_login_server(opts).unwrap(); let server = run_login_server(opts).unwrap();
let login_port = server.actual_port; let login_port = server.actual_port;

View File

@@ -160,7 +160,11 @@ impl CodexMessageProcessor {
let opts = LoginServerOptions { let opts = LoginServerOptions {
open_browser: false, open_browser: false,
..LoginServerOptions::new(config.codex_home.clone(), CLIENT_ID.to_string()) ..LoginServerOptions::new(
config.codex_home.clone(),
CLIENT_ID.to_string(),
config.responses_originator_header.clone(),
)
}; };
enum LoginChatGptReply { enum LoginChatGptReply {

View File

@@ -312,13 +312,11 @@ async fn run_ratatui_app(
if should_show_onboarding { if should_show_onboarding {
let directory_trust_decision = run_onboarding_app( let directory_trust_decision = run_onboarding_app(
OnboardingScreenArgs { OnboardingScreenArgs {
codex_home: config.codex_home.clone(),
cwd: config.cwd.clone(),
show_login_screen: should_show_login_screen(login_status, &config), show_login_screen: should_show_login_screen(login_status, &config),
show_trust_screen: should_show_trust_screen, show_trust_screen: should_show_trust_screen,
login_status, login_status,
preferred_auth_method: config.preferred_auth_method,
auth_manager: auth_manager.clone(), auth_manager: auth_manager.clone(),
config: config.clone(),
}, },
&mut tui, &mut tui,
) )

View File

@@ -2,6 +2,7 @@
use codex_core::AuthManager; use codex_core::AuthManager;
use codex_core::auth::CLIENT_ID; use codex_core::auth::CLIENT_ID;
use codex_core::config::Config;
use codex_login::ServerOptions; use codex_login::ServerOptions;
use codex_login::ShutdownHandle; use codex_login::ShutdownHandle;
use codex_login::run_login_server; use codex_login::run_login_server;
@@ -113,6 +114,7 @@ pub(crate) struct AuthModeWidget {
pub login_status: LoginStatus, pub login_status: LoginStatus,
pub preferred_auth_method: AuthMode, pub preferred_auth_method: AuthMode,
pub auth_manager: Arc<AuthManager>, pub auth_manager: Arc<AuthManager>,
pub config: Config,
} }
impl AuthModeWidget { impl AuthModeWidget {
@@ -314,7 +316,11 @@ impl AuthModeWidget {
} }
self.error = None; self.error = None;
let opts = ServerOptions::new(self.codex_home.clone(), CLIENT_ID.to_string()); let opts = ServerOptions::new(
self.codex_home.clone(),
CLIENT_ID.to_string(),
self.config.responses_originator_header.clone(),
);
match run_login_server(opts) { match run_login_server(opts) {
Ok(child) => { Ok(child) => {
let sign_in_state = self.sign_in_state.clone(); let sign_in_state = self.sign_in_state.clone();

View File

@@ -1,4 +1,5 @@
use codex_core::AuthManager; use codex_core::AuthManager;
use codex_core::config::Config;
use codex_core::git_info::get_git_repo_root; use codex_core::git_info::get_git_repo_root;
use crossterm::event::KeyCode; use crossterm::event::KeyCode;
use crossterm::event::KeyEvent; use crossterm::event::KeyEvent;
@@ -21,7 +22,6 @@ use crate::tui::FrameRequester;
use crate::tui::Tui; use crate::tui::Tui;
use crate::tui::TuiEvent; use crate::tui::TuiEvent;
use color_eyre::eyre::Result; use color_eyre::eyre::Result;
use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
use std::sync::RwLock; use std::sync::RwLock;
@@ -53,26 +53,25 @@ pub(crate) struct OnboardingScreen {
} }
pub(crate) struct OnboardingScreenArgs { pub(crate) struct OnboardingScreenArgs {
pub codex_home: PathBuf,
pub cwd: PathBuf,
pub show_trust_screen: bool, pub show_trust_screen: bool,
pub show_login_screen: bool, pub show_login_screen: bool,
pub login_status: LoginStatus, pub login_status: LoginStatus,
pub preferred_auth_method: AuthMode,
pub auth_manager: Arc<AuthManager>, pub auth_manager: Arc<AuthManager>,
pub config: Config,
} }
impl OnboardingScreen { impl OnboardingScreen {
pub(crate) fn new(tui: &mut Tui, args: OnboardingScreenArgs) -> Self { pub(crate) fn new(tui: &mut Tui, args: OnboardingScreenArgs) -> Self {
let OnboardingScreenArgs { let OnboardingScreenArgs {
codex_home,
cwd,
show_trust_screen, show_trust_screen,
show_login_screen, show_login_screen,
login_status, login_status,
preferred_auth_method,
auth_manager, auth_manager,
config,
} = args; } = args;
let preferred_auth_method = config.preferred_auth_method;
let cwd = config.cwd.clone();
let codex_home = config.codex_home.clone();
let mut steps: Vec<Step> = vec![Step::Welcome(WelcomeWidget { let mut steps: Vec<Step> = vec![Step::Welcome(WelcomeWidget {
is_logged_in: !matches!(login_status, LoginStatus::NotAuthenticated), is_logged_in: !matches!(login_status, LoginStatus::NotAuthenticated),
})]; })];
@@ -84,8 +83,9 @@ impl OnboardingScreen {
sign_in_state: Arc::new(RwLock::new(SignInState::PickMode)), sign_in_state: Arc::new(RwLock::new(SignInState::PickMode)),
codex_home: codex_home.clone(), codex_home: codex_home.clone(),
login_status, login_status,
preferred_auth_method,
auth_manager, auth_manager,
preferred_auth_method,
config,
})) }))
} }
let is_git_repo = get_git_repo_root(&cwd).is_some(); let is_git_repo = get_git_repo_root(&cwd).is_some();