tillu-daemon / app /utils /google_auth.py
tillu-AI's picture
upload app/utils/google_auth.py
8faac22 verified
"""
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