diff --git a/nanobot/webui/settings_api.py b/nanobot/webui/settings_api.py index afce14527..fd109d92c 100644 --- a/nanobot/webui/settings_api.py +++ b/nanobot/webui/settings_api.py @@ -298,9 +298,12 @@ def _oauth_provider_status(spec: Any) -> dict[str, Any]: token_filename=OPENAI_CODEX_PROVIDER.token_filename, ).load() expires_at = getattr(token, "expires", None) if token else None + now_ms = int(time.time() * 1000) return { "configured": bool( - token and token.access and expires_at and expires_at > int(time.time() * 1000) + token + and token.access + and (getattr(token, "refresh", None) or (expires_at and expires_at > now_ms)) ), "account": getattr(token, "account_id", None) if token else None, "expires_at": expires_at, diff --git a/tests/webui/test_settings_api.py b/tests/webui/test_settings_api.py index eccdaa858..c34715ea6 100644 --- a/tests/webui/test_settings_api.py +++ b/tests/webui/test_settings_api.py @@ -806,6 +806,27 @@ def test_openai_codex_oauth_status_uses_available_token( assert status["account"] == "acct-codex" +def test_openai_codex_oauth_status_uses_refreshable_expired_token( + monkeypatch: pytest.MonkeyPatch, +) -> None: + token = type( + "Token", + (), + { + "access": "access-token", + "refresh": "refresh-token", + "expires": 1, + "account_id": "acct-codex", + }, + )() + monkeypatch.setattr("oauth_cli_kit.storage.FileTokenStorage.load", lambda _self: token) + + status = _oauth_provider_status(find_by_name("openai_codex")) + + assert status["configured"] is True + assert status["expires_at"] == 1 + + def test_openai_codex_oauth_status_rejects_unavailable_token( monkeypatch: pytest.MonkeyPatch, ) -> None: