mirror of
https://github.com/hanzoai/dev.git
synced 2026-10-25 03:23:33 +00:00
fix(login): hanzo.id OAuth uses browser PKCE against /v1/iam (correct endpoints)
The device-code flow hit OpenAI-shaped endpoints (/api/accounts/deviceauth/*) that hanzo.id (Casdoor) does not serve. Fix: hanzo login now uses the browser PKCE flow with issuer https://hanzo.id/v1/iam, so authorize/token resolve to https://hanzo.id/v1/iam/oauth/{authorize,token} (verified vs hanzo.id OIDC discovery). Verified the emitted authorize URL is correct. Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
@@ -17,6 +17,7 @@ pub use login::run_login_status;
|
||||
pub use login::run_login_with_access_token;
|
||||
pub use login::run_login_with_api_key;
|
||||
pub use login::run_login_with_chatgpt;
|
||||
pub use login::run_login_with_hanzo;
|
||||
pub use login::run_login_with_device_code;
|
||||
pub use login::run_login_with_device_code_fallback_to_browser;
|
||||
pub use login::run_logout;
|
||||
|
||||
@@ -158,6 +158,57 @@ pub async fn login_with_chatgpt(
|
||||
server.block_until_done().await
|
||||
}
|
||||
|
||||
/// Browser PKCE login against Hanzo IAM (hanzo.id). Uses the same local
|
||||
/// callback server as ChatGPT login, but with the Hanzo client id + issuer so
|
||||
/// the authorize/token URLs resolve to https://hanzo.id/v1/iam/oauth/*.
|
||||
pub async fn login_with_hanzo(
|
||||
codex_home: std::path::PathBuf,
|
||||
cli_auth_credentials_store_mode: codex_login::AuthCredentialsStoreMode,
|
||||
auth_keyring_backend_kind: codex_login::AuthKeyringBackendKind,
|
||||
) -> std::io::Result<()> {
|
||||
clear_existing_auth_before_login(
|
||||
&codex_home,
|
||||
cli_auth_credentials_store_mode,
|
||||
auth_keyring_backend_kind,
|
||||
)
|
||||
.await;
|
||||
|
||||
let mut opts = ServerOptions::new(
|
||||
codex_home,
|
||||
codex_login::HANZO_CLIENT_ID.to_string(),
|
||||
None,
|
||||
cli_auth_credentials_store_mode,
|
||||
auth_keyring_backend_kind,
|
||||
);
|
||||
opts.issuer = codex_login::HANZO_ISSUER.to_string();
|
||||
let server = run_login_server(opts)?;
|
||||
print_login_server_start(server.actual_port, &server.auth_url);
|
||||
server.block_until_done().await
|
||||
}
|
||||
|
||||
pub async fn run_login_with_hanzo(cli_config_overrides: CliConfigOverrides) -> ! {
|
||||
let config = load_config_or_exit(cli_config_overrides).await;
|
||||
let _login_log_guard = init_login_file_logging(&config);
|
||||
tracing::info!("starting Hanzo IAM (hanzo.id) browser login flow");
|
||||
|
||||
match login_with_hanzo(
|
||||
config.codex_home.to_path_buf(),
|
||||
config.cli_auth_credentials_store_mode,
|
||||
config.auth_keyring_backend_kind(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(_) => {
|
||||
eprintln!("{LOGIN_SUCCESS_MESSAGE} (Hanzo)");
|
||||
std::process::exit(0);
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Error logging in with Hanzo: {e}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn run_login_with_chatgpt(cli_config_overrides: CliConfigOverrides) -> ! {
|
||||
let config = load_config_or_exit(cli_config_overrides).await;
|
||||
let _login_log_guard = init_login_file_logging(&config);
|
||||
|
||||
@@ -16,6 +16,7 @@ use codex_cli::run_login_status;
|
||||
use codex_cli::run_login_with_access_token;
|
||||
use codex_cli::run_login_with_api_key;
|
||||
use codex_cli::run_login_with_chatgpt;
|
||||
use codex_cli::run_login_with_hanzo;
|
||||
use codex_cli::run_login_with_device_code;
|
||||
use codex_cli::run_logout;
|
||||
use codex_cloud_tasks::Cli as CloudTasksCli;
|
||||
@@ -1408,18 +1409,10 @@ async fn cli_main(
|
||||
}
|
||||
}
|
||||
}
|
||||
// Hanzo IAM (hanzo.id) is the default.
|
||||
// Hanzo IAM (hanzo.id) is the default — browser PKCE
|
||||
// against https://hanzo.id/v1/iam/oauth/*.
|
||||
Some(LoginProvider::Hanzo) | None => {
|
||||
run_login_with_device_code(
|
||||
login_cli.config_overrides,
|
||||
login_cli
|
||||
.issuer_base_url
|
||||
.or_else(|| Some(codex_login::HANZO_ISSUER.to_string())),
|
||||
login_cli
|
||||
.client_id
|
||||
.or_else(|| Some(codex_login::HANZO_CLIENT_ID.to_string())),
|
||||
)
|
||||
.await;
|
||||
run_login_with_hanzo(login_cli.config_overrides).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1145,7 +1145,11 @@ pub const CLIENT_ID: &str = "app_EMoamEEZ73f0CkXaXp7hrann";
|
||||
|
||||
// Hanzo IAM (hanzo.id Casdoor OAuth — public client, PKCE only)
|
||||
pub const HANZO_CLIENT_ID: &str = "hanzo-dev";
|
||||
pub const HANZO_ISSUER: &str = "https://hanzo.id";
|
||||
// OAuth issuer base. hanzo.id (Casdoor) serves OIDC under /v1/iam, so the login
|
||||
// server's `{issuer}/oauth/authorize` + `{issuer}/oauth/token` resolve to
|
||||
// https://hanzo.id/v1/iam/oauth/authorize and .../oauth/token (verified against
|
||||
// hanzo.id/.well-known/openid-configuration).
|
||||
pub const HANZO_ISSUER: &str = "https://hanzo.id/v1/iam";
|
||||
|
||||
// Claude Code OAuth credentials (for Anthropic OAuth)
|
||||
pub const CLAUDE_CLIENT_ID: &str = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
|
||||
|
||||
Reference in New Issue
Block a user