mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
[device-auth] When headless environment is detected, show device login flow instead. (#8756)
When headless environment is detected, show device login flow instead.
This commit is contained in:
1 parent
d3ff668f68
commit
24d6e0114f
9 files changed
+1012
-42
No files matched your search
@@ -14,6 +14,14 @@ const ANSI_BLUE: &str = "\x1b[94m";
|
||||
const ANSI_GRAY: &str = "\x1b[90m";
|
||||
const ANSI_RESET: &str = "\x1b[0m";
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DeviceCode {
|
||||
pub verification_url: String,
|
||||
pub user_code: String,
|
||||
device_auth_id: String,
|
||||
interval: u64,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct UserCodeResp {
|
||||
device_auth_id: String,
|
||||
@@ -73,7 +81,8 @@ async fn request_user_code(
|
||||
if !resp.status().is_success() {
|
||||
let status = resp.status();
|
||||
if status == StatusCode::NOT_FOUND {
|
||||
return Err(std::io::Error::other(
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::NotFound,
|
||||
"device code login is not enabled for this Codex server. Use the browser login or verify the server URL.",
|
||||
));
|
||||
}
|
||||
@@ -137,34 +146,45 @@ async fn poll_for_token(
|
||||
}
|
||||
}
|
||||
|
||||
fn print_device_code_prompt(code: &str, issuer_base_url: &str) {
|
||||
fn print_device_code_prompt(verification_url: &str, code: &str) {
|
||||
let version = env!("CARGO_PKG_VERSION");
|
||||
println!(
|
||||
"\nWelcome to Codex [v{ANSI_GRAY}{version}{ANSI_RESET}]\n{ANSI_GRAY}OpenAI's command-line coding agent{ANSI_RESET}\n\
|
||||
\nFollow these steps to sign in with ChatGPT using device code authorization:\n\
|
||||
\n1. Open this link in your browser and sign in to your account\n {ANSI_BLUE}{issuer_base_url}/codex/device{ANSI_RESET}\n\
|
||||
\n1. Open this link in your browser and sign in to your account\n {ANSI_BLUE}{verification_url}{ANSI_RESET}\n\
|
||||
\n2. Enter this one-time code {ANSI_GRAY}(expires in 15 minutes){ANSI_RESET}\n {ANSI_BLUE}{code}{ANSI_RESET}\n\
|
||||
\n{ANSI_GRAY}Device codes are a common phishing target. Never share this code.{ANSI_RESET}\n",
|
||||
version = env!("CARGO_PKG_VERSION"),
|
||||
code = code,
|
||||
issuer_base_url = issuer_base_url
|
||||
);
|
||||
}
|
||||
|
||||
/// Full device code login flow.
|
||||
pub async fn run_device_code_login(opts: ServerOptions) -> std::io::Result<()> {
|
||||
pub async fn request_device_code(opts: &ServerOptions) -> std::io::Result<DeviceCode> {
|
||||
let client = reqwest::Client::new();
|
||||
let issuer_base_url = opts.issuer.trim_end_matches('/');
|
||||
let api_base_url = format!("{issuer_base_url}/api/accounts");
|
||||
let base_url = opts.issuer.trim_end_matches('/');
|
||||
let api_base_url = format!("{base_url}/api/accounts");
|
||||
let uc = request_user_code(&client, &api_base_url, &opts.client_id).await?;
|
||||
|
||||
print_device_code_prompt(&uc.user_code, issuer_base_url);
|
||||
Ok(DeviceCode {
|
||||
verification_url: format!("{base_url}/codex/device"),
|
||||
user_code: uc.user_code,
|
||||
device_auth_id: uc.device_auth_id,
|
||||
interval: uc.interval,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn complete_device_code_login(
|
||||
opts: ServerOptions,
|
||||
device_code: DeviceCode,
|
||||
) -> std::io::Result<()> {
|
||||
let client = reqwest::Client::new();
|
||||
let base_url = opts.issuer.trim_end_matches('/');
|
||||
let api_base_url = format!("{base_url}/api/accounts");
|
||||
|
||||
let code_resp = poll_for_token(
|
||||
&client,
|
||||
&api_base_url,
|
||||
&uc.device_auth_id,
|
||||
&uc.user_code,
|
||||
uc.interval,
|
||||
&device_code.device_auth_id,
|
||||
&device_code.user_code,
|
||||
device_code.interval,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -172,10 +192,10 @@ pub async fn run_device_code_login(opts: ServerOptions) -> std::io::Result<()> {
|
||||
code_verifier: code_resp.code_verifier,
|
||||
code_challenge: code_resp.code_challenge,
|
||||
};
|
||||
let redirect_uri = format!("{issuer_base_url}/deviceauth/callback");
|
||||
let redirect_uri = format!("{base_url}/deviceauth/callback");
|
||||
|
||||
let tokens = crate::server::exchange_code_for_tokens(
|
||||
issuer_base_url,
|
||||
base_url,
|
||||
&opts.client_id,
|
||||
&redirect_uri,
|
||||
&pkce,
|
||||
@@ -201,3 +221,10 @@ pub async fn run_device_code_login(opts: ServerOptions) -> std::io::Result<()> {
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Full device code login flow.
|
||||
pub async fn run_device_code_login(opts: ServerOptions) -> std::io::Result<()> {
|
||||
let device_code = request_device_code(&opts).await?;
|
||||
print_device_code_prompt(&device_code.verification_url, &device_code.user_code);
|
||||
complete_device_code_login(opts, device_code).await
|
||||
}
|
||||
Reference in new issue
Block a user