Skip to content

Conversation

@3clyp50
Copy link
Owner

@3clyp50 3clyp50 commented Jan 16, 2026

Motivation

  • Expose a ChatGPT-style OpenAI sign-in in External Services so users can authenticate with their ChatGPT/OpenAI subscription and surface OAuth-based auth immediately under API Keys; persist tokens for use by the server and enable UI-driven connect/disconnect workflows.

Description

  • Add python/helpers/openai_auth.py implementing a PKCE OAuth flow, local callback server bound to http://localhost:1455/auth/callback, token exchange, basic JWT payload parsing to extract chatgpt_account_id, and token storage in tmp/openai_auth.json.
  • Add API handlers python/api/openai_auth_status.py and python/api/openai_auth_logout.py to report stored auth status and clear stored tokens respectively.
  • Wire a start route /auth/openai in run_ui.py that creates the authorization flow and launches the local callback server, and update the External Services UI by adding webui/components/settings/external/openai_auth.html plus a navigation entry in webui/components/settings/external/external-settings.html to show status, expiration, account id, a "Sign in with ChatGPT" button and a Disconnect control.

Testing

  • Ran pip install -r requirements.txt successfully and then pip install -r requirements2.txt which installed packages but emitted a dependency conflict warning where browser-use expects openai==1.99.2 while requirements2.txt provided openai==1.99.5.
  • Launched the UI with WEB_UI_HOST=0.0.0.0 python run_ui.py, which started the server and served the index page at http://localhost:5000; an embedding preload reported a Hugging Face connectivity warning but the server remained responsive.
  • Executed an automated browser script to navigate to Settings → External Services → OpenAI Auth and captured a screenshot (artifacts/openai-auth-settings.png) verifying the new UI was rendered.
  • Did not complete a full OAuth login exchange in CI because completing the OAuth flow requires interactive login and external network access, but the local callback server, pending-flow handling, token-exchange logic and file storage were exercised where possible locally.

Codex Task

@3clyp50
Copy link
Owner Author

3clyp50 commented Jan 16, 2026

@coderabbitai full review

@coderabbitai
Copy link

coderabbitai bot commented Jan 16, 2026

✅ Actions performed

Full review triggered.

Repository owner deleted a comment from coderabbitai bot Jan 16, 2026
@coderabbitai
Copy link

coderabbitai bot commented Jan 16, 2026

Summary by CodeRabbit

  • New Features
    • Added OpenAI OAuth authentication enabling users to securely sign in with their OpenAI account
    • Added authentication status tracking with token expiration visibility and account information display
    • Added ability to disconnect or logout from OpenAI
    • Added OpenAI Auth settings panel for managing authentication state

✏️ Tip: You can customize this high-level summary in your review settings.

Walkthrough

This pull request introduces OpenAI OAuth authentication functionality to the application. It implements a PKCE-based authorization flow with local token persistence and adds API handlers for authentication status checks and logout. The implementation includes a callback server to handle OAuth redirects, core authentication logic in the helpers module, API endpoints in the python/api directory, and corresponding UI components in the webui for authentication management and status display within the external settings section.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and specifically describes the main change: adding OpenAI OAuth sign-in with status endpoints and UI integration across the codebase.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, covering motivation, implementation details, testing performed, and key components added.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings


📜 Recent review details

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 42cc3fe and 7d447f3.

📒 Files selected for processing (6)
  • python/api/openai_auth_logout.py
  • python/api/openai_auth_status.py
  • python/helpers/openai_auth.py
  • run_ui.py
  • webui/components/settings/external/external-settings.html
  • webui/components/settings/external/openai_auth.html
🧰 Additional context used
🧬 Code graph analysis (2)
python/api/openai_auth_status.py (3)
python/helpers/api.py (1)
  • ApiHandler (17-99)
python/helpers/tool.py (1)
  • Response (11-14)
python/helpers/openai_auth.py (1)
  • get_auth_status (113-131)
python/api/openai_auth_logout.py (4)
python/helpers/api.py (1)
  • ApiHandler (17-99)
python/helpers/tool.py (1)
  • Response (11-14)
python/api/openai_auth_status.py (1)
  • process (6-7)
python/helpers/openai_auth.py (1)
  • clear_auth (107-110)
🪛 Ruff (0.14.11)
python/api/openai_auth_status.py

