If you’re still copying dashboards for every customer, multi tenant row level security is the fix. One shared dashboard can serve every tenant safely, but only if your data model, identity flow, and BI setup all agree on who gets to see which rows.
What you’re building and what you’ll need
The goal is simple: one dashboard experience, many customers, and strict data isolation. Instead of cloning the same Tableau workbook, Power BI report, or AWS QuickSight analysis over and over, you set up one reusable dashboard and let access rules decide which rows appear for each signed-in person.
Before touching any filters, get clear on the moving parts. You need a BI tool that supports embedded or customer-facing analytics, a database or warehouse where tenant filtering is possible, and a signed-in app flow that can tell your analytics layer which customer account the person belongs to. If any one of those pieces is fuzzy, the whole setup gets shaky fast.
Prerequisites: tools, access, and source data
Have admin access to your BI platform and enough access to your database or warehouse to inspect tables, views, and security rules. Keep at least one test tenant account handy, plus one account that should not see that tenant’s data. That pair will save you from false confidence later.
Your source data needs one thing above all else: every protected row must carry a tenant identifier. Call it tenant_id, account_id, or something equally plain, but pick one convention and stick to it. If invoices have it but usage events don’t, your dashboard stops being secure the moment someone joins those tables.
What “row level security” means in plain English
Row level security means access rules decide which records a signed-in user can see. Not which dashboard loads. Not which tab appears. Which actual rows come back from a query.
That matters because duplicated dashboards solve the wrong problem. You don’t want fifty dashboard copies with slightly different filters. You want one dashboard that behaves differently based on identity. If you want a quick mental model for who should see which rows in a shared view, start there.

