How to build a client portal that won't leak customer data

How to build a client portal that won't leak customer data

June 4, 2026

You have described your application to an AI agent, watched the interface render in seconds, and successfully logged into your new dashboard. It feels like a massive win. You invite your first ten clients, and everything looks clean. But behind the beautiful React components and smooth Tailwind animations, a quiet disaster might be waiting to happen.

Building a client portal is fundamentally different from building a public-facing directory or a simple web app. In a client portal, data isolation is the only feature that actually matters. If a client can access their invoices, they should never be able to see the invoices of another client.

Many builders use AI code generators to scaffold these portals, relying on the AI to configure backends like Supabase. But relying on AI to write Row-Level Security (RLS) policies is a shortcut that often leads to leaked data.

Here is why AI-generated database security is fragile, and how visual permission schemas offer a more reliable path for client portals.

The illusion of frontend security

When you build with AI assistants, they tend to write code that prioritizes visual feedback. The AI hides a button if the user is not an administrator, or hides a list of records if the user’s email does not match the record.

This is frontend security, and it is entirely useless on its own.

Anyone with a browser can open developer tools, click the network tab, and inspect the incoming JSON payloads. If your application fetches all client records from the database and filters them in the frontend using React or Vue, your data is already leaked. An attacker does not need to hack your server. They just need to press F12 to view the raw data that your frontend code tried to hide.

To build a secure portal, you must filter data at the database level before it ever travels over the network. If a client requests a list of projects, the database engine must inspect the session, verify the identity, and return only the matching rows.

For projects built on Supabase, this database security relies entirely on PostgreSQL Row-Level Security (RLS).

The vulnerabilities of AI-generated Supabase RLS

Supabase is an excellent backend choice for custom apps, but it shifts the security burden to database configuration. Setting up RLS requires writing precise PostgreSQL policies using SQL.

When you ask an AI builder to handle this, you introduce several points of failure.

1. Context window drift and relationship complexity

A simple RLS policy is easy to write. If a user owns a row, they can read it. You write a rule checking if auth.uid() = user_id.

But client portals are rarely that simple. You usually have organizations, teams, external contractors, and different roles. A project might belong to an organization, and a user is a member of that organization with a specific role. To verify if a user can read a project record, the database must query a junction table.

As your database schema grows, the AI must keep track of all these relationships. The moment you prompt the AI for a new feature, context window drift can occur. The AI may write a new query that bypasses the existing RLS logic or simplifies the policy to get the code to run, opening up access to unauthorized users.

2. Silent failures in SQL logic

PostgreSQL policies fail silently. If you write a buggy policy, PostgreSQL does not crash your app. It either blocks all access or, if configured poorly, allows everyone to read the data.

AI models often make subtle mistakes with SQL null values or boolean logic. For example, a policy like this:

create policy "Users can view company data" on companies
  for select using (
    auth.uid() in (select user_id from members where company_id = id)
    or public_access = true
  );

If the database engine evaluates public_access as null, or if the subquery returns null in an unexpected way, the policy might evaluate to true for all authenticated users. An AI might generate this code because it looks correct syntactically, but it lacks the edge-case handling required for real production environments.

3. The testing gap

Professional developers do not just write RLS policies; they write test suites to verify them. They use tools like pgTAP to simulate different user sessions and confirm that a client cannot query another client’s data.

AI code generators do not write these tests unless you explicitly ask them to, and even then, running database-level security tests in an AI-generated environment is complex. Without automated test suites, you are guessing whether your policies are secure. You only find out they are broken when a client calls to ask why they can see another company’s dashboard.

Visual permission schemas: the no-code alternative

If you do not want to audit PostgreSQL code or manage test suites, you need a different security paradigm. Visual permission schemas, like the ones used in Softr, replace custom database code with structured, platform-enforced rules.

Instead of writing SQL statements to filter rows, you configure your access rules using a visual settings panel.

Visual mapping vs SQL scripting

While traditional setups require writing custom database policies, modern builders rely on visual settings. In Softr, you define user groups based on database attributes. For instance, you can create a group called “Client Portal Users” where the company ID matches the logged-in user’s company ID.

Once defined, you apply these groups directly to pages, blocks, and actions. The platform handles the backend routing and database queries automatically. The frontend block does not receive the full dataset; instead, the server filters the data source before rendering the block.

Natively secure databases

When you use Softr Databases as your native data layer, row-level security is built into the connection from the start. You don’t write security rules on the database tables directly - there’s no generated SQL to audit, no migration scripts to run, and no policies to maintain. The integration layer restricts query access by default, so there’s no generated code that can drift, break, or accidentally expose records after a schema change.

If a user group is only permitted to read records where the status is “Active” and the client field matches their account, the interface engine enforces this filter at the query level. The client cannot manipulate the API request to fetch inactive records or other clients’ rows because the backend server ignores unauthorized parameters.

Softr also connects to over 17 external sources - including Airtable and Google Sheets - if you need to bring in data you already manage elsewhere. The same visual user group rules apply regardless of where the data lives.

The maintenance burden of generated code

Building a client portal is not a single event. Your business rules will change. You will add new client tiers, introduce nested project structures, or change how administrators review submitted documents.

In a generated-code application built with tools like Bolt or Lovable, every layout change or relationship update requires refactoring the database schema and rewriting your security policies. You are relying on the AI to:

  • Read the entire codebase to understand the existing schema.
  • Generate a migration script without breaking existing data.
  • Rewrite the RLS rules to accommodate the new relationships.
  • Update the React frontend to pass the correct headers and tokens.

This is the “Day Two” problem. A single prompt can break a previously working security policy, and you might not notice until the code is already deployed to production.

With Softr, you make these changes without touching a codebase. You can use the AI Co-Builder to describe the change you need - “add a Manager user group that can approve project submissions” - and it updates the user groups, pages, and permissions together. Or you can make the change manually in the settings panel if you prefer. Either way, there’s no generated SQL to audit afterward and no risk of a security policy regression shipping silently to production.

Security best practices for your client portal

If you are building a portal that handles sensitive business data, keep these guidelines in mind:

Stop relying on frontend filters

Never assume data is safe because it is hidden in the UI. Always verify that your backend API or data source is filtering the data before it is sent. If your platform does not support secure backend filtering, do not use it for client portals.

Keep user roles simple

Avoid overly complex permission hierarchies. If your permissions are too complicated for a human to audit easily, an AI will struggle to write them correctly. Stick to clear, distinct user groups like Admins, Managers, and Clients.

Choose structured tools for operational software

AI code generators are great for validation, prototyping, and building custom consumer SaaS interfaces. But when your primary requirement is operational security, data isolation, and long-term stability, a structured no-code platform is the safer choice.

By leveraging a platform that isolates security configurations from the layout code, you ensure that styling updates will never accidentally expose your database to the public internet. You get a secure, professional portal running on a predictable foundation, leaving you to focus on your clients instead of debugging database policies.