6-6: Unused method argument: input

(ARG002)


6-6: Unused method argument: request

(ARG002)

python/api/openai_auth_logout.py

6-6: Unused method argument: input

(ARG002)


6-6: Unused method argument: request

(ARG002)

python/helpers/openai_auth.py

21-21: Possible hardcoded password assigned to: "TOKEN_URL"

(S105)


67-67: Do not catch blind exception: Exception

(BLE001)


99-99: Do not catch blind exception: Exception

(BLE001)


299-299: Unused method argument: format

(ARG002)


299-299: Unused method argument: args

(ARG002)


299-299: Dynamically typed expressions (typing.Any) are disallowed in *args

(ANN401)

🔇 Additional comments (14)
webui/components/settings/external/external-settings.html (2)

18-23: LGTM — OpenAI Auth navigation entry matches the existing settings layout.


66-68: LGTM — section correctly mounts the OpenAI Auth component.

python/helpers/openai_auth.py (6)

47-55: PKCE helper generation looks solid.


79-99: Robust token-file validation.


113-131: Status derivation is clear and consistent.


134-151: Authorization URL assembly aligns with PKCE flow.


154-170: Thread-safe pending-flow access is good.


302-328: Callback handler flow is clean and user-friendly.

python/api/openai_auth_logout.py (1)

5-8: LGTM — logout handler clears auth and returns success.

python/api/openai_auth_status.py (1)

5-7: LGTM — status endpoint delegates cleanly to helper.

webui/components/settings/external/openai_auth.html (1)

10-53: LGTM — status refresh and disconnect flow is clean.

run_ui.py (3)

20-20: LGTM!

Import is correctly placed alongside the related login import.


294-294: No concerns.

Whitespace change only.


170-176: The callback server initialization already has proper error handling and thread safety protections.

The openai_auth module already includes:

  • Error handling in ensure_callback_server() (lines 348–353) with try/except for OSError
  • Thread-safe set_pending_flow() using _pending_lock (line 155)

However, there is a legitimate issue: if ensure_callback_server() fails (e.g., port 1455 already in use), the error is logged but the route still redirects to the OAuth provider. The user completes the OAuth flow, but OpenAI's callback to http://localhost:1455/auth/callback fails silently because the server never started. The route should verify the callback server is actually running before redirecting.

Additionally, create_authorization_flow() lacks error handling, though it only calls standard library functions with minimal failure risk.

Likely an incorrect or invalid review comment.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@3clyp50 3clyp50 added enhancement New feature or request and removed codex labels Jan 16, 2026
@3clyp50
Copy link
Owner Author

3clyp50 commented Jan 16, 2026

@coderabbitai full review

@coderabbitai
Copy link

coderabbitai bot commented Jan 16, 2026

✅ Actions performed

Full review triggered.

@coderabbitai
Copy link

