← Reference

Every Convex function you export is callable by anyone

There is no rules layer. The authorisation check has to be inside each function.

01

What it is

Convex has no separate permissions system. Your deployment URL is public, the function names are in your bundle, and anything you export can be called by anybody who reads them.

That is a deliberate design: authorisation lives in your function code, where you can express it properly. It also means a function that forgets to check is simply open.

02

How to fix it

At the top of every query and mutation that touches user data, get the identity and refuse when there is none:

Copy this
const identity = await ctx.auth.getUserIdentity();
if (!identity) throw new Error("Not signed in");

Then filter by that identity rather than trusting an argument. A userId passed in as an argument can be changed by the caller; ctx.auth cannot.

03

How to confirm the fix

Call the function from a plain fetch with no auth header against your deployment URL. It should error rather than return data.

04

The mistake people make

Filtering on a userId that arrives as a function argument. The browser controls arguments, so anyone can pass someone else's id. Always derive the user from ctx.auth.

Not sure whether this applies to you?

Give us the address and we will tell you. No code, no access, no install — and every finding we have is shown in full, including on the free trial.

Check a site