Identify Agent Zero OAuth refresh requests
Cooper Gamble committed
May 29, 2026 at 12:06 UTC
808629942ac3818bf3c03725cf1deeec063e6298
2 files changed
+68
-1
plugins/_oauth/helpers/codex.py
+14
-1
@@ -500,7 +500,10 @@ def refresh_tokens(refresh_token: str) -> dict[str, str]:
500
cfg = codex_config()
501
response = requests.post(
502
cfg["token_url"],
503
- headers={"Content-Type": "application/json"},
503
+ headers={
504
+ "Content-Type": "application/json",
505
+ "User-Agent": resolve_agent_zero_user_agent(),
506
+ },
507
json={
508
"client_id": cfg["client_id"],
509
"grant_type": "refresh_token",
@@ -522,6 +525,16 @@ def refresh_tokens(refresh_token: str) -> dict[str, str]:
525
}
526
527
528
+def resolve_agent_zero_user_agent() -> str:
529
+ try:
530
+ from helpers import git
531
+
532
+ version = git.get_version()
533
+ except Exception:
534
+ version = "unknown"
535
+ return f"agent-zero/{version or 'unknown'}"
536
+
537
+
538
def should_refresh(access_token: str, last_refresh: str) -> bool:
539
if not access_token:
540
return True
tests/test_oauth_codex.py
+54
@@ -258,6 +258,60 @@ def test_token_error_message_prefers_description():
258
assert codex._token_error_message(FakeResponse()) == "refresh token was already used"
259
260
261
+def test_refresh_tokens_sends_agent_zero_user_agent(monkeypatch):
262
+ requests: list[dict] = []
263
+
264
+ class FakeResponse:
265
+ ok = True
266
+
267
+ @staticmethod
268
+ def json():
269
+ return {
270
+ "access_token": "access-1",
271
+ "refresh_token": "refresh-1",
272
+ }
273
+
274
+ def post(url, *, headers, json, timeout):
275
+ requests.append(
276
+ {
277
+ "url": url,
278
+ "headers": headers,
279
+ "json": json,
280
+ "timeout": timeout,
281
+ }
282
+ )
283
+ return FakeResponse()
284
+
285
+ monkeypatch.setattr(
286
+ codex,
287
+ "codex_config",
288
+ lambda: {"token_url": "https://auth.example/oauth/token", "client_id": "client"},
289
+ )
290
+ monkeypatch.setattr(codex, "resolve_agent_zero_user_agent", lambda: "agent-zero/v1.18")
291
+ monkeypatch.setattr(codex.requests, "post", post)
292
+
293
+ assert codex.refresh_tokens("refresh-0") == {
294
+ "id_token": "",
295
+ "access_token": "access-1",
296
+ "refresh_token": "refresh-1",
297
+ }
298
+ assert requests == [
299
+ {
300
+ "url": "https://auth.example/oauth/token",
301
+ "headers": {
302
+ "Content-Type": "application/json",
303
+ "User-Agent": "agent-zero/v1.18",
304
+ },
305
+ "json": {
306
+ "client_id": "client",
307
+ "grant_type": "refresh_token",
308
+ "refresh_token": "refresh-0",
309
+ },
310
+ "timeout": 30,
311
+ }
312
+ ]
313
+
314
+
315
def test_default_auth_file_ignores_codex_cli_credentials(tmp_path, monkeypatch):
316
shared_auth = tmp_path / ".codex" / "auth.json"
317
private_auth = tmp_path / "usr" / "plugins" / "_oauth" / "codex" / "auth.json"