coderabbitai bot commented Jan 16, 2026

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{"name":"HttpError","status":404,"request":{"method":"PATCH","url":"https://api.github.com/repos/3clyp50/agent-zero/issues/comments/3760084096","headers":{"accept":"application/vnd.github.v3+json","user-agent":"octokit.js/0.0.0-development octokit-core.js/7.0.6 Node.js/24","authorization":"token [REDACTED]","content-type":"application/json; charset=utf-8"},"body":{"body":"<!-- This is an auto-generated comment: summarize by coderabbit.ai -->\n<!-- walkthrough_start -->\n\n<!-- This is an auto-generated comment: release notes by coderabbit.ai -->\n\n## Summary by CodeRabbit\n\n* **New Features**\n  * Added OpenAI authentication integration with a new OpenAI Auth settings panel.\n  * Users can now sign in with their ChatGPT account, view connection status, account information, and token expiration.\n  * Added ability to disconnect from OpenAI account via settings.\n\n<sub>✏️ Tip: You can customize this high-level summary in your review settings.</sub>\n\n<!-- end of auto-generated comment: release notes by coderabbit.ai -->\n## Walkthrough\n\nThis pull request introduces OpenAI OAuth 2.0 PKCE-based authentication functionality. It adds a backend OAuth flow helper module with token persistence and management, two new API endpoint handlers for authentication status and logout, a web route to initiate the authorization flow, and a new HTML component with integrated UI for managing OpenAI authentication settings. The implementation includes HTTP callback server handling, token validation, expiration tracking, and account ID extraction from access tokens.\n\n<!-- walkthrough_end -->\n\n\n<!-- pre_merge_checks_walkthrough_start -->\n\n<details>\n<summary>🚥 Pre-merge checks | ✅ 2 | ❌ 1</summary>\n\n<details>\n<summary>❌ Failed checks (1 warning)</summary>\n\n|     Check name     | Status     | Explanation                                                                          | Resolution                                                                         |\n| :----------------: | :--------- | :----------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------- |\n| Docstring Coverage | ⚠️ Warning | Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. | Write docstrings for the functions missing them to satisfy the coverage threshold. |\n\n</details>\n<details>\n<summary>✅ Passed checks (2 passed)</summary>\n\n|     Check name    | Status   | Explanation                                                                                                                                                                 |\n| :---------------: | :------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n|    Title check    | ✅ Passed | The title accurately summarizes the main changes: adding OpenAI OAuth sign-in functionality with status endpoints and UI integration, which aligns with the core changeset. |\n| Description check | ✅ Passed | The description is comprehensive and directly related to the changeset, covering motivation, implementation details, and testing methodology.                               |\n\n</details>\n\n<sub>✏️ Tip: You can configure your own custom pre-merge checks in the settings.</sub>\n\n</details>\n\n<!-- pre_merge_checks_walkthrough_end -->\n\n<!-- finishing_touch_checkbox_start -->\n\n<details>\n<summary>✨ Finishing touches</summary>\n\n- [ ] <!-- {\"checkboxId\": \"7962f53c-55bc-4827-bfbf-6a18da830691\"} --> 📝 Generate docstrings\n\n</details>\n\n<!-- finishing_touch_checkbox_end -->\n\n<!-- tips_start -->\n\n---\n\nThanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=3clyp50/agent-zero&utm_content=18)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.\n\n<details>\n<summary>❤️ Share</summary>\n\n- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)\n- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)\n- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)\n- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)\n\n</details>\n\n<sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub>\n\n<!-- tips_end -->\n\n<!-- internal state start -->\n\n\n<!-- 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= -->\n\n<!-- internal state end -->"},"request":{"retryCount":1}},"response":{"url":"https://api.github.com/repos/3clyp50/agent-zero/issues/comments/3760084096","status":404,"headers":{"access-control-allow-origin":"*","access-control-expose-headers":"ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset","content-encoding":"gzip","content-security-policy":"default-src 'none'","content-type":"application/json; charset=utf-8","date":"Fri, 16 Jan 2026 13:57:41 GMT","referrer-policy":"origin-when-cross-origin, strict-origin-when-cross-origin","server":"github.com","strict-transport-security":"max-age=31536000; includeSubdomains; preload","transfer-encoding":"chunked","vary":"Accept-Encoding, Accept, X-Requested-With","x-accepted-github-permissions":"issues=write; pull_requests=write","x-content-type-options":"nosniff","x-frame-options":"deny","x-github-api-version-selected":"2022-11-28","x-github-media-type":"github.v3; format=json","x-github-request-id":"400B:C17EB:29C247:B7E29E:696A43D5","x-ratelimit-limit":"5000","x-ratelimit-remaining":"4946","x-ratelimit-reset":"1768574636","x-ratelimit-resource":"core","x-ratelimit-used":"54","x-xss-protection":"0"},"data":{"message":"Not Found","documentation_url":"https://docs.github.com/rest/issues/comments#update-an-issue-comment","status":"404"}}}

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In `@python/helpers/openai_auth.py`:
- Around line 41-45: Concurrent initializations of the callback HTTP server can
race and overwrite globals; add a dedicated lock (e.g., _callback_lock =
threading.Lock()) and wrap the initialization logic inside
ensure_callback_server() with this lock: re-check _callback_server after
acquiring the lock, create and bind the ThreadingHTTPServer instance locally
first, only assign to the global _callback_server and start/set _callback_thread
after successful bind/start, and use try/finally to release the lock; apply the
same guarded pattern to the other initialization block at the 343-358 region
that touches _callback_server and _callback_thread.
- Around line 103-111: The persisted OAuth tokens in AUTH_FILE are written with
default permissions; update the _save_auth_file function (and consider
clear_auth's behavior) to enforce restrictive file permissions (owner read/write
only) after creating the file: write the JSON as before using
files.write_file(AUTH_FILE, ...), then call os.chmod on
files.get_abs_path(AUTH_FILE) to set mode 0o600 (or create the file with os.open
and os.fdopen using os.O_CREAT|os.O_WRONLY with explicit mode 0o600), ensuring
the token file is owner-only readable/writable and not world-readable.
- Around line 58-68: The _decode_jwt_payload function currently catches all
Exceptions; narrow the except clause to only the decode/parse errors (e.g.,
binascii.Error from base64.urlsafe_b64decode, UnicodeDecodeError from .decode(),
and json.JSONDecodeError from json.loads) so unexpected bugs aren't
masked—import binascii if needed and replace "except Exception:" with "except
(binascii.Error, UnicodeDecodeError, json.JSONDecodeError): return None" while
preserving the same return behavior.
- Around line 173-199: In exchange_authorization_code, guard the httpx.post call
and the response.json() parse: wrap the httpx.post(...) in a try/except that
catches httpx.RequestError (log via PrintStyle.error with the exception and
return None) and then wrap payload = response.json() in a try/except catching
ValueError/JSONDecodeError (log the raw response.text and the parse error via
PrintStyle.error and return None); keep the existing status_code check and field
validations after successful parse.

