101:async def google_start_impl(user, client: dict = None):
102-    """Shared implementation: generate state, persist, return Google's authorization URL.
103-    `client` may be pre-resolved (e.g. the user's imported OAuthClientConfig row);
104-    when omitted the server-level configuration is used (honest 400 if absent)."""
105-    if client is None:
106-        try:
107-            client = gmail_service.resolve_client_config()
108-        except gmail_service.GmailOauthError as e:
109-            # Be honest: no client config -> no authorization URL (never fabricate one)
110-            raise HTTPException(400, f"Google OAuth is not configured: {e}")
111-    state = gmail_service.generate_state()
112-    if _oauth_redis is not None:
113-        _oauth_redis.setex(f"oauthstate:{state}", 600, json.dumps({"user_id": user.id}))
114-    else:
115-        _pending_oauth_states[state] = {"user_id": user.id}
116-    auth_url = gmail_service.build_authorization_url(client, state)
117-    return {"authorization_url": auth_url}
118-
119-
120-_pending_oauth_states: dict = {}
121-
122-
123-@router.get("/google/start")
124-async def google_start(request: Request, user=Depends(get_current_user)):
125-    return await google_start_impl(user)
126-
127-
128-@router.get("/google/callback")
129-async def google_callback(request: Request, code: str = "", state: str = "", error: str = "",
130-                          db: AsyncSession = Depends(get_db)):
131-    """Handle Google's response: verify state, exchange code for tokens, store encrypted."""
132-    from app.models import GmailAccount, OAuthClientConfig, OAuthToken
133-
134-    if error:
135-        return HTMLResponse(f"<h3>Google authorization failed: {error}</h3>"
136-                            f"<p><a href='{settings.FRONTEND_BASE_URL}/gmail-accounts.html'>Back</a></p>")
137-    if not code or not state:
138-        raise HTTPException(400, "Missing code or state")
139-
140-    # 1) verify state (CSRF protection of OAuth handshake)
141-    user_id = None
===get_current_user_csrf===
app/api/deps.py:74:async def get_current_user_csrf(user: "User" = Depends(get_current_user),
app/api/deps.py-75-                                _: None = Depends(require_csrf)) -> "User":
app/api/deps.py-76-    """Combined dependency: authenticate first (401 for anonymous), then CSRF (403).
app/api/deps.py-77-
app/api/deps.py-78-    Used by state-changing endpoints in routers where CSRF must be enforced but
app/api/deps.py-79-    anonymous callers should still receive the semantically-correct 401.
app/api/deps.py-80-    """
app/api/deps.py-81-    return user
app/api/deps.py-82-
app/api/deps.py-83-
app/api/deps.py-84-def rate_limit_user(request: Request, user=Depends(get_current_user)):
app/api/deps.py-85-    key = f"u:{user.id}:{request.url.path.split('?')[0]}"
app/api/deps.py-86-    allowed, remaining = rate_limit(key)
app/api/deps.py-87-    if not allowed:
app/api/deps.py-88-        raise HTTPException(status_code=429, detail="Rate limit exceeded; try again shortly")
app/api/deps.py-89-    return True
