A config-based file uploader Node.js API that supports multiple cloud storage providers (AWS S3, Cloudflare R2) with SSOJet OpenID Connect authentication. This is a pure API project with no frontend dashboard - perfect for integration into existing applications.
- Features
- Architecture Overview
- Prerequisites
- Installation & Setup
- Authentication Flow
- API Usage
- Configuration
- Testing
- Troubleshooting
- References
- π SSOJet OpenID Connect Authentication - Secure SSO integration
- π Multi-Cloud Storage - AWS S3 and Cloudflare R2 support
- βοΈ Configuration-Based - Environment variable driven
- π€ File Upload with URLs - Get direct access URLs after upload
- π‘οΈ Security - Helmet.js, CORS, input validation
- π§ͺ Testing Suite - Jest tests and cURL examples
- π Pure API - No frontend dependencies, JSON responses only
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
β β β β β β
β Client App ββββββ File Uploader ββββββ Cloud Storage β
β β β API β β (S3/R2) β
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
β β β
β β β
β βββββββββββββββββββ β
βββββββββββββββββ SSOJet Auth βββββββββββββββ
β Provider β
βββββββββββββββββββ
1. Client β GET /auth/ssojet
β
2. Redirect β SSOJet OAuth Provider
β
3. User Authentication β SSOJet
β
4. Callback β POST /auth/callback
β
5. Session Created β Client receives JSON response
β
6. File Upload β POST /api/upload (with session cookie)
β
7. Cloud Storage β File uploaded to S3/R2
β
8. Response β JSON with file URL
- Node.js (v16.0.0 or higher)
- npm (comes with Node.js)
- Git (for cloning repository)
- SSOJet Account - Sign up at SSOJet
- Cloud Storage Account (choose one or both):
- AWS Account with S3 access
- Cloudflare Account with R2 access
- Log in to SSOJet Dashboard
- Navigate to Applications
- Create new application:
- Name:
File Uploader API
- Type:
Regular Web App
- Callback URL:
http://localhost:3000/auth/callback
- Name:
- Copy credentials:
- Client ID
- Client Secret
- Issuer URL (from Advanced > Endpoints)
π Reference: SSOJet Integration Guide
git clone https://github.com/abhimanyusinghtuta/nodejs-file-upload-ssojet-auth-s3-r2.git
cd nodejs-file-upload-ssojet-auth-s3-r2
npm install
cp .env.example .env
Edit .env
file with your credentials:
# Server Configuration
PORT=3000
NODE_ENV=development
# SSOJet Authentication
SSOJET_CLIENT_ID=your_ssojet_client_id
SSOJET_CLIENT_SECRET=your_ssojet_client_secret
SSOJET_AUTHORITY_URL=https://your-subdomain.auth.ssojet.com
SSOJET_REDIRECT_URI=http://localhost:3000/auth/callback
# Session Configuration
SESSION_SECRET=your_super_secret_session_key
AUTH_ENABLED=true
# Default Storage Provider
DEFAULT_STORAGE_PROVIDER=cloudflare-r2
# AWS S3 Configuration (if using S3)
AWS_ACCESS_KEY_ID=your_aws_access_key
AWS_SECRET_ACCESS_KEY=your_aws_secret_key
AWS_REGION=us-east-1
AWS_S3_BUCKET=your-s3-bucket-name
# Cloudflare R2 Configuration (if using R2)
CLOUDFLARE_R2_ACCOUNT_ID=your_cloudflare_account_id
CLOUDFLARE_R2_ACCESS_KEY_ID=your_r2_access_key
CLOUDFLARE_R2_SECRET_ACCESS_KEY=your_r2_secret_key
CLOUDFLARE_R2_BUCKET=your-r2-bucket-name
CLOUDFLARE_R2_PUBLIC_URL=https://your-public-domain.com
# Development
npm run dev
# Production
npm start
The server will start on http://localhost:3000
curl -X GET "http://localhost:3000/api/auth/status" --cookie-jar cookies.txt
curl -X GET "http://localhost:3000/login" --cookie cookies.txt
Open in browser: http://localhost:3000/auth/ssojet
- Browser redirects to SSOJet
- User completes authentication
- SSOJet redirects back with auth code
- Server exchanges code for tokens
- Session cookie is created
- JSON response returned with user info
Successful Response:
{
"success": true,
"message": "Authentication successful",
"data": {
"user": {
"id": "user_id_from_ssojet",
"email": "user@example.com",
"name": "User Name",
"loginTime": "2025-09-05T18:50:16.242Z"
},
"redirectTo": "/api/auth/user",
"sessionId": "session_id_for_reference"
},
"timestamp": "2025-09-05T18:50:16.243Z"
}
curl -X GET "http://localhost:3000/api/auth/user" --cookie cookies.txt
All file upload endpoints require authentication. Include session cookie in requests.
curl -X POST "http://localhost:3000/api/upload" \
--form "file=@/path/to/your/file.jpg" \
--form "provider=cloudflare-r2" \
--cookie cookies.txt
Parameters:
file
(required): File to uploadprovider
(optional):aws-s3
orcloudflare-r2
(defaults to env variable)
Success Response:
{
"success": true,
"message": "File uploaded successfully",
"data": {
"filename": "file.jpg",
"originalName": "file.jpg",
"size": 51234,
"mimetype": "image/jpeg",
"provider": "cloudflare-r2",
"url": "https://devcdn.pseo.one/uploads/1693934567890-file.jpg",
"key": "uploads/1693934567890-file.jpg"
},
"timestamp": "2025-09-05T18:50:16.243Z"
}
curl -X GET "http://localhost:3000/api/providers" --cookie cookies.txt
curl -X GET "http://localhost:3000/api/health"
curl -X GET "http://localhost:3000/"
AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
AWS_REGION=us-east-1
AWS_S3_BUCKET=my-upload-bucket
CLOUDFLARE_R2_ACCOUNT_ID=abc123def456
CLOUDFLARE_R2_ACCESS_KEY_ID=your_r2_access_key
CLOUDFLARE_R2_SECRET_ACCESS_KEY=your_r2_secret_key
CLOUDFLARE_R2_BUCKET=my-r2-bucket
CLOUDFLARE_R2_PUBLIC_URL=https://cdn.example.com
# Enable/disable authentication
AUTH_ENABLED=true
# SSOJet configuration
SSOJET_CLIENT_ID=cli_xxxxx.xxxxx.xxxxx
SSOJET_CLIENT_SECRET=sk_xxxxx.xxxxx
SSOJET_AUTHORITY_URL=https://your-subdomain.auth.ssojet.com
SSOJET_REDIRECT_URI=http://localhost:3000/auth/callback
# Session security
SESSION_SECRET=your-super-secret-session-key-here
# Run all tests
npm test
# Run tests in watch mode
npm run test:watch
# Run API integration tests
npm run test:api
# Test health endpoint
npm run test:health
# Test upload functionality
npm run test:upload
# Test provider listing
npm run test:providers
See comprehensive cURL examples in CURL_EXAMPLES.md
Issue: "Failed to obtain access token"
Solution: Verify SSOJet endpoints in OIDC discovery:
https://your-subdomain.auth.ssojet.com/.well-known/openid-configuration
Issue: Upload API works without authentication
Solution: Check AUTH_ENABLED=true in .env file
Check server logs for requireAuth middleware debug output
Issue: "Provider not configured"
Solution: Verify cloud storage credentials in .env
Check DEFAULT_STORAGE_PROVIDER setting
Issue: File upload fails with 413 error
Solution: File too large. Current limit is 50MB
Adjust limit in server.js if needed
Enable detailed logging:
NODE_ENV=development npm start
Check server console for detailed authentication and upload logs.
- SSOJet Integration Guide: docs.ssojet.com/integration-guide-llm.txt
- AWS S3 API: docs.aws.amazon.com/s3
- Cloudflare R2 API: developers.cloudflare.com/r2
- Express.js: Web framework
- Passport.js: Authentication middleware
- passport-openidconnect: OpenID Connect strategy
- @aws-sdk/client-s3: AWS S3 SDK v3
- Multer: File upload handling
- Helmet.js: Security headers
- OpenID Connect Spec: openid.net/connect
- OAuth 2.0 Spec: oauth.net/2
- Fork the repository
- Create feature branch (
git checkout -b feature/amazing-feature
) - Commit changes (
git commit -m 'Add amazing feature'
) - Push to branch (
git push origin feature/amazing-feature
) - Open Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
Built with β€οΈ using Node.js, Express, and SSOJet Authentication
### Cloudflare R2 Configuration
```env
# Cloudflare R2 Configuration
CLOUDFLARE_R2_ACCOUNT_ID=your_account_id
CLOUDFLARE_R2_ACCESS_KEY_ID=your_r2_access_key
CLOUDFLARE_R2_SECRET_ACCESS_KEY=your_r2_secret_key
CLOUDFLARE_R2_BUCKET=your-r2-bucket-name
CLOUDFLARE_R2_ENDPOINT=https://your-account-id.r2.cloudflarestorage.com
# Server Configuration
PORT=3000
NODE_ENV=development
# Default Storage Provider
DEFAULT_STORAGE_PROVIDER=aws-s3 # or 'cloudflare-r2'
# File Upload Configuration
MAX_FILE_SIZE=10485760 # 10MB in bytes
ALLOWED_FILE_TYPES=image/jpeg,image/png,image/gif,application/pdf,text/plain
POST /api/upload
Upload a file to the configured storage provider.
Request:
- Method:
POST
- Content-Type:
multipart/form-data
- Body: FormData with
file
field - Optional Query Parameters:
provider
: Override default storage provider (aws-s3
orcloudflare-r2
)
Response:
{
"success": true,
"message": "File uploaded successfully",
"data": {
"filename": "unique-filename.jpg",
"originalName": "original-filename.jpg",
"size": 1024576,
"mimeType": "image/jpeg",
"url": "https://bucket-name.s3.amazonaws.com/unique-filename.jpg",
"provider": "aws-s3",
"uploadedAt": "2023-12-07T10:30:00.000Z"
}
}
GET /api/health
Check API health status.
Response:
{
"status": "healthy",
"timestamp": "2023-12-07T10:30:00.000Z",
"version": "1.0.0"
}
# Upload file using default provider
curl -X POST \
-F "file=@/path/to/your/file.jpg" \
http://localhost:3000/api/upload
# Upload file using specific provider
curl -X POST \
-F "file=@/path/to/your/file.jpg" \
"http://localhost:3000/api/upload?provider=cloudflare-r2"
const formData = new FormData();
formData.append('file', fileInput.files[0]);
fetch('/api/upload', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
console.log('Upload successful:', data.data.url);
})
.catch(error => {
console.error('Upload failed:', error);
});
# Development mode (with auto-reload)
npm run dev
# Production mode
npm start
# Run all tests
npm test
# Run tests in watch mode
npm run test:watch
fileuploader/
βββ src/
β βββ config/
β β βββ database.js
β β βββ storage.js
β β βββ validation.js
β βββ controllers/
β β βββ uploadController.js
β βββ middleware/
β β βββ auth.js
β β βββ upload.js
β β βββ validation.js
β βββ providers/
β β βββ awsS3Provider.js
β β βββ cloudflareR2Provider.js
β β βββ storageProviderFactory.js
β βββ routes/
β β βββ upload.js
β βββ utils/
β β βββ fileUtils.js
β β βββ response.js
β βββ server.js
βββ tests/
β βββ integration/
β β βββ upload.test.js
β βββ unit/
β βββ providers.test.js
β βββ utils.test.js
βββ .env.example
βββ .gitignore
βββ package.json
βββ README.md
The API includes comprehensive error handling:
- 400 Bad Request: Invalid file type, missing file, or validation errors
- 413 Payload Too Large: File size exceeds the maximum allowed limit
- 500 Internal Server Error: Storage provider errors or server issues
- File type validation
- File size limits
- Secure headers with Helmet.js
- CORS configuration
- Input sanitization
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add some amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
For support, please open an issue in the GitHub repository or contact the maintainers.