@router.post("/logout") async def logout(response: Response): response.delete_cookie(settings.SESSION_COOKIE_NAME) response.delete_cookie(settings.CSRF_COOKIE_NAME) return {"ok": True} @router.get("/me") async def me(user=Depends(get_current_user)): return {"id": user.id, "email": user.email, "full_name": user.full_name, "is_admin": user.is_admin} # --------------------------------------------------------------------------- # Google OAuth 2.0 (authorization code flow with state verification) # --------------------------------------------------------------------------- async def google_start_impl(user): """Shared implementation: generate state, persist, return Google's authorization URL.""" client = gmail_service.resolve_client_config() state = gmail_service.generate_state() if _oauth_redis is not None: _oauth_redis.setex(f"oauthstate:{state}", 600, json.dumps({"user_id": user.id})) else: _pending_oauth_states[state] = {"user_id": user.id} auth_url = gmail_service.build_authorization_url(client, state) return {"authorization_url": auth_url} _pending_oauth_states: dict = {} @router.get("/google/start") async def google_start(request: Request, user=Depends(get_current_user)): return await google_start_impl(user) @router.get("/google/callback") async def google_callback(request: Request, code: str = "", state: str = "", error: str = "", db: AsyncSession = Depends(get_db)): """Handle Google's response: verify state, exchange code for tokens, store encrypted.""" from app.models import GmailAccount, OAuthClientConfig, OAuthToken if error: return HTMLResponse(f"