🔒 [security fix] Add missing authentication to server actions#99
Conversation
Co-authored-by: aniruddhaadak80 <127435065+aniruddhaadak80@users.noreply.github.com>
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
✅ Deploy Preview for career-zen ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
There was a problem hiding this comment.
Pull request overview
This PR addresses a security gap in the Next.js App Router by adding Clerk authentication checks to several Server Actions so unauthenticated users can’t invoke AI-powered or data-mutating operations.
Changes:
- Added Clerk
auth()gating to AI-related Server Actions (Sarvam/Gemini) to require a signed-in user. - Added auth/ownership checks to resume persistence and retrieval actions.
- Hardened login activity logging to ensure the action only logs for the authenticated user.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
src/app/actions/user.ts |
Adds Clerk auth check to logLogin and prevents logging for mismatched user IDs. |
src/app/actions/sarvam.ts |
Requires authentication for Sarvam transcription and chat completion actions. |
src/app/actions/resume.ts |
Requires authentication and enforces user ownership for resume saves; makes resume fetch user-scoped. |
src/app/actions/optimize.ts |
Requires authentication before calling Gemini optimization flow. |
src/app/actions/extract.ts |
Requires authentication before extracting text (PDF/DOCX/Image OCR). |
Comments suppressed due to low confidence (2)
src/app/actions/resume.ts:26
saveResumevalidatesdata.userIdagainst the authenticated user, but then continues to use the client-provideddata.userIdfor the DB write andlogActivity. Even though the equality check prevents impersonation today, it’s safer and simpler to use the authenticateduserIdvariable for all persisted identity fields, and optionally remove/ignoreuserIdfrom the input payload to reduce the chance of future regressions.
const { userId } = await auth();
if (!userId || userId !== data.userId) {
return { success: false, error: "Unauthorized" };
}
try {
await prisma.resume.create({
data: {
userId: data.userId,
fileName: data.fileName,
src/app/actions/user.ts:15
logLoginis invoked fromsrc/components/LoginTracker.tsxwithoutawait. If anything before/aroundlogActivitythrows (e.g.cookies()access), this can surface as an unhandled promise rejection in the client. To make this safe, wrap the whole function body in atry/catchand always resolve a{ logged: false }-style response (or adjust the client toawait/.catch()the call).
export async function logLogin(providedUserId: string) {
const { userId } = await auth();
if (!userId || userId !== providedUserId) return { logged: false };
// Use a short-lived cookie to strictly prevent spamming the database on every page load
const cookieStore = await cookies();
if (cookieStore.get('has_logged_in_session')) {
return { logged: false };
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| export async function getUserResumes() { | ||
| const { userId } = await auth(); | ||
| if (!userId) { | ||
| return { success: false, error: "Not authenticated" }; | ||
| } |
There was a problem hiding this comment.
getUserResumes returns { success: false, error: "Not authenticated" } without a data field. Other list-style actions (e.g. getApplications in src/app/actions/tracker.ts) return a consistent shape with data: [] on auth or fetch failures; returning a consistent response shape here would prevent consumers from needing extra guards.
🎯 What: The vulnerability fixed is missing authentication in several Next.js Server Actions.
⚠️ Risk: Unauthenticated users could potentially invoke these actions, leading to unauthorized use of AI services (Sarvam, Gemini) and potential data leakage or manipulation in the database.
🛡️ Solution: Integrated Clerk's
auth()check at the beginning of sensitive server actions (transcribeAudio,generateCoachResponse,extractText,optimizeResume,saveResume,getUserResumes,logLogin). These actions now return an error or unauthorized status if the user is not authenticated or if they attempt to access data that doesn't belong to them.PR created automatically by Jules for task 2659666468053366896 started by @aniruddhaadak80