Row level security is the rule that decides which records each person can see inside the same dashboard. In embedded analytics, that one rule is the difference between a clean customer experience and a data leak that ruins trust fast. If your product shows dashboards to customers, partners, or internal teams, you need to understand how row level security works before you ship anything that looks polished on the surface.
What row-level security means in embedded analytics
Row level security limits data at the record level. Put simply, your dashboard can stay the same while the data inside it changes based on who signed in. One customer sees only their orders. Another sees only their stores. An account manager sees only the accounts assigned to that portfolio.
That sounds basic, but it solves one of the hardest problems in customer-facing analytics: how to serve many users from one shared reporting setup without exposing the wrong data.
In internal BI, a mistake is bad. In embedded analytics, a mistake is much worse because your customer is the one staring at the screen. If your app has a dashboard tab, row level security is not a nice extra. It is part of the product.
The simple version: one dashboard, different data for each person
Picture a Monday morning sales dashboard. A hospital group logs in and sees only hospital locations tied to that organization. A franchise owner opens the exact same embedded report and sees stores in Arizona and Nevada. An internal account manager opens that same report and sees the book of business assigned to that name.
Same dashboard. Same charts. Different rows underneath.
That is the easiest way to understand row level security. It is like a hotel key card. Every guest walks through the same lobby, but the card opens only the rooms assigned to that person. The hallway is shared. Access is not.
Why this matters more in customer-facing analytics than internal BI
Embedded analytics raises the stakes because your dashboards sit inside a multi-tenant product. Customers assume the product already knows who belongs where. If a user signs in and sees another company’s data, that trust is gone before support can draft a reply.
There is also a practical reason. Without row level security, you end up cloning dashboards per customer, per region, or per team. That gets ugly fast. Every metric change turns into a copy-and-paste project. Every new account adds setup work. Every exception becomes a one-off.
A better model is one dashboard with controlled visibility. If you want a quick grounding in how shared reporting stays private, this explainer on who gets access inside shared dashboards covers the core idea from the product side.

How row-level security actually works
Under the hood, row level security is just a filter tied to identity. The system looks at who the user is, checks what that person is allowed to see, and then applies a filter before data is returned or displayed.
The filter could be based on tenant ID, account ID, region, department, franchise, portfolio, or any other attribute that maps a user to data. The mechanics vary by stack, but the pattern is consistent: user in, rule matched, rows filtered.
The three moving parts: users, rules, and data filters
Every RLS setup has three parts.
First, the user identity. That could be an email address, an internal user ID, a group membership, or a session token passed in from your app.
Second, the rule. This is the logic that says a user with attribute X can see rows where field Y matches. Sometimes the rule is simple, like tenant_id = current_tenant. Sometimes it is messier, like this account manager can see these 43 customers across two business units.
Third, the enforcement layer. That is where the filter actually gets applied, whether in the database, semantic layer, BI tool, or app.
If any one of those parts is shaky, the whole model gets shaky.
Static RLS vs dynamic RLS
Static RLS means fixed roles or fixed filters. For example, “West Region” always sees western states, or “Franchise Group A” always sees a defined list of stores. It works, but it gets hard to manage when you have lots of customers and constant access changes.
Dynamic RLS calculates access at runtime based on user attributes. A signed-in user gets matched to a tenant, region, or permissions table, and the filter is applied automatically.
For embedded analytics, dynamic RLS is usually the better fit. If your product has dozens or hundreds of customers, static rules become an admin chore. Dynamic rules scale better because access changes live in data, not in a growing pile of hand-built roles.
Row-level security vs column-level security
These two get mixed up all the time.
Row level security controls which records appear. Column-level security controls which fields appear. So RLS decides whether a user can see Account 123 at all, while column security decides whether that user can see fields like margin, salary, or internal notes.
You often need both. But they solve different problems, and treating one as a substitute for the other is a mistake.
Where RLS gets enforced in an embedded analytics stack
Row level security can be enforced in several places: your database, your semantic model, your BI tool, or your application layer. The safest pattern usually pushes enforcement as close to the data as possible.
Why? Because every layer farther away from the source creates another chance to return too much and hide it later.
Database-level RLS
At the database level, systems like PostgreSQL support policies that filter rows before the query result leaves the database, as described in Postgres Row Level Security documentation. Warehouse-native controls can do something similar in platforms like Redshift, where fine-grained row-level access control can be tied to session context.
This is strong security because the database itself refuses to hand over disallowed rows. The catch is complexity. Database-level policies can be harder to design, test, and align with BI tools, especially when embedded identity has to be translated into database session context.
Still, if the data is sensitive and the blast radius is high, database-level enforcement is often worth the extra design effort.
BI-tool-level RLS in Tableau, Power BI, and QuickSight
This is where many teams start. Tableau, Power BI, and QuickSight all support user-based filters in the analytics layer. Usually that means roles, mappings, security tables, or dataset rules.
This approach is popular because it fits the tools teams already use. It is also easier to reason about than custom app logic scattered across services. If your setup lives mostly inside one BI platform, native RLS is often the fastest path to something solid.
That said, native RLS is only as good as the identity passed into it. If your embedded session loses the real user context, the rule can stop working even though the dashboard still loads.
App-layer filtering and why it is not enough on its own
Here’s the blunt version: app-only filtering is not security.
If your application fetches a broad result set and then hides rows in the UI, the underlying data was still exposed to the app layer. That leaves room for bugs, logging leaks, export mistakes, and API paths that skip the hiding step.
App-layer filtering can improve user experience. It can even add another layer of restriction. But it should not be the only layer standing between one customer and another customer’s data.

