UploadThing - Cant pass userId

Hey guys, my lack of knowledge is showing here 😓 I am trying to pass the input prop to pass my user ID but I cant use that prop since I am not really using the base UploadComponent as I followed the nextJS docs and it uses a generator. So it says input is not a valid prop, how do I fix this? /api/uploadthing/core.ts
import { createUploadthing, type FileRouter } from "uploadthing/next";
import { UploadThingError } from "uploadthing/server";

const f = createUploadthing();

// FileRouter for your app, can contain multiple FileRoutes
export const ourFileRouter = {
// Define as many FileRoutes as you like, each with a unique routeSlug
imageUploader: f({ image: { maxFileSize: "4MB" } })
// Set permissions and file types for this FileRoute
.middleware(async ({ req, input }) => {
try {
// This code runs on your server before upload
const userId = input.userId as string | null;

// If you throw, the user will not be able to upload
if (!userId) throw new UploadThingError("Unauthorized");

// Whatever is returned here is accessible in onUploadComplete as `metadata`
return { userId };
} catch (error) {
console.error("Middleware error:", error);
throw new UploadThingError("Internal Server Error");
}
})
.onUploadComplete(async ({ metadata, file }) => {
// This code RUNS ON YOUR SERVER after upload
console.log("Upload complete for userId:", metadata.userId);

console.log("file url", file.url);

// !!! Whatever is returned here is sent to the clientside `onClientUploadComplete` callback
return { uploadedBy: metadata.userId };
}),
} satisfies FileRouter;

export type OurFileRouter = typeof ourFileRouter;
import { createUploadthing, type FileRouter } from "uploadthing/next";
import { UploadThingError } from "uploadthing/server";

const f = createUploadthing();

// FileRouter for your app, can contain multiple FileRoutes
export const ourFileRouter = {
// Define as many FileRoutes as you like, each with a unique routeSlug
imageUploader: f({ image: { maxFileSize: "4MB" } })
// Set permissions and file types for this FileRoute
.middleware(async ({ req, input }) => {
try {
// This code runs on your server before upload
const userId = input.userId as string | null;

// If you throw, the user will not be able to upload
if (!userId) throw new UploadThingError("Unauthorized");

// Whatever is returned here is accessible in onUploadComplete as `metadata`
return { userId };
} catch (error) {
console.error("Middleware error:", error);
throw new UploadThingError("Internal Server Error");
}
})
.onUploadComplete(async ({ metadata, file }) => {
// This code RUNS ON YOUR SERVER after upload
console.log("Upload complete for userId:", metadata.userId);

console.log("file url", file.url);

// !!! Whatever is returned here is sent to the clientside `onClientUploadComplete` callback
return { uploadedBy: metadata.userId };
}),
} satisfies FileRouter;

export type OurFileRouter = typeof ourFileRouter;
page.tsx
"use client";

import { UploadButton } from "../../../util/uploadthing";
import { useEffect, useState } from "react";

export default function Home() {
const [userId, setUserId] = useState<string | null>(null);

useEffect(() => {
const fetchUserId = async () => {
const response = await fetch("/api/auth/me/id");
const data = await response.json();
setUserId(data.userId);
};

fetchUserId();
}, []);

return (
<main className="flex min-h-screen flex-col items-center justify-between p-24">
<UploadButton
endpoint="imageUploader"
input={{ userId }}
onClientUploadComplete={(res) => {
// Do something with the response
console.log("Files: ", res);
alert("Upload Completed");
}}
onUploadError={(error: Error) => {
// Do something with the error.
alert(`ERROR! ${error.message}`);
}}
/>
</main>
);
}
"use client";

import { UploadButton } from "../../../util/uploadthing";
import { useEffect, useState } from "react";

export default function Home() {
const [userId, setUserId] = useState<string | null>(null);

useEffect(() => {
const fetchUserId = async () => {
const response = await fetch("/api/auth/me/id");
const data = await response.json();
setUserId(data.userId);
};

fetchUserId();
}, []);

return (
<main className="flex min-h-screen flex-col items-center justify-between p-24">
<UploadButton
endpoint="imageUploader"
input={{ userId }}
onClientUploadComplete={(res) => {
// Do something with the response
console.log("Files: ", res);
alert("Upload Completed");
}}
onUploadError={(error: Error) => {
// Do something with the error.
alert(`ERROR! ${error.message}`);
}}
/>
</main>
);
}
10 Replies
Juraj98
Juraj98•6mo ago
I don't think you should be passing userId from the client. If it's passed from the client, it can be modified. The outFileRouter runs on the server, so you should get the userId from whatever auth solution you're using. For example, I'm using clerk, and my middleware looks like this:
import { auth } from "@clerk/nextjs/server";