In `@webui/components/settings/external/openai_auth.html`:
- Around line 90-100: The Token Expiration field is rendered when status is
'error' due to the condition using only !== 'disconnected' and !== 'loading';
update the template rendering logic (the <template> that checks the status
variable) to explicitly exclude the 'error' state or only render when status ===
'connected' (or the appropriate successful state), so expiresLabel is only shown
when the auth status is valid and not when an error occurred.
📜 Review details

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 42cc3fe and 7d447f3.

📒 Files selected for processing (6)
  • python/api/openai_auth_logout.py
  • python/api/openai_auth_status.py
  • python/helpers/openai_auth.py
  • run_ui.py
  • webui/components/settings/external/external-settings.html
  • webui/components/settings/external/openai_auth.html
🧰 Additional context used
🧬 Code graph analysis (4)
run_ui.py (2)
python/helpers/api.py (1)
  • requires_auth (31-32)
python/helpers/openai_auth.py (3)
  • create_authorization_flow (134-151)
  • set_pending_flow (154-157)
  • ensure_callback_server (343-358)
python/api/openai_auth_status.py (1)
python/helpers/tool.py (1)
  • Response (11-14)
python/helpers/openai_auth.py (2)
python/helpers/print_style.py (1)
  • PrintStyle (6-157)
python/helpers/files.py (3)
  • exists (486-488)
  • read_file (206-212)
  • write_file (357-362)
python/api/openai_auth_logout.py (4)
python/helpers/api.py (1)
  • ApiHandler (17-99)
python/helpers/tool.py (1)
  • Response (11-14)
python/api/openai_auth_status.py (1)
  • process (6-7)
python/helpers/openai_auth.py (1)
  • clear_auth (107-110)
🪛 Ruff (0.14.11)
python/api/openai_auth_status.py

6-6: Unused method argument: input

(ARG002)


6-6: Unused method argument: request

(ARG002)

python/helpers/openai_auth.py

21-21: Possible hardcoded password assigned to: "TOKEN_URL"

(S105)


67-67: Do not catch blind exception: Exception

(BLE001)


99-99: Do not catch blind exception: Exception

(BLE001)


299-299: Unused method argument: format

(ARG002)


299-299: Unused method argument: args

(ARG002)


299-299: Dynamically typed expressions (typing.Any) are disallowed in *args

(ANN401)

python/api/openai_auth_logout.py

6-6: Unused method argument: input

(ARG002)


6-6: Unused method argument: request

(ARG002)

🔇 Additional comments (8)
webui/components/settings/external/external-settings.html (1)

18-23: LGTM! Navigation and section structure follow existing patterns.

The new OpenAI Auth entry correctly integrates with the existing navigation framework. Minor observation: this reuses /public/auth.svg which is also used by the "Authentication" section (line 38). Consider using a distinct icon if visual differentiation is desired.

Also applies to: 66-68

python/api/openai_auth_logout.py (1)

5-8: LGTM! Simple delegation to helper.

The unused input and request parameters flagged by static analysis are required by the ApiHandler.process abstract method signature. This is a false positive.