Common RLS patterns for embedded analytics
Most embedded products end up using a handful of familiar access patterns. Once you can recognize them, architecture choices get easier.
Tenant-based filtering
This is the standard SaaS pattern. Every row includes a tenant ID, account ID, or customer ID. Each user belongs to one tenant, and the filter matches rows to that value.
It is clean, scalable, and easy to explain. If your product is truly one-customer-per-tenant, this is usually the right starting point.
A lot of teams looking for a way to isolate customers without making duplicate dashboards land here because it keeps both the data model and the analytics model simple.
Role- and region-based filtering
Some access rules depend less on customer ownership and more on organizational structure. Sales leaders see a region. Franchise operators see a territory. Customer success managers see assigned accounts. Partners see only accounts linked to that partner.
This is common in internal portals and partner-facing analytics. It works well, but the rules tend to multiply over time. A simple region model can turn into region plus business unit plus exception list before anyone notices.
User-to-entity mapping tables
This pattern uses a security table that maps each user to one or more entities, such as customers, stores, departments, clinics, or accounts. The dashboard query joins or relates to that table and filters rows based on matching records.
This is one of the most practical patterns for embedded analytics because it handles many-to-many access cleanly. If a user should see seven stores or twelve client accounts, a mapping table handles that without inventing a new role for every case.
Hierarchical access
Hierarchical access means a user can see not only direct entities, but also child entities underneath. A district manager sees all stores in a district. A parent company sees all subsidiaries. A hospital network sees each individual location.
This is where RLS starts getting tricky. Hierarchies can change. Mergers happen. Territories shift. One parent account becomes two. If your access model includes inherited visibility, make sure the hierarchy is explicit in the data model and not buried in one-off code.
How RLS works in Power BI for embedded use cases
Power BI comes up in almost every row level security discussion because it has strong built-in support and a lot of embedded deployments. The concepts are straightforward. The identity handling is where things get slippery.
Roles, DAX filters, and security tables
In Power BI, RLS is typically defined through roles and DAX filters in the semantic model, as shown in Microsoft’s guide to row-level security with Power BI. A role might say that a user can only see rows where Region = “West”, or where CustomerID exists in a related entitlement table.
Security tables are common in real products. Instead of hardcoding rules into every report, you maintain a table that maps users to allowed entities, then let relationships and DAX apply the filter. That keeps access in data, which is usually easier to update than editing report logic one role at a time.
Dynamic functions like USERNAME() and USERPRINCIPALNAME()
Power BI includes functions like USERNAME() and USERPRINCIPALNAME() that return the current identity inside the model. In plain English, these functions answer the question: who is this session acting as?
That is useful for dynamic RLS, but embedded setups complicate it. External users may not have native Power BI accounts. B2B guest identities can behave differently. Service principal setups can shift how identity is passed. Microsoft documents several considerations for external users and dynamic RLS behavior.
If the identity coming into the report does not match the identity stored in your security table, the result is often a blank dashboard. Better than leaked data, but still frustrating.
What changes when you embed Power BI
Embedded Power BI adds another layer: app-owned versus user-owned embedding. In app-owned embedding, your application authenticates and then passes effective identity for the end user. In user-owned embedding, the signed-in person authenticates directly with Power BI context.
This matters because the dashboard can render perfectly while still evaluating RLS against the wrong identity. Development environments hide this problem all the time. An admin account sees everything, tests pass, and production goes live with external users mapped incorrectly.
If you are dealing with customer logins rather than internal Microsoft identities, your setup for handling outside users without breaking access rules becomes just as important as the DAX itself.