Step 1: Pick a tenancy pattern that won’t trap you later
Before setting up any security policy, decide what kind of tenant model you’re supporting. This is the part that feels architectural, but it directly affects dashboard maintenance, support load, and your odds of leaking data by accident.
For customer-facing analytics, a shared dashboard on top of tenant-aware filtering is usually the cleanest path. It keeps analytics updates centralized and makes every improvement available to all customers at once.
Shared dashboard vs duplicated dashboards
Duplicating dashboards feels fine on day one. Customer A gets a copy. Customer B gets a copy. Everyone is happy until the first metric definition changes at 4:45 p.m. on a Friday, and now you’re patching twelve versions of the same chart.
A shared dashboard wins because it gives you one place to update logic, layout, and labels. The security work moves into data access, where it belongs. That’s harder upfront and much easier forever after.
Shared schema, separate schema, or separate database
There are three common layouts. A shared schema puts all tenant data in the same tables with a tenant key on each row. Separate schemas split tenants into different namespaces. Separate databases isolate tenants even more aggressively.
For most embedded analytics setups, shared schema plus tenant-aware filtering is the sweet spot. It scales better, keeps reporting logic centralized, and works naturally with row level security. Separate schemas and separate databases can work, but they usually add operational overhead that shows up later in refresh jobs, migrations, and support.
Decide where enforcement should happen
You can enforce tenant filtering in the database, in the BI tool, or in an embedding layer that injects access context. All three are possible. One is the safest default.
Database-level enforcement is the best place to start because it gives you fewer ways to leak data. If a workbook changes, a filter gets removed, or someone creates a new report on the same source, the underlying database policy still applies. That is exactly why many PostgreSQL multi-tenant patterns lean on native row filtering policies.
Step 2: Add tenant context to your data model
A reusable dashboard only works if your underlying model is tenant-aware from the ground up. This is the part that feels boring, but it decides whether your security model is solid or held together with hope.
Add a tenant_id to every protected table
Every protected fact table should include a tenant key. So should tenant-scoped dimensions and derived tables. If a customer order has tenant_id but the joined subscription table doesn’t, your access logic turns brittle the moment someone writes a more complex query.
Keep the field type and naming consistent. Don’t mix UUIDs in one table, strings in another, and legacy account names in a third. Security logic gets easier when the key is boring.
Handle shared reference data the right way
Some tables should be global. Calendar tables, standard country codes, and universal product metadata often belong here. Those do not need tenant filtering because nothing private lives in them.
The trick is separating global reference data from tenant-scoped data cleanly. If a dimension contains both public fields and customer-specific fields, split it or tag it carefully. Clear boundaries keep your security model predictable.
Validate joins so tenant filters don’t break analysis
Bad joins cause two problems: extra rows and wider access. Both are ugly. If a join only uses customer_id and ignores tenant_id, records can spill across tenant boundaries in subtle ways.
Validate every join path before building reports. Join on tenant-aware keys where appropriate, inspect row counts, and compare output for a known tenant against expected totals. For a deeper checklist on keeping external reporting rules tight and predictable, this is one of the first places worth pressure-testing.
Step 3: Define the user-to-tenant mapping
Dashboards do not magically know which tenant a person belongs to. Your app, embed layer, or BI platform has to supply that context somehow.
Choose the identity field to trust
Pick one stable field that represents tenant membership. tenant_id is ideal. account_id is fine if it means the same thing everywhere. A signed claim from your app can also work.
Do not rely on display names or email labels alone. Emails change. Names get reformatted. Security rules built on mutable labels tend to fail in the most annoying way possible: quietly.
Support users who belong to more than one tenant
Multi-tenant access is a common edge case, especially for agencies, channel partners, franchise operators, and internal account managers. One person may need access to several tenant IDs at once.
Handle that with a many-to-many mapping, not by cloning dashboards. Your security model should allow one user identity to resolve to multiple allowed tenant IDs, then filter rows accordingly.
Create a simple access mapping table
Create one access table or view that links user identity to allowed tenant IDs. Keep it plain. For example: user_id, tenant_id, role, active.
That table becomes your source of truth. Your BI tool can reference it directly, or your database policy can use it when evaluating access. If your setup spans multiple tools, a single place to manage tenant filtering rules saves a lot of cleanup later.
Step 4: Set up row level security in the data layer
https://www.youtube.com/watch?v=OwCgDPa0DnA
This is the strongest version of the pattern: the database or warehouse enforces tenant access before the BI tool renders anything. The shared dashboard stays the same. The returned rows change by session context.
Pass tenant context into each session
Your app or embed flow needs to pass the current user identity, current tenant, or allowed tenant list into the data session. In PostgreSQL-backed setups, that often means session variables or request context. In warehouse-driven stacks, it may mean secure views, user attributes, or connection-level context.
This is the trick that makes one dashboard behave like a custom dashboard for each customer. Same workbook. Same report. Different result set.
Write an RLS policy or secure filter rule
The policy logic is straightforward in concept: return a row only if that row’s tenant_id is in the signed-in user’s allowed tenant list. The exact syntax varies by platform, but the pattern does not.
Keep the rule as close to the data as possible, and avoid clever fallback logic. If tenant context is missing, the safest response is no rows, not all rows. That sounds obvious, but permissive defaults still cause plenty of leaks.
Test with a known tenant and a known non-tenant
Use a small test dataset and run two checks after the policy is in place. First, confirm that an authorized tenant sees the expected records. Second, confirm that an unauthorized account sees none.
Checkpoint: if a test user can switch a parameter, edit a URL, or hit a saved view and suddenly see a different tenant’s data, your security is not done.

