First Version
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate a Google OAuth token for Gmail API.
|
||||
|
||||
This script runs a local OAuth flow that:
|
||||
1. Opens your browser automatically to the Google authorization page
|
||||
2. Handles the callback via a local HTTP server
|
||||
3. Saves the resulting token to ./secrets/gmail_creds.json
|
||||
|
||||
No manual copy-paste required.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
SCOPES = [
|
||||
"https://www.googleapis.com/auth/gmail.modify",
|
||||
"https://www.googleapis.com/auth/gmail.labels",
|
||||
]
|
||||
|
||||
_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
SECRET_PATH = os.path.join(_ROOT, "secrets", "gmail_creds.json")
|
||||
_OAUTH_CLIENT_PATH = os.path.join(_ROOT, "secrets", "google_oauth_client.json")
|
||||
|
||||
|
||||
def _load_oauth_client() -> tuple[str, str]:
|
||||
if not os.path.exists(_OAUTH_CLIENT_PATH):
|
||||
print(f"Missing OAuth client file: {_OAUTH_CLIENT_PATH}")
|
||||
print("Create it with: {\"client_id\": \"...\", \"client_secret\": \"...\"}")
|
||||
sys.exit(1)
|
||||
with open(_OAUTH_CLIENT_PATH) as f:
|
||||
data = json.load(f)
|
||||
return data["client_id"], data["client_secret"]
|
||||
|
||||
|
||||
def main() -> None:
|
||||
# Lazy-import so we can show helpful errors if not installed.
|
||||
try:
|
||||
from google.auth.transport.requests import Request
|
||||
from google.oauth2.credentials import Credentials
|
||||
from google_auth_oauthlib.flow import InstalledAppFlow
|
||||
except ImportError as e:
|
||||
print(f"Missing dependencies: {e}")
|
||||
print("Install with: pip3 install google-auth google-auth-oauthlib google-api-python-client")
|
||||
sys.exit(1)
|
||||
|
||||
creds = None
|
||||
|
||||
# Try to load existing credentials first, in case they have refresh token.
|
||||
if os.path.exists(SECRET_PATH):
|
||||
print(f"Existing credentials found at {SECRET_PATH}")
|
||||
try:
|
||||
creds = Credentials.from_authorized_user_file(SECRET_PATH, SCOPES)
|
||||
except Exception:
|
||||
creds = None
|
||||
|
||||
# If creds exist and are valid, we're good.
|
||||
if creds and creds.valid:
|
||||
print("Credentials are already valid!")
|
||||
return
|
||||
|
||||
# If creds exist but expired, try to refresh.
|
||||
if creds and creds.expired and creds.refresh_token:
|
||||
print("Token expired. Attempting refresh...")
|
||||
try:
|
||||
creds.refresh(Request())
|
||||
print("Token refreshed successfully!")
|
||||
except Exception as e:
|
||||
print(f"Refresh failed: {e}")
|
||||
creds = None
|
||||
|
||||
if not creds or not creds.valid:
|
||||
client_id, client_secret = _load_oauth_client()
|
||||
# Start OAuth flow using local server (opens browser automatically).
|
||||
flow = InstalledAppFlow.from_client_config(
|
||||
{
|
||||
"installed": {
|
||||
"client_id": client_id,
|
||||
"client_secret": client_secret,
|
||||
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
||||
"token_uri": "https://oauth2.googleapis.com/token",
|
||||
"redirect_uris": ["http://localhost"],
|
||||
}
|
||||
},
|
||||
SCOPES,
|
||||
)
|
||||
|
||||
print("\nOpening browser for Google authorization...")
|
||||
creds = flow.run_local_server(
|
||||
port=0, # pick a random available port
|
||||
open_browser=True,
|
||||
prompt="consent",
|
||||
access_type="offline",
|
||||
)
|
||||
|
||||
# Save credentials.
|
||||
os.makedirs(os.path.dirname(SECRET_PATH), exist_ok=True)
|
||||
with open(SECRET_PATH, "w") as f:
|
||||
f.write(creds.to_json())
|
||||
|
||||
print(f"\n✅ Gmail OAuth token saved to {SECRET_PATH}")
|
||||
print(f" Scopes: {creds.scopes}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user