python/api/openai_auth_status.py (1)

5-7: LGTM! Clean delegation to helper.

Same as the logout handler—the unused parameters are required by the abstract method signature. The implementation correctly delegates to get_auth_status() which handles all edge cases (missing file, expired tokens).

webui/components/settings/external/openai_auth.html (2)

15-32: LGTM! Status refresh logic is well-structured.

The error handling with try/catch and status state management is clean. The status determination logic correctly prioritizes connected over expired based on the backend's response structure.


110-115: Good security practice with noopener,noreferrer.

Opening the OAuth flow in a new tab with these attributes prevents reverse tabnabbing attacks.

python/helpers/openai_auth.py (3)

47-55: PKCE helper implementation looks solid.

Verifier/challenge generation and base64url encoding are clear and correct.


134-171: Flow state + PKCE wiring is clean and thread-safe.

Nice use of state, PKCE, and a lock around pending flow state.


298-329: Callback handler validates state and provides clear UX.

State checks + explicit success/error pages are well done.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +41 to +45
_pending_lock = threading.Lock()
_pending_flow: AuthorizationFlow | None = None
_callback_server: ThreadingHTTPServer | None = None
_callback_thread: threading.Thread | None = None

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Serialize callback server initialization to avoid races.

Concurrent calls to ensure_callback_server() can clobber _callback_server on a bind race. Guard the initialization with a lock and only set the global after successful bind.

🧵 Suggested fix
 _pending_lock = threading.Lock()
 _pending_flow: AuthorizationFlow | None = None
 _callback_server: ThreadingHTTPServer | None = None
 _callback_thread: threading.Thread | None = None
+_callback_lock = threading.Lock()
@@
 def ensure_callback_server() -> None:
     global _callback_server, _callback_thread
-    if _callback_server:
-        return
-
-    try:
-        _callback_server = ThreadingHTTPServer(("127.0.0.1", 1455), _CallbackHandler)
-    except OSError as exc:
-        PrintStyle.error(f"OpenAI OAuth callback server unavailable: {exc}")
-        _callback_server = None
-        return
-
-    _callback_thread = threading.Thread(
-        target=_callback_server.serve_forever, daemon=True
-    )
-    _callback_thread.start()
+    with _callback_lock:
+        if _callback_server:
+            return
+        try:
+            server = ThreadingHTTPServer(("127.0.0.1", 1455), _CallbackHandler)
+        except OSError as exc:
+            PrintStyle.error(f"OpenAI OAuth callback server unavailable: {exc}")
+            return
+        _callback_server = server
+        _callback_thread = threading.Thread(
+            target=_callback_server.serve_forever, daemon=True
+        )
+        _callback_thread.start()

Also applies to: 343-358

🤖 Prompt for AI Agents
In `@python/helpers/openai_auth.py` around lines 41 - 45, Concurrent
initializations of the callback HTTP server can race and overwrite globals; add
a dedicated lock (e.g., _callback_lock = threading.Lock()) and wrap the
initialization logic inside ensure_callback_server() with this lock: re-check
_callback_server after acquiring the lock, create and bind the
ThreadingHTTPServer instance locally first, only assign to the global
_callback_server and start/set _callback_thread after successful bind/start, and
use try/finally to release the lock; apply the same guarded pattern to the other
initialization block at the 343-358 region that touches _callback_server and
_callback_thread.

Comment on lines +58 to +68
def _decode_jwt_payload(token: str) -> dict[str, Any] | None:
try:
parts = token.split(".")
if len(parts) != 3:
return None
payload = parts[1]
payload += "=" * (-len(payload) % 4)
decoded = base64.urlsafe_b64decode(payload.encode()).decode("utf-8")
return json.loads(decoded)
except Exception:
return None
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Avoid blanket exception handling in JWT payload decoding.

Catching Exception can mask unexpected bugs. Narrow to decode/JSON errors to keep failures explicit.

♻️ Suggested refactor
+import binascii
-    except Exception:
+    except (binascii.Error, UnicodeDecodeError, ValueError):
         return None
🧰 Tools
🪛 Ruff (0.14.11)

67-67: Do not catch blind exception: Exception

(BLE001)

