feat: add Gemini auth check to token warning banner
/auth/status now returns per-backend status: Claude warns on <24h expiry, Gemini warns only when oauth_creds.json is missing or has no refresh_token (access token rotates automatically so expiry_date is not a useful signal). Banner shows warnings for both backends when needed, and the hint text names the specific CLI commands to run. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,12 @@
|
||||
"""
|
||||
Claude CLI OAuth token status.
|
||||
CLI auth status for both Claude and Gemini backends.
|
||||
|
||||
GET /auth/status — returns expiry info; warns when < WARN_HOURS remain
|
||||
GET /auth/status — returns per-backend auth info and warning flags
|
||||
|
||||
Claude: warns when OAuth token is < WARN_HOURS from expiry (requires
|
||||
user to re-run `claude` to refresh via browser flow).
|
||||
Gemini: warns only when oauth_creds.json is missing or has no
|
||||
refresh_token (access token rotates automatically every ~1h).
|
||||
"""
|
||||
import json
|
||||
import logging
|
||||
@@ -12,17 +17,17 @@ from fastapi import APIRouter
|
||||
logger = logging.getLogger(__name__)
|
||||
router = APIRouter(prefix="/auth")
|
||||
|
||||
CREDENTIALS_PATH = Path.home() / ".claude" / ".credentials.json"
|
||||
WARN_HOURS = 24 # show warning banner when fewer than this many hours remain
|
||||
CLAUDE_CREDS = Path.home() / ".claude" / ".credentials.json"
|
||||
GEMINI_CREDS = Path.home() / ".gemini" / "oauth_creds.json"
|
||||
GEMINI_ACCTS = Path.home() / ".gemini" / "google_accounts.json"
|
||||
WARN_HOURS = 24
|
||||
|
||||
|
||||
@router.get("/status")
|
||||
async def auth_status() -> dict:
|
||||
def _claude_status() -> dict:
|
||||
try:
|
||||
data = json.loads(CREDENTIALS_PATH.read_text())
|
||||
data = json.loads(CLAUDE_CREDS.read_text())
|
||||
oauth = data["claudeAiOauth"]
|
||||
expires_at_ms = oauth["expiresAt"]
|
||||
expires_dt = datetime.fromtimestamp(expires_at_ms / 1000, tz=timezone.utc)
|
||||
expires_dt = datetime.fromtimestamp(oauth["expiresAt"] / 1000, tz=timezone.utc)
|
||||
now = datetime.now(tz=timezone.utc)
|
||||
hours_remaining = (expires_dt - now).total_seconds() / 3600
|
||||
return {
|
||||
@@ -33,5 +38,32 @@ async def auth_status() -> dict:
|
||||
"expired": hours_remaining <= 0,
|
||||
}
|
||||
except Exception as e:
|
||||
logger.warning("auth status check failed: %s", e)
|
||||
logger.warning("claude auth check failed: %s", e)
|
||||
return {"ok": False, "error": str(e), "warning": True, "expired": False}
|
||||
|
||||
|
||||
def _gemini_status() -> dict:
|
||||
try:
|
||||
creds = json.loads(GEMINI_CREDS.read_text())
|
||||
if not creds.get("refresh_token"):
|
||||
return {"ok": True, "authenticated": False, "warning": True, "account": None}
|
||||
account = None
|
||||
try:
|
||||
accts = json.loads(GEMINI_ACCTS.read_text())
|
||||
account = accts.get("active")
|
||||
except Exception:
|
||||
pass
|
||||
return {"ok": True, "authenticated": True, "warning": False, "account": account}
|
||||
except FileNotFoundError:
|
||||
return {"ok": True, "authenticated": False, "warning": True, "account": None}
|
||||
except Exception as e:
|
||||
logger.warning("gemini auth check failed: %s", e)
|
||||
return {"ok": False, "error": str(e), "warning": True, "authenticated": False}
|
||||
|
||||
|
||||
@router.get("/status")
|
||||
async def auth_status() -> dict:
|
||||
return {
|
||||
"claude": _claude_status(),
|
||||
"gemini": _gemini_status(),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user