Authentication & Security
Authentication is a vital part in protecting your app from malicious usage. In this section we'll go over how to protect different parts of the UploadThing flow.
Do not protect the entire /api/uploadthing
route from being called by
unauthenticated users. The endpoint is called as a webhook by our server and
thus must be publically available.
Protecting the endpoint from spoofing
The callback request is like a webhook that is called by UploadThing when your
file has been uploaded to the storage provider. The callback data is signed
(HMAC SHA256) using the API key that uploaded the file. Since v6.7
of the
Uploadthing SDK, the callback data is automatically verified before executing
the callback. There is no additional work needed to protect the callback
endpoint other than making sure you're on a version ^6.7
to ensure your
endpoint is appropriately protected.
Protecting unauthenticated users from uploading files
You can protect unauthenticated users from uploading files via the
.middleware()
function in each file route. This
makes it trivial to protect some file routes, and keep some public.
Using your favorite authentication provider (or self-roll if that's your thing), retrieve the current user's session from the incoming request. If it's not valid, you can throw an error which will terminate the upload flow. In the following example, we have a public file route that is protected by rate limiting, and a protected route that allows any authenticated user to upload files:
import { auth } from "auth";
import { createUploadthing, UploadThingError } from "uploadthing/server";
import { RateLimit } from "~/lib/ratelimit";
const ratelimiter = new RateLimit({
/** rules */
});
export const uploadRouter = {
publicRoute: f({ image: {} })
.middleware(async ({ req }) => {
const limit = await ratelimiter.verify(req);
if (!limit.ok) {
throw new UploadThingError("Rate limit exceeded");
}
return {};
})
.onUploadComplete(() => {
/** ... */
}),
privateRoute: f({ image: {} })
.middleware(async ({ req }) => {
const session = await auth(req);
if (!session) {
throw new UploadThingError("You need to be logged in to upload files");
}
return { userId: session.user.id };
})
.onUploadComplete(() => {
/** ... */
}),
};
By throwing an UploadThingError
, the error message is automatically sent
down to the client. If throwing other errors, you need an
errorFormatter
to control what
is sent down to the client.