Fix guide · high · convex_function_no_auth
Convex query/mutation returns data without authentication
A Convex function (extracted from your client bundle) returned a successful response when called without an auth header. Anonymous callers can read whatever this function returns.
Why it matters
Convex doesn't have Row-Level Security. The platform's contract is that every function enforces its own access control via auth.getUserIdentity(). Skipping the check on a single function makes that function publicly readable forever — and the function name is in your client bundle.
How to fix it
Add the auth check to the function:
// convex/messages.ts
import { query } from "./_generated/server";
import { v } from "convex/values";
export const list = query({
args: { conversationId: v.id("conversations") },
handler: async (ctx, { conversationId }) => {
const identity = await ctx.auth.getUserIdentity();
if (!identity) throw new Error("Unauthenticated");
// Verify the user is a member of this conversation:
const membership = await ctx.db
.query("conversation_members")
.withIndex("by_user_conversation", q =>
q.eq("userId", identity.subject).eq("conversationId", conversationId)
)
.unique();
if (!membership) throw new Error("Forbidden");
return await ctx.db
.query("messages")
.withIndex("by_conversation", q => q.eq("conversationId", conversationId))
.collect();
},
});
For mutations: identical pattern. Always derive the user ID from identity.subject rather than trusting a client-supplied authorId.
Full guide: /blog/convex-security.
Did vibecheck flag this on your app?
If you reached this page from a vibecheck inspection report, the redacted match in your scan output is the exact string we found in your bundle. After applying the fix above, run the inspection again — the finding should clear.
Run another inspection