Step 5: Configure tenant-aware access in Tableau, Power BI, or AWS QuickSight
Once the data layer is secure, connect your BI tool to that same identity and access model. The goal is not to rebuild security three different ways. The goal is to make sure the BI tool respects the context you already defined.
Tableau: user filters, data source filters, and embedded identity
Tableau supports user filters, security tables, and data source filters. Those can work well, especially for internal use. But for customer-facing analytics, Tableau is often safer consuming already-filtered data from the source.
If you do use Tableau-side rules, make sure embedded identity and user attributes map cleanly to tenant membership. Otherwise, the workbook exists, the filter exists, and the session still loads the wrong scope.
Power BI: roles, relationships, and effective identity
Power BI handles dynamic access with roles and relationships, then applies that logic through embedded effective identity. This is where teams often get tripped up. The role is defined, the dataset looks correct, but the embed token never activates the expected role.
Test the embed path, not just the desktop file. A report that behaves in Power BI Service can still fail once embedded if identity claims are incomplete or mismatched.
AWS QuickSight: namespace users, rules datasets, and row permissions
QuickSight supports row-level permissions through rules datasets and related access controls. That makes it workable for shared dashboards, as long as your permission dataset stays in sync with your application identity model.
The good news is you do not need one analysis per customer. You need one reusable analysis and a clean mapping between viewer identity and allowed rows. If you’re evaluating tools more broadly, it helps to know what actually matters in an embedded reporting stack.
Step 6: Build one dashboard template instead of one dashboard per customer
Secure data is only half the job. The dashboard itself has to be reusable, or it will drift into tenant-specific clutter even if the access model is perfect.
Remove tenant-specific labels and hardcoded filters
Strip out customer names from chart titles, dashboard text, and default filter labels unless those values are injected dynamically. Hardcoded tenant names are how a shared dashboard quietly turns back into a custom one.
Neutral labels make your template durable. “Monthly revenue” ages well. “Acme North Region revenue” does not.
Use parameters for lightweight customization
You can still personalize the experience without duplicating the dashboard. Pass a customer display name, default date range, theme color, or region label as a parameter if your BI tool supports it.
Keep the distinction sharp: parameters are for presentation, not security. Cosmetic customization is fine. Data isolation should never depend on a parameter someone can change.
Keep metrics definitions consistent across tenants
A shared dashboard only works when core metrics mean the same thing for everyone. If one tenant treats “active user” as 7 days and another wants 30 days, your shared model starts to crack.
Set standard definitions and protect them. Otherwise, every exception request becomes a tiny fork, and enough tiny forks turn back into duplicated dashboards.
Step 7: Embed the dashboard and pass the right user context
This is where the dashboard stops being a BI project and starts feeling like part of your product. A person signs into your app once, opens analytics, and sees only the right data.
Tie app authentication to analytics identity
Use your existing app login as the source of truth. If a person is authenticated in your product, that identity should flow into the embedded analytics session too.
That keeps the experience clean and reduces mismatched permissions. One sign-in. One identity. One access model.
Pass tenant and role claims safely
Send the minimum useful claims: user ID, tenant ID or allowed tenants, and any role flags that affect visibility. Anything beyond that tends to create confusion, stale mappings, or accidental logic sprawl.
Keep claims stable and auditable. If support asks why a user saw a chart last Tuesday at 9:12 a.m., you want a traceable answer.
Verify the embedded experience with real test accounts
Test with realistic personas. A single-tenant admin. A multi-tenant manager. A limited end user. Log in as each and inspect dashboards, drill-downs, exports, and filtered views.
Checkpoint: if those personas work cleanly inside the actual product, not just inside your BI admin console, you’re close.