🤖 Prompt for AI Agents
In `@python/helpers/openai_auth.py` around lines 58 - 68, The _decode_jwt_payload
function currently catches all Exceptions; narrow the except clause to only the
decode/parse errors (e.g., binascii.Error from base64.urlsafe_b64decode,
UnicodeDecodeError from .decode(), and json.JSONDecodeError from json.loads) so
unexpected bugs aren't masked—import binascii if needed and replace "except
Exception:" with "except (binascii.Error, UnicodeDecodeError,
json.JSONDecodeError): return None" while preserving the same return behavior.

Comment on lines +103 to +111
def _save_auth_file(auth: OpenAIAuth) -> None:
files.write_file(AUTH_FILE, json.dumps(auth, indent=2))


def clear_auth() -> None:
abs_path = files.get_abs_path(AUTH_FILE)
if os.path.exists(abs_path):
os.remove(abs_path)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Restrict permissions on persisted OAuth tokens.

tmp/openai_auth.json contains access/refresh tokens but is written with default permissions. On shared machines this can expose tokens to other users.

🔐 Proposed hardening
 def _save_auth_file(auth: OpenAIAuth) -> None:
     files.write_file(AUTH_FILE, json.dumps(auth, indent=2))
+    try:
+        os.chmod(files.get_abs_path(AUTH_FILE), 0o600)
+    except OSError as exc:
+        PrintStyle.warning(f"Unable to restrict permissions on {AUTH_FILE}: {exc}")
🤖 Prompt for AI Agents
In `@python/helpers/openai_auth.py` around lines 103 - 111, The persisted OAuth
tokens in AUTH_FILE are written with default permissions; update the
_save_auth_file function (and consider clear_auth's behavior) to enforce
restrictive file permissions (owner read/write only) after creating the file:
write the JSON as before using files.write_file(AUTH_FILE, ...), then call
os.chmod on files.get_abs_path(AUTH_FILE) to set mode 0o600 (or create the file
with os.open and os.fdopen using os.O_CREAT|os.O_WRONLY with explicit mode
0o600), ensuring the token file is owner-only readable/writable and not
world-readable.

Comment on lines +173 to +199
def exchange_authorization_code(code: str, verifier: str) -> OpenAIAuth | None:
response = httpx.post(
TOKEN_URL,
headers={"Content-Type": "application/x-www-form-urlencoded"},
data={
"grant_type": "authorization_code",
"client_id": CLIENT_ID,
"code": code,
"code_verifier": verifier,
"redirect_uri": REDIRECT_URI,
},
timeout=15.0,
)
if response.status_code != HTTPStatus.OK:
PrintStyle.error(
f"OpenAI OAuth token exchange failed: {response.status_code} {response.text}"
)
return None

payload = response.json()
access_token = payload.get("access_token")
refresh_token = payload.get("refresh_token")
expires_in = payload.get("expires_in")
if not access_token or not refresh_token or not isinstance(expires_in, (int, float)):
PrintStyle.error("OpenAI OAuth token response missing fields.")
return None

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n python/helpers/openai_auth.py | head -210 | tail -50

Repository: 3clyp50/agent-zero

Length of output: 1947


🏁 Script executed:

head -50 python/helpers/openai_auth.py

Repository: 3clyp50/agent-zero

Length of output: 1220


🏁 Script executed:

web_search
httpx.RequestError and httpx.post exception handling documentation

Repository: 3clyp50/agent-zero

Length of output: 169


🌐 Web query:

httpx.post RequestError exception handling Python documentation

💡 Result:

Short answer — catch httpx.RequestError (or the broader httpx.HTTPError) around your httpx.post call. RequestError is the base class for network/transport errors and exposes the original .request; HTTPStatusError is raised by response.raise_for_status(). Example:

  • Catch transport/network errors only:

try:
resp = httpx.post("https://example.com", json=data)
except httpx.RequestError as exc:
print(f"Request failed for {exc.request.url!r}: {exc}")

  • Catch both request/transport errors and non-2xx responses:

try:
resp = httpx.post("https://example.com", json=data)
resp.raise_for_status()
except httpx.RequestError as exc:
print(f"Request error for {exc.request.url!r}: {exc}")
except httpx.HTTPStatusError as exc:
print(f"HTTP error {exc.response.status_code} for {exc.request.url!r}: {exc}")

Reference: httpx exceptions and quickstart (covers RequestError, HTTPError, and raise_for_status()). [1][2]

Sources
[1] HTTPX — Exceptions (api) documentation.
[2] HTTPX — Quickstart (Exceptions section).


🏁 Script executed:

rg -t py "httpx\." --context 3

Repository: 3clyp50/agent-zero

Length of output: 3905


Guard token exchange against network and JSON parsing failures.

httpx.post() can raise httpx.RequestError for network timeouts, connection errors, and other transport issues. response.json() can raise ValueError if the response body is not valid JSON. Both exceptions are currently unhandled and will break the OAuth callback flow.

Wrap the HTTP request and JSON parsing in explicit error handlers:

🛠️ Proposed fix
 def exchange_authorization_code(code: str, verifier: str) -> OpenAIAuth | None:
-    response = httpx.post(
-        TOKEN_URL,
-        headers={"Content-Type": "application/x-www-form-urlencoded"},
-        data={
-            "grant_type": "authorization_code",
-            "client_id": CLIENT_ID,
-            "code": code,
-            "code_verifier": verifier,
-            "redirect_uri": REDIRECT_URI,
-        },
-        timeout=15.0,
-    )
+    try:
+        response = httpx.post(
+            TOKEN_URL,
+            headers={"Content-Type": "application/x-www-form-urlencoded"},
+            data={
+                "grant_type": "authorization_code",
+                "client_id": CLIENT_ID,
+                "code": code,
+                "code_verifier": verifier,
+                "redirect_uri": REDIRECT_URI,
+            },
+            timeout=15.0,
+        )
+    except httpx.RequestError as exc:
+        PrintStyle.error(f"OpenAI OAuth token exchange failed: {exc}")
+        return None
     if response.status_code != HTTPStatus.OK:
         PrintStyle.error(
             f"OpenAI OAuth token exchange failed: {response.status_code} {response.text}"
         )
         return None
 
-    payload = response.json()
+    try:
+        payload = response.json()
+    except ValueError:
+        PrintStyle.error("OpenAI OAuth token response was not valid JSON.")
+        return None
🤖 Prompt for AI Agents
In `@python/helpers/openai_auth.py` around lines 173 - 199, In
exchange_authorization_code, guard the httpx.post call and the response.json()
parse: wrap the httpx.post(...) in a try/except that catches httpx.RequestError
(log via PrintStyle.error with the exception and return None) and then wrap
payload = response.json() in a try/except catching ValueError/JSONDecodeError
(log the raw response.text and the parse error via PrintStyle.error and return
None); keep the existing status_code check and field validations after
successful parse.

Comment on lines +90 to +100
<template x-if="status !== 'disconnected' && status !== 'loading'">
<div class="field">
<div class="field-label">
<div class="field-title">Token Expiration</div>
<div class="field-description">Refresh tokens are stored to keep the session active.</div>
</div>
<div class="field-control">
<input type="text" readonly :value="expiresLabel" />
</div>
</div>
</template>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Token expiration field visible during error state.

The condition status !== 'disconnected' && status !== 'loading' also includes status === 'error', which may show stale or placeholder expiration data when the status fetch fails.

Suggested fix
-          <template x-if="status !== 'disconnected' && status !== 'loading'">
+          <template x-if="status === 'connected' || status === 'expired'">
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<template x-if="status !== 'disconnected' && status !== 'loading'">
<div class="field">
<div class="field-label">
<div class="field-title">Token Expiration</div>
<div class="field-description">Refresh tokens are stored to keep the session active.</div>
</div>
<div class="field-control">
<input type="text" readonly :value="expiresLabel" />
</div>
</div>
</template>
<template x-if="status === 'connected' || status === 'expired'">
<div class="field">
<div class="field-label">
<div class="field-title">Token Expiration</div>
<div class="field-description">Refresh tokens are stored to keep the session active.</div>
</div>
<div class="field-control">
<input type="text" readonly :value="expiresLabel" />
</div>
</div>
</template>
🤖 Prompt for AI Agents
In `@webui/components/settings/external/openai_auth.html` around lines 90 - 100,
The Token Expiration field is rendered when status is 'error' due to the
condition using only !== 'disconnected' and !== 'loading'; update the template
rendering logic (the <template> that checks the status variable) to explicitly
exclude the 'error' state or only render when status === 'connected' (or the
appropriate successful state), so expiresLabel is only shown when the auth
status is valid and not when an error occurred.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant