A secure, full-stack cloud file storage application - similar to Google Drive - built as an internship project.
Author: Prerana Hippargi
| Layer | Technology |
|---|---|
| Frontend | React 18, Vite, React Router v6, Axios |
| Backend | Node.js, Express.js |
| Authentication | Google OAuth 2.0 |
| Storage | Google Cloud Storage (GCS) |
| Security | Helmet, express-rate-limit, HPP |
| Logging | Winston |
- π Google OAuth 2.0 login - secure sign-in with your Google account
- π€ Drag & drop file upload - with real-time progress bar
- π₯ Signed download URLs - secure, time-limited download links
- π File versioning - every upload creates a new version; view history and restore any version
- π Shareable links - generate expiring public links for any file
- π File management - delete files (all versions) with one click
- π File search - filter your files instantly
- π Toast notifications - real-time feedback on every action
| Feature | Implementation |
|---|---|
| Secure HTTP headers | Helmet.js (15+ headers) |
| Rate limiting | express-rate-limit (per route) |
| HTTP Parameter Pollution | hpp middleware |
| File type validation | MIME type whitelist |
| Filename sanitization | Strips path traversal & dangerous chars |
| IDOR protection | gcsPath ownership verified on every request |
| Session security | httpOnly + sameSite cookies, 32-char secret enforced |
| Input validation | UUID format checks on all params |
| Error safety | Stack traces never exposed to client in production |
| Route | Limit |
|---|---|
All /api/* |
100 requests / 15 min |
/auth/* |
10 requests / 15 min |
| File uploads | 20 uploads / hour |
| Share link creation | 30 links / hour |
| Public downloads | 50 downloads / hour |
clouddrive/
βββ backend/
β βββ .env.example
β βββ package.json
β βββ logs/ β auto-generated at runtime
β βββ src/
β βββ index.js β Express entry + all security wired in
β βββ config/
β β βββ gcs.js β Google Cloud Storage client
β β βββ oauth.js β Google OAuth2 setup
β β βββ logger.js β Winston logger
β βββ middleware/
β β βββ auth.js β session guard (requireAuth)
β β βββ security.js β rate limiters, helmet, MIME whitelist
β βββ routes/
β β βββ auth.js β /auth/login, /callback, /me, /logout
β β βββ files.js β /api/files (upload, download, versions)
β β βββ share.js β /api/share (create, list, revoke)
β βββ services/
β βββ fileService.js β all GCS operations + versioning
β βββ shareService.js β share token management
β
βββ frontend/
βββ index.html β Vite entry HTML (root level)
βββ vite.config.js β Vite config + proxy to backend
βββ package.json
βββ src/
βββ main.jsx β React entry point
βββ App.jsx β Router + auth guards
βββ index.css β Global design system
βββ context/
β βββ AuthContext.jsx β Global auth state
βββ hooks/
β βββ useToast.js β Toast notification system
βββ utils/
β βββ api.js β All Axios API calls
βββ pages/
β βββ LoginPage.jsx/css β Google OAuth login screen
β βββ Dashboard.jsx/css β Main file manager UI
β βββ SharePage.jsx/css β Public file download page
βββ components/
βββ Dropzone.jsx/css β Drag & drop upload
βββ FileCard.jsx/css β File row with actions
βββ VersionsModal.jsx β Version history + restore
βββ ShareModal.jsx β Generate & copy share links
- Node.js v18+
- A Google Cloud Platform account (free tier is sufficient)
- Go to console.cloud.google.com
- Create a new project and note your Project ID
- Enable these APIs:
- Cloud Storage API
- Google OAuth2 API
- Go to Cloud Storage β Buckets β Create
- Give it a globally unique name e.g.
clouddrive-yourname-2025 - Region:
us-central1 - Leave all other settings as default β Create
- Go to IAM & Admin β Service Accounts β Create Service Account
- Name:
clouddrive-saβ Create - Grant role: Storage Object Admin β Done
- Click the service account β Keys β Add Key β JSON
- Download and rename to
service-account-key.json - Place it inside your
backend/folder
- Go to APIs & Services β Credentials β Create Credentials β OAuth 2.0 Client ID
- Configure the OAuth consent screen first if prompted (External, fill in app name + email)
- Application type: Web application
- Authorized redirect URIs β add:
http://localhost:5000/auth/callback - Copy the Client ID and Client Secret
cd backend
cp .env.example .envFill in your .env:
PORT=5000
NODE_ENV=development
# Generate with: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
SESSION_SECRET=your-32-char-minimum-secret-here
GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=your-client-secret
GOOGLE_REDIRECT_URI=http://localhost:5000/auth/callback
GCP_PROJECT_ID=your-gcp-project-id
GCP_KEY_FILE=./service-account-key.json
GCS_BUCKET_NAME=clouddrive-yourname-2025
CLIENT_URL=http://localhost:3000
LOG_LEVEL=infoGenerate a secure SESSION_SECRET:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"Backend:
cd backend
npm installFrontend:
cd frontend
npm installOpen two terminals:
Terminal 1 β Backend:
cd backend
npm run devExpected output:
π CloudDrive backend running on http://localhost:5000
Terminal 2 β Frontend:
cd frontend
npm run devExpected output:
VITE v5.x ready in xxx ms
β Local: http://localhost:3000/
Open http://localhost:3000 in your browser and log in with Google!
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /auth/login |
No | Redirect to Google OAuth |
| GET | /auth/callback |
No | OAuth2 callback |
| GET | /auth/me |
No | Get current session user |
| POST | /auth/logout |
Yes | Destroy session |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /api/files |
Yes | List all files (latest versions) |
| POST | /api/files/upload |
Yes | Upload a new file |
| POST | /api/files/:id/version |
Yes | Upload new version of existing file |
| GET | /api/files/:id/versions |
Yes | Get full version history |
| GET | /api/files/:id/download |
Yes | Get signed download URL |
| POST | /api/files/:id/restore |
Yes | Restore an older version |
| DELETE | /api/files/:id |
Yes | Delete file and all versions |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /api/share/create |
Yes | Create a share token |
| GET | /api/share/mine |
Yes | List your share links |
| DELETE | /api/share/:token |
Yes | Revoke a share link |
| GET | /api/share/info/:token |
No | Public: get file metadata |
| GET | /api/share/download/:token |
No | Public: get download URL |
gs://your-bucket/
βββ users/{userId}/files/{fileId}/v1/filename.pdf
βββ users/{userId}/files/{fileId}/v2/filename.pdf β new version
βββ users/{userId}/files/{fileId}/v3/filename.pdf β restored version
βββ share-index.json β share token registry
| Error | Fix |
|---|---|
SESSION_SECRET must be at least 32 chars |
Generate one with the crypto command above |
CORS blocked for origin |
Ensure CLIENT_URL=http://localhost:3000 in .env |
Could not load default credentials |
Check GCP_KEY_FILE path points to your JSON key |
Access denied on GCS bucket |
Ensure service account has Storage Object Admin role |
redirect_uri_mismatch |
Add http://localhost:5000/auth/callback in GCP OAuth credentials |
JSX syntax extension not enabled |
Ensure you're using the updated vite.config.js |
| Port already in use | Change PORT=5001 in .env |
Backend:
cd backend
gcloud run deploy clouddrive-backend \
--source . \
--region us-central1 \
--allow-unauthenticated \
--set-env-vars="NODE_ENV=production,SESSION_SECRET=xxx,..."Frontend:
cd frontend
npm run build
# deploy the dist/ folder to Firebase Hosting or GCS static siteDon't forget to:
- Add your Cloud Run URL to the OAuth 2.0 authorized redirect URIs in GCP
- Update
CLIENT_URLin backend env vars to your frontend's production URL - Use Workload Identity instead of a service account key file in production
- Google OAuth 2.0 login/logout
- Drag & drop file upload with progress bar
- Signed URL file download
- File versioning β upload, view history, restore
- Shareable links with configurable expiry
- Share link revocation
- File deletion (all versions)
- File search
- Toast notifications
- Rate limiting on all endpoints
- Secure HTTP headers (Helmet)
- MIME type validation
- IDOR protection
- Winston request/error logging