Pushduck
Pushduck// S3 uploads for any framework
GuidesSecurity

Authentication

Secure your file uploads with proper authentication and authorization patterns

Authentication & Authorization

Secure your file upload endpoints with robust authentication and authorization middleware.

Important: Never expose upload endpoints without proper authentication in production. Unprotected endpoints can lead to storage abuse and security vulnerabilities.

Authentication Patterns

Modern, type-safe authentication with native TypeScript support:

import { s3 } from "@/lib/upload";
import { auth } from "@/lib/auth"; // Better Auth instance

const s3Router = s3.createRouter({
  userFiles: s3.image()
    .maxFileSize("5MB")
    .maxFiles(10)
    .middleware(async ({ req, metadata }) => {
      const session = await auth.api.getSession({ headers: req.headers });

      if (!session?.user?.id) {
        throw new Error("Authentication required");
      }

      return {
        ...metadata,
        userId: session.user.id,
        userEmail: session.user.email,
      };
    }),
});

export const { GET, POST } = s3Router.handlers;

NextAuth.js Integration

import { s3 } from "@/lib/upload";
import { getServerSession } from "next-auth";
import { authOptions } from "@/lib/auth";

const s3Router = s3.createRouter({
  userFiles: s3.image()
    .maxFileSize("5MB")
    .maxFiles(10)
    .middleware(async ({ req, metadata }) => {
      const session = await getServerSession(authOptions);

      if (!session?.user?.id) {
        throw new Error("Authentication required");
      }

      return {
        ...metadata,
        userId: session.user.id,
        userEmail: session.user.email,
      };
    }),
});

export const { GET, POST } = s3Router.handlers;

JWT Token Validation

import jwt from "jsonwebtoken";

const s3Router = s3.createRouter({
  protectedUploads: s3.file()
    .maxFileSize("10MB")
    .maxFiles(5)
    .middleware(async ({ req, metadata }) => {
      const token = req.headers.get("authorization")?.replace("Bearer ", "");

      if (!token) {
        throw new Error("Authorization token required");
      }

      try {
        const payload = jwt.verify(token, process.env.JWT_SECRET!) as any;

        return {
          ...metadata,
          userId: payload.sub,
          roles: payload.roles || [],
        };
      } catch (error) {
        throw new Error("Invalid or expired token");
      }
    }),
});

Custom Authentication

const s3Router = s3.createRouter({
  apiKeyUploads: s3.file()
    .maxFileSize("25MB")
    .maxFiles(1)
    .types(['application/pdf', 'application/msword'])
    .middleware(async ({ req, metadata }) => {
      const apiKey = req.headers.get("x-api-key");

      if (!apiKey) {
        throw new Error("API key required");
      }

      // Validate API key against your database
      const client = await validateApiKey(apiKey);

      if (!client) {
        throw new Error("Invalid API key");
      }

      return {
        ...metadata,
        clientId: client.id,
        plan: client.plan,
        quotaUsed: client.quotaUsed,
      };
    }),
});

Simple Role Checking

For apps with admins vs regular users:

const s3Router = s3.createRouter({
  adminUploads: s3.file()
    .maxFileSize("100MB")
    .middleware(async ({ req, metadata }) => {
      const { userId, role } = await authenticateUser(req);
      
      if (role !== "admin") {
        throw new Error("Admin access required");
      }
      
      return { ...metadata, userId, role };
    }),
});

That's it! For more complex permissions (multi-tenant, resource-based), see advanced patterns.


Next Steps