// ====

.middleware(async () => {
const user = auth();

if (!user.userId) throw new UploadThingError("Unauthorized");

return { userId: user.userId };
})
import { auth } from "@clerk/nextjs/server";

// ====

.middleware(async () => {
const user = auth();

if (!user.userId) throw new UploadThingError("Unauthorized");

return { userId: user.userId };
})
oz
ozOP•6mo ago
I use auth0 for auth, they dont have an easy way to get ID the way we need it, so my Java server passes it instead via REST. . So your saying I should make a nextjs server action to get userId and call that server action from within the middleware? Also thanks for your help 🙂
Juraj98
Juraj98•6mo ago
How is your auth implemented on the next.js side?
oz
ozOP•6mo ago
Sorry missed noto. No logic is really done on the system, so we only really have auth logic for the middleware to check if signed in. All logic is done on our Springboot server, so our NextJS app just forwards all requests there with the users token
Juraj98
Juraj98•6mo ago
So, if I understand it correctly, you have jwt token attached to your headers? If yes, you could get the token from headers in middleware, verity its signature, and hopefully get the userId from there. You could technically send a request from the middleware to your java server, but that's extra latency + request you don't need.
oz
ozOP•6mo ago
Decoding the token is a no go for us b/c auth0 is weird with the settings for that. I know its probably not the best to do, but I would like to make the request in the middleware to get that if I cant pass it from frontend I tried that but when I fetched it was like the request (from the /api/uploadthing/core.ts file) was not part of the system as /api/route threw an error saying the URL needed a prefix and the token was null, which is why it feels like its not "a part of" the system Would a server action for the request fix this? So I would call the server action in the middleware?
Juraj98
Juraj98•6mo ago
Man, tbh, I don't know. Maybe? I realistically don't see a reason why a fetch request would fail. It feels like the configuration you have going is pretty strange. Can you share your next.js middleware with me?
oz
ozOP•6mo ago
import { NextResponse } from "next/server";
import { getAccessToken } from "@auth0/nextjs-auth0/edge";
import type { NextRequest } from "next/server";
import { withMiddlewareAuthRequired } from "@auth0/nextjs-auth0/edge";

async function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;

// Bypass middleware for API requests and static files in the public directory
if (pathname.startsWith("/api/") || pathname.startsWith("/_next/static/") || /\.(jpeg|jpg|png|svg|gif|ico|webp|bmp|tiff?)$/i.test(pathname)) {
return NextResponse.next();
}

const res = new NextResponse();
const { accessToken } = await getAccessToken(request, res);

// Redirect to login if no access token is found
if (!accessToken) {
return NextResponse.redirect(new URL("/api/auth/login", request.url));
}

const [header, payload, signature] = accessToken.split(".");
const base64Url = require("base64url");
const permissions = JSON.parse(base64Url.decode(payload)).permissions;
// Determine user roles based on permissions
const isAdmin = permissions.includes("FRONTEND_admin");
const isEmployee = permissions.includes("FRONTEND_employee");
const isClient = permissions.includes("FRONTEND_client");

// Logic for Redirect based on user roles and requested path

return NextResponse.next();
}

export default withMiddlewareAuthRequired(middleware);
import { NextResponse } from "next/server";
import { getAccessToken } from "@auth0/nextjs-auth0/edge";
import type { NextRequest } from "next/server";
import { withMiddlewareAuthRequired } from "@auth0/nextjs-auth0/edge";

async function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;

// Bypass middleware for API requests and static files in the public directory
if (pathname.startsWith("/api/") || pathname.startsWith("/_next/static/") || /\.(jpeg|jpg|png|svg|gif|ico|webp|bmp|tiff?)$/i.test(pathname)) {
return NextResponse.next();
}

const res = new NextResponse();
const { accessToken } = await getAccessToken(request, res);

// Redirect to login if no access token is found
if (!accessToken) {
return NextResponse.redirect(new URL("/api/auth/login", request.url));
}

const [header, payload, signature] = accessToken.split(".");
const base64Url = require("base64url");
const permissions = JSON.parse(base64Url.decode(payload)).permissions;
// Determine user roles based on permissions
const isAdmin = permissions.includes("FRONTEND_admin");
const isEmployee = permissions.includes("FRONTEND_employee");
const isClient = permissions.includes("FRONTEND_client");

