Spaces:
Sleeping
Sleeping
| """ | |
| Google OAuth2 token helper. | |
| Exchanges the stored refresh token for a short-lived access token. | |
| Access tokens expire after 1 hour — this helper caches and auto-refreshes. | |
| """ | |
| import time | |
| import httpx | |
| from typing import Optional | |
| from app.config import settings | |
| from app.utils.logging import get_logger | |
| logger = get_logger("google_auth") | |
| TOKEN_URL = "https://oauth2.googleapis.com/token" | |
| # Simple in-process cache: (access_token, expires_at) | |
| _cache: tuple[Optional[str], float] = (None, 0.0) | |
| async def get_access_token() -> Optional[str]: | |
| """ | |
| Return a valid Google OAuth2 access token. | |
| Refreshes automatically when expired (or within 60s of expiry). | |
| Returns None if credentials are not configured. | |
| """ | |
| global _cache | |
| # Check required config | |
| if not all([ | |
| settings.google_client_id, | |
| settings.google_client_secret, | |
| settings.gmail_refresh_token, | |
| ]): | |
| logger.warning( | |
| "Google OAuth not configured — set GOOGLE_CLIENT_ID, " | |
| "GOOGLE_CLIENT_SECRET, GMAIL_REFRESH_TOKEN" | |
| ) | |
| return None | |
| access_token, expires_at = _cache | |
| # Return cached token if still valid (with 60s buffer) | |
| if access_token and time.time() < expires_at - 60: | |
| return access_token | |
| # Refresh | |
| try: | |
| async with httpx.AsyncClient() as client: | |
| response = await client.post( | |
| TOKEN_URL, | |
| data={ | |
| "grant_type": "refresh_token", | |
| "refresh_token": settings.gmail_refresh_token, | |
| "client_id": settings.google_client_id, | |
| "client_secret": settings.google_client_secret, | |
| }, | |
| timeout=10.0, | |
| ) | |
| response.raise_for_status() | |
| data = response.json() | |
| access_token = data["access_token"] | |
| expires_in = data.get("expires_in", 3600) | |
| _cache = (access_token, time.time() + expires_in) | |
| logger.debug("Google access token refreshed") | |
| return access_token | |
| except Exception as e: | |
| logger.error(f"Failed to refresh Google access token: {e}") | |
| return None | |