Step 8: Test for leaks, edge cases, and maintenance pain
A dashboard that loads is not the same thing as a secure analytics feature. This step is about trying to break the setup on purpose.
Run cross-tenant access tests
Switch users. Alter URLs. Open old bookmarks. Export CSVs. Try direct report links. Test cached states after sign-out and sign-in. Think of it like checking a front door lock by jiggling the handle three extra times.
The most common leak paths are boring, not dramatic. A stale bookmark, an export job, a forgotten view. Those still count.
Check extracts, caches, and scheduled refreshes
Caching and extracts deserve special attention because tenant context can get lost in background processing. If your BI tool materializes data without preserving access scope, you can end up with reports that look right in testing and behave badly in production.
Review how scheduled refreshes work, how query caches are keyed, and whether extracted datasets preserve tenant isolation. This is especially relevant in customer-facing deployments with outside users logging into embedded dashboards.
Review admin and support access paths
Admin, support, and service accounts often need broader visibility. That’s fine, but those exceptions should be explicit, limited, and documented.
Avoid one-off overrides hidden in random views or embed code. Privileged access should be easy to audit and easy to remove.
Step 9: Troubleshoot common multi-tenant RLS problems
Even good setups hit snags. The nice part is that most failures fall into a few recognizable buckets.
Problem: users see no data
Start with the mapping table. Confirm the user identity matches the expected tenant ID exactly. Then check that session context is actually reaching the database or BI tool, and that the right role or permission rule is active.
The fastest test is simple: inspect the active identity, inspect the allowed tenants, then run one known-good query.
Problem: users see too much data
Treat this as the highest-priority bug. Check joins first, especially derived tables and summary views. Then inspect default roles, fallback logic, and any unsecured tables used by the dashboard.
Overexposure usually comes from one missing condition, one permissive join, or one data source that never got the same security treatment as the rest.
Problem: performance drops after RLS is enabled
Security rules can slow queries if tenant_id is not indexed or if the policy depends on expensive subqueries. Simplify the rule path, reduce unnecessary joins, and make sure tenant filtering happens early in query execution.
Fast matters here. If analytics feel sluggish, users stop trusting them as part of the product experience.
Problem: one tenant needs special access rules
Don’t clone the dashboard just to handle one exception. Add a controlled permission layer instead, such as feature flags, role-based dimensions, or a scoped access override in your mapping table.
The shared model should bend a little. It should not split in two.
What success looks like and what to do next
https://www.youtube.com/watch?v=vZT1Qx2xUCo
Success looks boring in the best possible way. One dashboard template, one identity flow, one access model, and no pile of near-identical dashboard copies waiting to drift apart.
Roll out in phases
Start with an internal tenant. Then move to one friendly customer. Then expand. That phased rollout keeps the blast radius small and makes testing feel manageable instead of dramatic.
A pilot done well is worth more than a big launch done fast.
Document your RLS contract
Write down the small set of rules that matter most: where tenant identity comes from, how user-to-tenant access is mapped, and where enforcement happens. Keep it short enough that your team will actually read it.
That document becomes the guardrail for every future dashboard update, embed change, and support request.
Try one thing this week
Pick one existing customer-facing dashboard and strip out the hardcoded tenant filters. Replace them with a shared version backed by real row-level access rules, then test it with one allowed tenant and one blocked tenant.
If you want the shortest path to getting this into production, try embedportal.com. It simplifies user management, passes tenant context cleanly, and helps keep RLS working across Tableau, Power BI, and AWS QuickSight without turning access control into its own side project.
Frequently Asked Questions
Is multi tenant row level security better than making one dashboard per customer?
Yes. One shared dashboard with proper row filtering is easier to maintain, easier to update, and less likely to drift into inconsistent metrics. Cloned dashboards look simpler at first and get messy fast.
Should row level security live in the database or the BI tool?
Database-level enforcement is the safest default because it protects the data before any dashboard logic runs. BI-level security can still help, but it should not be your only line of defense for customer-facing analytics.
Can one user have access to more than one tenant?
Yes. The usual pattern is a mapping table that links one user ID to multiple tenant IDs. Your security rule checks whether each row’s tenant key is in that allowed set.
What causes the most common RLS leak?
Poor joins and missing tenant filters in derived tables are common causes. Cached extracts and embed sessions with incomplete identity claims also create problems more often than expected.
Does row level security hurt dashboard performance?
It can, especially if tenant keys are not indexed or the access rule depends on complex joins. In most cases, a clean tenant key, simple policies, and careful modeling keep performance in a good place.
Can you personalize a shared dashboard without breaking security?
Yes. Use parameters for display choices like customer name, theme, or date defaults. Keep security separate from presentation so nobody can change a parameter and widen data access.
Curious how this would work on your own data?