How RLS works in Tableau for embedded analytics
Tableau usually handles row level security through user filters, group membership, or entitlement tables tied to usernames. The approach feels different from Power BI, but the core logic is the same: identify the user, map allowed values, filter rows.
User filters and entitlement tables
Tableau user filters can restrict what a signed-in user sees based on Tableau username or group. For more flexible access, many teams use an entitlement table that joins usernames or group IDs to allowed customers, stores, or departments.
That approach is practical because it centralizes permissions in a table instead of burying them inside workbook-level logic. If access changes often, entitlement tables are easier to maintain than a growing stack of custom workbook filters.
Live connections vs extracts
This choice matters more than it first appears. With live connections, Tableau queries the source in real time, so access changes can take effect quickly if the underlying rules update. With extracts, the data is cached on a refresh schedule, which can delay changes in what users see.
The catch is not usually the row filter itself. The catch is timing. If a customer loses access at 2:00 PM and the extract refreshes overnight, you need to know exactly what stays available in that gap.
Embedded Tableau considerations
In embedded Tableau, authentication and session identity have to line up with the viewer context. Trusted authentication, connected apps, and embedded session handling all affect whether the workbook evaluates the right person.
This is one of those places where keeping access logic centralized instead of spread across systems saves a lot of pain. If your app user, Tableau user, and entitlement record drift apart, the dashboard either goes blank or shows too much.
How RLS works in Amazon QuickSight for embedded analytics
QuickSight handles row level security with rules datasets and identity-aware access patterns. For teams already building on AWS, that can be a good fit because the controls sit close to the rest of the stack.
Rules datasets and tag-based access patterns
A common QuickSight pattern is a permissions dataset that maps users or groups to allowed values, such as region, customer, or account. QuickSight uses that dataset to restrict rows in the main dataset.
In some setups, tag-based session attributes can help drive access as well. The idea is the same as elsewhere: pass user context, match it to rules, filter rows before the dashboard renders.
Namespaces, users, and multi-tenant isolation
QuickSight namespaces can help separate users in multi-tenant embedded environments. If your product serves many customers, namespace design can reduce accidental overlap and simplify isolation between customer groups.
But namespaces are not a complete replacement for row filters. You still need clean access rules inside datasets. Separation at the identity level helps, but it does not automatically solve entity-level visibility.
Common QuickSight gotchas
QuickSight issues often look simple from the outside. A dashboard loads blank. A customer sees too little. An admin sees everything. The root cause is usually one of three things: stale provisioning, mismatched identifiers, or an incomplete permissions dataset.
This is especially common when user creation happens in one system and security rules get updated in another. If those workflows drift, your dashboard becomes the place where the mismatch shows up.
The biggest mistakes that break row-level security
Most RLS failures do not come from one dramatic bug. They come from ordinary shortcuts that seem harmless until real users hit edge cases.
Treating RLS as a dashboard setting instead of a system design choice
This is the big one. Secure filtering depends on identity, data modeling, provisioning, and enforcement. It is not just a checkbox in Tableau, Power BI, or QuickSight.
If your app signs users in one way, your BI tool recognizes users another way, and your warehouse stores permissions in a third format, row level security becomes fragile by default.
Testing with admin accounts only
Admin accounts are terrible test users for RLS because admins often bypass restrictions. Everything looks fine. Every chart fills in. Nobody notices a problem until a customer logs in and sees a blank view or the wrong records.
Test with real permission shapes: one-tenant users, multi-entity users, users with no access, partner users, and edge-case hierarchies. That is where leaks and silent failures show up.
Forgetting nested access and edge cases
Real products are messy. Shared accounts exist. Customers merge. Parent-child orgs change. One user can belong to multiple regions or temporarily manage another team.
If your model only handles the clean demo case, it will break on a random Tuesday at 4:45 PM, usually right after a sales call promises “flexible account rollups” to a large prospect.
Letting user management drift away from security rules
This happens slowly. Your app has a user record. Your BI platform has a user record. Your security table has another record. One says jane@company.com. Another says Jane.Company. Another uses an internal UUID. Then a customer asks why Jane sees nothing.
Access control is only as reliable as the user matching behind it. This is why teams eventually need a cleaner way to keep BI users and permissions in sync, not just better dashboard settings.
Best practices for building RLS that stays manageable
The goal is not just secure RLS. The goal is secure RLS that still works after customer count triples and your original setup choices start showing strain.
Start with a clean entitlement model
Decide early what grants access. Tenant ID? Region? Account list? Group membership? Hierarchy? Pick the smallest rule set that reflects reality.
Clean rules beat clever rules every time. If access can be described in one clear model, maintenance stays sane. If access depends on nested exceptions and tool-specific hacks, your future self inherits a mess.
Keep security logic in one place when possible
Duplicating the same rule in app code, warehouse SQL, and BI dashboards creates drift. One layer gets updated. Another does not. Nobody notices until numbers disagree.
Sometimes you need layered enforcement for safety, especially in high-risk environments. But the source of truth for who can see what should still live in one place, ideally in data you can audit and update.
Audit, monitor, and retest regularly
RLS is not set-and-forget. Access changes. Customer structures change. User identities change. Dashboards change.
Use view-as-user testing where the platform supports it. Log access changes. Check onboarding and offboarding paths. Review broad-access accounts on a schedule. If your external user model is growing, these practical habits for keeping access rules healthy become less of a nice idea and more of a survival tool.
Design for least privilege
Default-deny is the right instinct here. If the system cannot match a user to an access rule, the safest result is no data.
Blank access is annoying, but accidental broad access is worse. Make the product fail closed, not open.
FAQs and common misconceptions about row level security
Is row-level security enough by itself?
No. RLS is a core control, but it is not the whole security model. You still need authentication, tenant-aware identity handling, column protections where needed, and auditing. Think of RLS as the lock on one door, not the entire building.
Does RLS hurt performance?
It can, depending on how the rules are designed. A simple tenant_id filter is usually easy to scale. A maze of many-to-many joins, nested hierarchies, and custom logic can slow things down. Good modeling matters as much as the RLS feature itself.
Can one user belong to multiple customers or regions?
Yes. That is common in partner, franchise, and account management scenarios. The clean way to support it is a many-to-many entitlement model, usually through a mapping table, rather than inventing a separate role for every combination.
What happens if a user has no matching RLS rule?
The safest outcome is no data returned. In practice, a blank dashboard is often the first clue that the signed-in identity does not match the security mapping. It feels like a bug, but it is often a user-to-rule mismatch.
Is hiding rows in the UI the same as row-level security?
No. Hiding rows after the app already fetched the data is presentation logic, not real security. True RLS restricts what gets returned in the first place.
Choosing the right RLS approach for your embedded analytics stack
There is no single perfect pattern, but there is usually a clear best fit once you look at your product shape and operational reality.
If your product is single-tenant per customer
Keep it simple. If each customer has a clean tenant boundary and users stay inside that boundary, tenant-based filtering is usually enough. Fewer rules, fewer edge cases, less ongoing maintenance.
You still need to pass identity correctly and test it end to end. But the model itself can stay refreshingly boring.
If your product is multi-tenant with shared datasets
Dynamic RLS becomes the default. Shared datasets are efficient, but they put more pressure on identity mapping and entitlement design. Every user session needs to resolve to the right set of allowed rows, every time.
This is also where pushing enforcement closer to the data starts looking smarter, especially if customer sensitivity is high or permission logic gets complicated.
If your team uses Tableau, Power BI, or QuickSight today
Use native RLS where it fits. It is usually the fastest way to get secure embedded reporting off the ground. Push enforcement closer to the data when risk is high. Avoid rebuilding the same user permissions by hand in multiple tools.
And be honest about the operational burden. The dashboard is only half the problem. Provisioning users, passing identity, syncing entitlement tables, and debugging blank dashboards is the other half. If you are evaluating tooling, this guide to choosing an embedded analytics setup that fits your product is a useful next step.
Try a simpler way to manage users and row-level security
Here’s the part that usually causes the real headaches: user provisioning, permissions sync, and keeping RLS accurate across Tableau, Power BI, and QuickSight as customers change, teams move, and access rules pile up.
You can absolutely build all of that by hand. Plenty of teams do. It just tends to turn into a lot of glue code, admin work, and “why is this dashboard blank for only this one customer” debugging.
embedportal.com is built for exactly that operational layer. It simplifies user management and row level security for embedded analytics, so you can stop stitching together identity mapping, dashboard access, and platform-specific permissions one workflow at a time. If your team is juggling external users and customer-facing dashboards this week, try embedportal.com and see how much cleaner RLS feels when the user management piece stops fighting you.
Curious how this would work on your own data?


