-
Notifications
You must be signed in to change notification settings - Fork 0
Add automatic browser-based OAuth flow #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
46fa568
60889e4
aa048d7
39cf747
c5b10be
ceba656
8a1643a
a4b37f5
1e17b06
6919066
47404bd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -163,3 +163,6 @@ cython_debug/ | |
|
|
||
| # api-blaster OAuth credentials and tokens | ||
| .api-blaster/ | ||
|
|
||
| # claude local settings | ||
| .claude/ | ||
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,8 +1,20 @@ | ||||||||||||
| import json | ||||||||||||
| import pathlib | ||||||||||||
| import webbrowser | ||||||||||||
| import threading | ||||||||||||
| import queue | ||||||||||||
| import os | ||||||||||||
| from http.server import HTTPServer, BaseHTTPRequestHandler | ||||||||||||
| from typing import Dict, List, Optional | ||||||||||||
| from urllib.parse import urlparse, urlunparse | ||||||||||||
| from requests_oauthlib import OAuth2Session | ||||||||||||
|
|
||||||||||||
| # Allow OAuth2 over HTTP for localhost (development only) | ||||||||||||
| # This is safe because localhost traffic never leaves your machine | ||||||||||||
| os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1' | ||||||||||||
|
|
||||||||||||
|
|
||||||||||||
|
|
||||||||||||
|
|
||||||||||||
| class OAuthSession: | ||||||||||||
| """ | ||||||||||||
|
|
@@ -72,60 +84,242 @@ def _load_config(self) -> Dict: | |||||||||||
| def _prompt_for_config(self) -> Dict: | ||||||||||||
| """Prompt user for OAuth2 configuration and save it.""" | ||||||||||||
| print("No config saved to file yet, lets get set up!") | ||||||||||||
| print("\nFor the redirect URL, use http://localhost:8080/cb (recommended for OAuth)") | ||||||||||||
|
||||||||||||
| config = { | ||||||||||||
| "client_id": input("Enter your Client ID: "), | ||||||||||||
| "client_secret": input("Enter your Client Secret: "), | ||||||||||||
| "redirect_uri": input("Enter your Redirect URL (default: https://localhost:9090/cb): ") or "https://localhost:9090/cb" | ||||||||||||
| "redirect_uri": input("Enter your Redirect URL (default: http://localhost:8080/cb): ") or "http://localhost:8080/cb" | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| # Save config | ||||||||||||
| with open(self.conf_file_path, "w") as f: | ||||||||||||
| json.dump(config, f) | ||||||||||||
|
|
||||||||||||
| return config | ||||||||||||
|
|
||||||||||||
| def _load_existing_token(self) -> Optional[Dict]: | ||||||||||||
| """Load existing token from file if available and valid.""" | ||||||||||||
| try: | ||||||||||||
| with open(self.auth_file_path, "r") as f: | ||||||||||||
| token = json.load(f) | ||||||||||||
|
|
||||||||||||
| # Validate required fields | ||||||||||||
| required_fields = ["refresh_token", "expires_at"] | ||||||||||||
| for field in required_fields: | ||||||||||||
| if field not in token: | ||||||||||||
| raise KeyError(f"Missing required field: {field}") | ||||||||||||
|
|
||||||||||||
| return token | ||||||||||||
|
|
||||||||||||
| except (FileNotFoundError, json.JSONDecodeError, KeyError): | ||||||||||||
| return None | ||||||||||||
|
|
||||||||||||
| def _start_callback_server(self, host: str, port: int) -> tuple[Optional[HTTPServer], queue.Queue, threading.Event]: | ||||||||||||
| """Start a local server to capture OAuth callback. | ||||||||||||
|
|
||||||||||||
|
||||||||||||
| Args: | |
| host (str): Hostname or IP address for the local callback server to listen on (e.g., "localhost"). | |
| port (int): Port number for the local callback server (e.g., 8080). |
Copilot
AI
Oct 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If server_ready.wait() times out, the server thread may still be running. The thread is marked as daemon so it won't block process exit, but server.server_close() is called before the thread has confirmed startup. This could cause issues if the thread is still initializing. Consider adding a flag to signal the thread to stop or waiting briefly for the thread to complete.
Copilot
AI
Oct 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The time module should be imported at the top of the file with other imports rather than inline within a function. This follows Python conventions and improves code readability.
Copilot
AI
Oct 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The condition 'not server_ready.is_set()' is redundant here. If the server is None (line 224), server_ready.set() is never called, so it will never be set. The second condition will always be True when server is None. Consider simplifying to just 'if server is None:' for clarity.
| if server is None or not server_ready.is_set(): | |
| if server is None: |
Copilot
AI
Oct 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The timeout value of 120 seconds is a magic number. Consider defining it as a named constant (e.g., OAUTH_CALLBACK_TIMEOUT_SECONDS = 120) at the class or module level to improve maintainability and make it easier to adjust.
Copilot
AI
Oct 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logic for reconstructing the redirect URL could benefit from a comment explaining why we're combining components from both parsed_redirect and parsed_callback, particularly the fallback behavior for the path on line 304.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Setting OAUTHLIB_INSECURE_TRANSPORT globally affects all OAuth sessions in the process. Consider setting this only for localhost redirect URIs to prevent accidentally allowing insecure transport for production OAuth endpoints. You can check if the redirect_uri is localhost before setting this environment variable.