auth.deliver changes from as:file to as:env: - Gmail: GMAIL_CREDS_JSON env var (Google authorized_user JSON) - Gcal: GCAL_CREDS_JSON env var mcp_config.env removed entirely — Skald injects the env var at runtime, no path on disk needed. Server scripts updated: - Check GMAIL_CREDS_JSON / GCAL_CREDS_JSON env var first - Use Credentials.from_authorized_user_info() instead of from_authorized_user_file() - Fall back to file-based loading for standalone/legacy use - _persist_creds only writes to disk when _creds_path is set gcal/verify.py: support GCAL_CREDS_JSON env var with shared _check_api() Docs (SKALD.md): updated examples, field table, connector table.
109 lines
3.3 KiB
Python
109 lines
3.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Verify Google Calendar OAuth credentials are valid.
|
|
|
|
Priority:
|
|
1. GCAL_CREDS_JSON env var — authorized_user JSON injected by Skald.
|
|
2. GOOGLE_CREDS_PATH env var → file on disk (standalone).
|
|
3. Default path ./secrets/google_creds.json (standalone).
|
|
|
|
Loads credentials and does one cheap API probe (calendarList.list(maxResults=1)).
|
|
|
|
Output: single JSON object on stdout:
|
|
{"ok": true, "message": "..."}
|
|
{"ok": false, "message": "..."}
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
import sys
|
|
|
|
|
|
def _check_api(creds) -> None:
|
|
"""Perform a live Calendar API probe with the given credentials."""
|
|
try:
|
|
from google.auth.transport.requests import Request
|
|
from googleapiclient.discovery import build
|
|
except ImportError as e:
|
|
_fail(f"Missing dependencies: {e}. Install google-api-python-client and google-auth.")
|
|
return
|
|
|
|
if creds.expired and creds.refresh_token:
|
|
try:
|
|
creds.refresh(Request())
|
|
except Exception as e:
|
|
_fail(f"Token refresh failed: {e}")
|
|
return
|
|
|
|
try:
|
|
service = build("calendar", "v3", credentials=creds)
|
|
result = service.calendarList().list(maxResults=1).execute()
|
|
items = result.get("items", [])
|
|
primary = next((c for c in items if c.get("primary")), None)
|
|
account = primary.get("id", "?") if primary else "?"
|
|
_ok(f"Google Calendar credentials valid. Account: {account}")
|
|
except Exception as e:
|
|
_fail(f"API probe failed: {e}")
|
|
|
|
|
|
def main() -> None:
|
|
# Priority 1: env var with full JSON (Skald mode)
|
|
raw = os.environ.get("GCAL_CREDS_JSON")
|
|
if raw:
|
|
try:
|
|
from google.oauth2.credentials import Credentials
|
|
creds = Credentials.from_authorized_user_info(
|
|
json.loads(raw),
|
|
["https://www.googleapis.com/auth/calendar"],
|
|
)
|
|
_check_api(creds)
|
|
return
|
|
except ImportError as e:
|
|
_fail(f"Missing dependencies: {e}. Install google-api-python-client and google-auth.")
|
|
return
|
|
except Exception as e:
|
|
_fail(f"Failed to load credentials from GCAL_CREDS_JSON: {e}")
|
|
return
|
|
|
|
# Priority 2/3: file-based (standalone use)
|
|
creds_path = os.environ.get(
|
|
"GOOGLE_CREDS_PATH",
|
|
os.path.join(os.path.dirname(__file__), "secrets", "google_creds.json"),
|
|
)
|
|
|
|
if not os.path.exists(creds_path):
|
|
_fail(f"Credentials not found. Set GCAL_CREDS_JSON env var (Skald mode) or "
|
|
f"place credentials file at {creds_path} (standalone mode).")
|
|
return
|
|
|
|
try:
|
|
from google.oauth2.credentials import Credentials
|
|
creds = Credentials.from_authorized_user_file(
|
|
creds_path,
|
|
["https://www.googleapis.com/auth/calendar"],
|
|
)
|
|
except ImportError as e:
|
|
_fail(f"Missing dependencies: {e}. Install google-api-python-client and google-auth.")
|
|
return
|
|
except Exception as e:
|
|
_fail(f"Failed to load credentials: {e}")
|
|
return
|
|
|
|
_check_api(creds)
|
|
|
|
|
|
def _ok(message: str) -> None:
|
|
json.dump({"ok": True, "message": message}, sys.stdout)
|
|
sys.stdout.write("\n")
|
|
|
|
|
|
def _fail(message: str) -> None:
|
|
json.dump({"ok": False, "message": message}, sys.stdout)
|
|
sys.stdout.write("\n")
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|