// Logic for Redirect based on user roles and requested path

return NextResponse.next();
}

export default withMiddlewareAuthRequired(middleware);
I feel very scared that this is bad haha
Juraj98
Juraj98•6mo ago
If you console.log return value from headers() in the ourFileRouter middleware, what do you get? headers function from import { headers } from "next/headers"; Never mind that. Can you try this?
import { getSession } from '@auth0/nextjs-auth0';

// ==========

.middleware(async ({ req, input }) => {
try {
// This code runs on your server before upload
const { user } = await getSession();

const userId = user.user_id;

// If you throw, the user will not be able to upload
if (!userId) throw new UploadThingError("Unauthorized");

// Whatever is returned here is accessible in onUploadComplete as `metadata`
return { userId };
} catch (error) {
console.error("Middleware error:", error);
throw new UploadThingError("Internal Server Error");
}
})
import { getSession } from '@auth0/nextjs-auth0';

// ==========

.middleware(async ({ req, input }) => {
try {
// This code runs on your server before upload
const { user } = await getSession();

const userId = user.user_id;

// If you throw, the user will not be able to upload
if (!userId) throw new UploadThingError("Unauthorized");

// Whatever is returned here is accessible in onUploadComplete as `metadata`
return { userId };
} catch (error) {
console.error("Middleware error:", error);
throw new UploadThingError("Internal Server Error");
}
})
The user might be null if you're not authenticated. I don't know what are the exact types. But something like this should work according to auth0 docs.
oz
ozOP•6mo ago
Property 'user' does not exist on type 'Session | null | undefined'. Figured it out, you really helped me get there, thanks! In case someone else finds this helpful
import { createUploadthing, type FileRouter } from "uploadthing/next";
import { UploadThingError } from "uploadthing/server";
import { getSession } from "@auth0/nextjs-auth0";

const f = createUploadthing();

// FileRouter for your app, can contain multiple FileRoutes
export const ourFileRouter = {
// Define as many FileRoutes as you like, each with a unique routeSlug
imageUploader: f({ image: { maxFileSize: "4MB" } })
// Set permissions and file types for this FileRoute
.middleware(async ({ req, input }) => {
try {
// This code runs on your server before upload
const { user } = (await getSession()) || {};

const userId = user.sub;

// If you throw, the user will not be able to uploada
if (!userId) throw new UploadThingError("Unauthorized");

// Whatever is returned here is accessible in onUploadComplete as `metadata`
return { userId };
} catch (error) {
console.error("Middleware error:", error);
throw new UploadThingError("Internal Server Error");
}
})
.onUploadComplete(async ({ metadata, file }) => {
// This code RUNS ON YOUR SERVER after upload
console.log("Upload complete for userId:", metadata.userId);

console.log("file url", file.url);

// !!! Whatever is returned here is sent to the clientside `onClientUploadComplete` callback
return { uploadedBy: metadata.userId };
}),
} satisfies FileRouter;

export type OurFileRouter = typeof ourFileRouter;
import { createUploadthing, type FileRouter } from "uploadthing/next";
import { UploadThingError } from "uploadthing/server";
import { getSession } from "@auth0/nextjs-auth0";

const f = createUploadthing();

// FileRouter for your app, can contain multiple FileRoutes
export const ourFileRouter = {
// Define as many FileRoutes as you like, each with a unique routeSlug
imageUploader: f({ image: { maxFileSize: "4MB" } })
// Set permissions and file types for this FileRoute
.middleware(async ({ req, input }) => {
try {
// This code runs on your server before upload
const { user } = (await getSession()) || {};

const userId = user.sub;

// If you throw, the user will not be able to uploada
if (!userId) throw new UploadThingError("Unauthorized");

// Whatever is returned here is accessible in onUploadComplete as `metadata`
return { userId };
} catch (error) {
console.error("Middleware error:", error);
throw new UploadThingError("Internal Server Error");
}
})
.onUploadComplete(async ({ metadata, file }) => {
// This code RUNS ON YOUR SERVER after upload
console.log("Upload complete for userId:", metadata.userId);

console.log("file url", file.url);

// !!! Whatever is returned here is sent to the clientside `onClientUploadComplete` callback
return { uploadedBy: metadata.userId };
}),
} satisfies FileRouter;

export type OurFileRouter = typeof ourFileRouter;
Upload is succeeding except the webhook, maybe thats it hmm.
Want results from more Discord servers?
Add your server