Advanced
Authentication
Secure your uploads with authentication
Authentication
BucketKit doesn't include built-in authentication, but it's designed to integrate seamlessly with your existing auth system.
Backend Authentication
Express.js with JWT
import express from 'express';
import jwt from 'jsonwebtoken';
import { createBucketKit } from '@nilovon/bucketkit-core';
const app = express();
app.use(express.json());
// Auth middleware
function authenticate(req, res, next) {
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) {
return res.status(401).json({ error: 'Unauthorized' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch {
res.status(401).json({ error: 'Invalid token' });
}
}
const bucketKit = createBucketKit({
provider: 'aws-s3',
region: 'us-east-1',
bucket: 'my-uploads',
});
// Protected upload endpoint
app.post('/api/upload', authenticate, async (req, res) => {
try {
const result = await bucketKit.createPresignedUpload({
...req.body,
userId: req.user.id, // Associate with authenticated user
});
res.json(result);
} catch (error) {
res.status(400).json({ error: error.message });
}
});Next.js with NextAuth
// app/api/upload/route.ts
import { getServerSession } from 'next-auth';
import { createBucketKit } from '@nilovon/bucketkit-core';
import { authOptions } from '@/lib/auth';
const bucketKit = createBucketKit({
provider: 'aws-s3',
region: process.env.S3_REGION!,
bucket: process.env.S3_BUCKET!,
});
export async function POST(request: Request) {
const session = await getServerSession(authOptions);
if (!session?.user) {
return Response.json({ error: 'Unauthorized' }, { status: 401 });
}
const body = await request.json();
try {
const result = await bucketKit.createPresignedUpload({
...body,
userId: session.user.id,
});
return Response.json(result);
} catch (error) {
return Response.json(
{ error: error.message },
{ status: 400 }
);
}
}Frontend Authentication
With Auth Headers
import { BucketKitProvider } from '@nilovon/bucketkit-react';
import { useAuth } from 'your-auth-library';
function App() {
const { token, isAuthenticated } = useAuth();
if (!isAuthenticated) {
return <LoginPage />;
}
return (
<BucketKitProvider
endpoint="/api/upload"
headers={{
Authorization: `Bearer ${token}`,
}}
>
<UploadPage />
</BucketKitProvider>
);
}With Cookies (Same-Origin)
// If using cookies for auth, they're sent automatically
<BucketKitProvider
endpoint="/api/upload"
// No headers needed - cookies are included automatically
>
<UploadPage />
</BucketKitProvider>Dynamic Token Refresh
function App() {
const { getAccessToken } = useAuth();
const [headers, setHeaders] = useState({});
useEffect(() => {
async function updateToken() {
const token = await getAccessToken();
setHeaders({ Authorization: `Bearer ${token}` });
}
updateToken();
// Refresh token periodically
const interval = setInterval(updateToken, 4 * 60 * 1000); // 4 minutes
return () => clearInterval(interval);
}, [getAccessToken]);
return (
<BucketKitProvider endpoint="/api/upload" headers={headers}>
<UploadPage />
</BucketKitProvider>
);
}Role-Based Uploads
Different Policies by Role
app.post('/api/upload', authenticate, async (req, res) => {
const { role } = req.user;
// Different policies for different roles
const policyByRole = {
admin: {
maxSize: 100 * 1024 * 1024, // 100 MB
allowedMimeTypes: ['*/*'],
},
user: {
maxSize: 10 * 1024 * 1024, // 10 MB
allowedMimeTypes: ['image/*', 'application/pdf'],
},
guest: {
maxSize: 1 * 1024 * 1024, // 1 MB
allowedMimeTypes: ['image/jpeg', 'image/png'],
},
};
const result = await bucketKit.createPresignedUpload({
...req.body,
userId: req.user.id,
policyOverrides: policyByRole[role] || policyByRole.guest,
});
res.json(result);
});Upload Quotas
import { db } from '@/lib/database';
app.post('/api/upload', authenticate, async (req, res) => {
const { id: userId } = req.user;
// Check user's upload quota
const usage = await db.getUserStorageUsage(userId);
const quota = await db.getUserStorageQuota(userId);
if (usage + req.body.size > quota) {
return res.status(403).json({
error: 'Storage quota exceeded',
usage,
quota,
});
}
const result = await bucketKit.createPresignedUpload({
...req.body,
userId,
});
// Track the upload
await db.recordUpload({
userId,
key: result.key,
size: req.body.size,
});
res.json(result);
});Security Best Practices
- Always validate on the server - Client-side checks can be bypassed
- Use short-lived presigned URLs - Default 15 minutes is usually sufficient
- Associate uploads with users - Track who uploaded what
- Implement rate limiting - Prevent abuse
- Validate file content - MIME types can be spoofed, consider virus scanning
- Use HTTPS - Always use secure connections