Authorization enforced only in the browser
What it looks like
// AdminPanel.tsx
if (!user?.isAdmin) return <Navigate to="/" />;
// app/api/users/route.ts
export async function GET() {
return Response.json(await db.users.findMany());
}What happens
Anyone who calls the API directly, or edits the page in developer tools, gets the data and actions the screen was hiding.
Where it shows up
Descriptive, not a verdict: builders and stacks whose defaults make this reachable, or that a public incident names. Each hub states the tool's own security features.
Seen in public
On the Base44 app builder, undocumented registration and one-time-password endpoints accepted a non-secret app_id, so anyone could create a verified account on a private app; Wix fixed it within 24 hours.
- 2025-07-09 Reported to Wix
- 2025-07-10 Fix verified
- under 24 hours Time to fix
- 2025-07-29 Public disclosure
Jul 2025 · Wiz Research · all sources
Check it yourself
Uses your builder's dashboard and your browser. No tools to install.
- Sign in as a normal user and open developer tools (right-click, Inspect), then the Network tab.
- Browse the app and note any request whose address contains admin, users, export or all.
- Right-click one of those requests, choose Copy as cURL and run it in a terminal, or open its address in a new tab.
- Change the address to a path you were never shown, such as /api/admin/users, and load it while signed in as the normal user.
A bad result is a full response where you expected an HTTP 401 or HTTP 403 error.
What we check
We map every API route and server action the browser can reach and call each one with a signed-out client, a normal account and a second account, ignoring what the interface shows. The report lists routes that answered when the screen would have hidden them.
Fix direction
Move each permission check into the code that reads or writes the data: the route handler, server action or database policy. Next.js and Clerk both document checking the session as close to the data as possible and treating hiding in the interface as a convenience, not a control.
Mechanism documented in: How to implement authentication in Next.js (verified 2026-09-21); Authorization Cheat Sheet (verified 2026-09-21).
Covered by
Pre-launch check ($490, 48 hours) and Security sweep ($290, 24 hours)
Both include a re-check after your fixes.
Related patterns
- Admin routes without any login checkLogin and permissionsCritical
A stranger who types /admin or /api/admin into the address bar can see or change everything the owner can.
- Record ids accepted without an ownership checkData accessCritical
Anyone who changes the id in a URL or request can read, edit or delete another customer's record.
- Tokens trusted without server verificationLogin and permissionsCritical
Anyone can write their own token naming another user, or an admin, and the server will act as that person.
- Role chosen by the client at signupLogin and permissionsCritical
A visitor who edits the signup request, or updates their own profile, can make themselves an admin.
Questions
is client side authorization enough
No. Code that runs in the browser belongs to the visitor, who can change it or skip it and call your API directly. The OWASP authorization cheat sheet says access control checks must be made on the server, and Next.js's guide says hiding a component is not a security boundary. Keep the browser check for a smoother experience and add the real one on the server.
how do i know if my api routes check permissions
Copy a request from the Network tab while signed in as a normal user, then replay it signed out and with an admin-only address. If the data comes back, the route is not checking. A platform-level case on Base44 in 2025 showed the same principle: registration endpoints accepted a non-secret app id, so a private setting in the builder was not enforced where it mattered. Wix fixed it within a day. Run the replay test on your own routes.
what does authorization on the server mean
It means the code that fetches or changes data first confirms who is asking and whether they are allowed, using a session the server has verified. Supabase does this with RLS policies in the database, Clerk with the auth() helper in route handlers and server actions, and Firebase with security rules or the Admin SDK. The check happens where the data is, not where the button is.
Back to the catalogue, or run the self-check for your stack.