codex_cli/auth/
current.rs1use anyhow::Result;
2use serde_json::json;
3use std::path::Path;
4
5use crate::auth;
6use crate::auth::output::{self, AuthCurrentResult};
7use crate::fs;
8use crate::paths;
9
10pub fn run() -> Result<i32> {
11 run_with_json(false)
12}
13
14pub fn run_with_json(output_json: bool) -> Result<i32> {
15 let auth_file = match paths::resolve_auth_file() {
16 Some(path) => path,
17 None => {
18 if output_json {
19 output::emit_error(
20 "auth current",
21 "auth-file-not-configured",
22 "CODEX_AUTH_FILE is not configured",
23 None,
24 )?;
25 }
26 return Ok(1);
27 }
28 };
29
30 if !auth_file.is_file() {
31 if output_json {
32 output::emit_error(
33 "auth current",
34 "auth-file-not-found",
35 format!("{} not found", auth_file.display()),
36 Some(json!({
37 "auth_file": auth_file.display().to_string(),
38 })),
39 )?;
40 } else {
41 eprintln!("codex: {} not found", auth_file.display());
42 }
43 return Ok(1);
44 }
45
46 let auth_key = auth::identity_key_from_auth_file(&auth_file).ok().flatten();
47 let auth_hash = match fs::sha256_file(&auth_file) {
48 Ok(hash) => hash,
49 Err(_) => {
50 eprintln!("codex: failed to hash {}", auth_file.display());
51 return Ok(1);
52 }
53 };
54
55 let secret_dir = paths::resolve_secret_dir();
56 let mut matched: Option<(String, MatchMode)> = None;
57
58 if let Some(secret_dir) = secret_dir
59 && let Ok(entries) = std::fs::read_dir(&secret_dir)
60 {
61 for entry in entries.flatten() {
62 let path = entry.path();
63 if path.extension().and_then(|s| s.to_str()) != Some("json") {
64 continue;
65 }
66
67 if let Some(key) = auth_key.as_deref()
68 && let Ok(Some(candidate_key)) = auth::identity_key_from_auth_file(&path)
69 && candidate_key == key
70 {
71 let candidate_hash = match fs::sha256_file(&path) {
72 Ok(hash) => hash,
73 Err(_) => {
74 if output_json {
75 output::emit_error(
76 "auth current",
77 "hash-failed",
78 format!("failed to hash {}", path.display()),
79 Some(json!({
80 "path": path.display().to_string(),
81 })),
82 )?;
83 } else {
84 eprintln!("codex: failed to hash {}", path.display());
85 }
86 return Ok(1);
87 }
88 };
89 let mode = if candidate_hash == auth_hash {
90 MatchMode::Exact
91 } else {
92 MatchMode::Identity
93 };
94 matched = Some((file_name(&path), mode));
95 break;
96 }
97
98 let candidate_hash = match fs::sha256_file(&path) {
99 Ok(hash) => hash,
100 Err(_) => {
101 if output_json {
102 output::emit_error(
103 "auth current",
104 "hash-failed",
105 format!("failed to hash {}", path.display()),
106 Some(json!({
107 "path": path.display().to_string(),
108 })),
109 )?;
110 } else {
111 eprintln!("codex: failed to hash {}", path.display());
112 }
113 return Ok(1);
114 }
115 };
116 if candidate_hash == auth_hash {
117 matched = Some((file_name(&path), MatchMode::Exact));
118 break;
119 }
120 }
121 }
122
123 if let Some((secret_name, mode)) = matched {
124 if output_json {
125 let match_mode = match mode {
126 MatchMode::Exact => "exact",
127 MatchMode::Identity => "identity",
128 };
129 output::emit_result(
130 "auth current",
131 AuthCurrentResult {
132 auth_file: auth_file.display().to_string(),
133 matched: true,
134 matched_secret: Some(secret_name),
135 match_mode: Some(match_mode.to_string()),
136 },
137 )?;
138 } else {
139 match mode {
140 MatchMode::Exact => {
141 println!("codex: {} matches {}", auth_file.display(), secret_name);
142 }
143 MatchMode::Identity => {
144 println!(
145 "codex: {} matches {} (identity; secret differs)",
146 auth_file.display(),
147 secret_name
148 );
149 }
150 }
151 }
152 return Ok(0);
153 }
154
155 if output_json {
156 output::emit_error(
157 "auth current",
158 "secret-not-matched",
159 format!("{} does not match any known secret", auth_file.display()),
160 Some(json!({
161 "auth_file": auth_file.display().to_string(),
162 "matched": false,
163 })),
164 )?;
165 } else {
166 println!(
167 "codex: {} does not match any known secret",
168 auth_file.display()
169 );
170 }
171 Ok(2)
172}
173
174#[derive(Copy, Clone)]
175enum MatchMode {
176 Exact,
177 Identity,
178}
179
180fn file_name(path: &Path) -> String {
181 path.file_name()
182 .and_then(|name| name.to_str())
183 .unwrap_or_default()
184 .to_string()
185}