Secure multi-tenant embedded analytics with JWT authentication, row-level security, and isolation patterns. Technical guide for SaaS teams.
When you embed analytics into your SaaS product, you're not just dropping a dashboard into a web page. You're creating a security perimeter that must authenticate users, isolate their data, and prevent cross-tenant access—all while maintaining the performance expectations your customers demand.
The stakes are high. A single misconfigured authentication layer can expose customer data across your entire product. Worse, a slow or brittle auth system becomes a bottleneck that degrades the analytics experience your users depend on. You need a strategy that's simultaneously bulletproof and invisible.
This guide walks through the technical architecture for embedding analytics securely in multi-tenant SaaS applications. We'll focus on three core pillars: JSON Web Tokens (JWT) for stateless authentication, row-level security (RLS) for data isolation, and multi-tenancy patterns that keep customer data separated at every layer.
A JSON Web Token is a cryptographically signed, URL-safe string that contains claims about a user's identity and permissions. Instead of your analytics platform making a round-trip call to your authentication server for every request, the token itself carries all the information needed to verify who the user is and what they can access.
JWT has three parts, separated by periods:
For embedded analytics, JWT solves a critical problem: you can't rely on browser cookies or session storage alone when your analytics is embedded in your product. The analytics platform (whether it's D23's managed Apache Superset or another solution) needs to trust the identity assertion without calling back to your auth server on every single query.
When your user logs into your SaaS product, your backend generates a JWT with claims that identify them, their organization, and their role. That token gets passed to the embedded analytics frontend. The analytics platform validates the signature (proving it came from your backend) and extracts the claims to enforce access control.
The key advantage: stateless authentication. The analytics platform doesn't need to maintain session state or make synchronous calls to verify identity. The token is self-contained and cryptographically verifiable.
JWT handles user identity. Row-level security (RLS) handles data isolation. These two mechanisms work together but solve different problems.
RLS is a database-level feature that filters query results based on the user's identity or attributes. Instead of trusting your analytics application layer to enforce access control, you push that enforcement into the database itself. This is the "defense in depth" principle: even if a bug exists in your application code, the database refuses to return unauthorized data.
Here's how it works in practice:
For example, imagine a SaaS product serving multiple e-commerce companies. Company A and Company B both use the embedded analytics. A user from Company A should never see Company B's revenue data, even if they somehow craft a SQL query that tries to access it.
Without RLS, you rely entirely on your application layer to filter results. With RLS, the database itself enforces the boundary:
CREATE POLICY company_isolation ON orders
FOR SELECT
USING (company_id = current_user_id);
This policy means: "Only return rows where the company_id matches the current user's company_id." The database enforces this on every query, regardless of what SQL the user submits.
RLS is not unique to one database vendor. Supabase's authentication documentation shows how to integrate JWT claims with PostgreSQL RLS policies. Multi-tenant database architectures explain the same pattern across different platforms. The principle is universal: push security enforcement as close to the data as possible.
Multi-tenancy is the structural pattern that allows you to serve multiple customers from a single analytics instance. There are three main approaches, each with different security and complexity trade-offs.
In this pattern, all customers' data lives in one database, but each customer gets their own schema (namespace). Queries are routed to the correct schema based on the user's JWT claims.
Pros:
Cons:
All customers' data lives in the same tables, distinguished by a tenant ID column. Row-level security policies filter data based on the tenant ID in the user's JWT claims.
Pros:
Cons:
This is the pattern most commonly recommended for embedded analytics at scale. Multi-tenant analytics for healthcare SaaS discusses this approach using encrypted URL embedding with signed tokens and server-side RLS. Best practices for dynamic pages and multi-organization authentication show JWT claims being used for user organizations and roles with database RLS integration.
Each customer gets their own database instance. This is the strongest isolation model but operationally the most complex.
Pros:
Cons:
Most SaaS teams start with shared database + RLS and only move to separate databases if they have extreme isolation requirements (often driven by regulatory or compliance needs).
The power of JWT lies in the claims you include in the token. These claims become the input to your RLS policies and your analytics platform's access control decisions.
A well-designed JWT for embedded analytics includes:
Core identity claims:
sub (subject): Unique user IDiat (issued at): Token creation timestampexp (expiration): When the token becomes invalidiss (issuer): Your backend serviceTenant/organization claims:
org_id: The customer's organization IDtenant_id: Equivalent to org_id in some systemsworkspace_id: If your SaaS supports multiple workspaces per customerRole and permission claims:
role: User's role (admin, analyst, viewer)permissions: Array of specific permissions ("view_dashboards", "edit_queries", etc.)dataset_ids: Array of specific datasets the user can accessCustom analytics claims:
department: If analytics should be filtered by departmentregion: If data is region-specificcost_center: For cost allocation across your productHere's an example JWT payload for a user embedded in your SaaS:
{
"sub": "user_12345",
"org_id": "org_acme",
"role": "analyst",
"permissions": ["view_dashboards", "edit_queries"],
"dataset_ids": ["sales_data", "customer_data"],
"iat": 1704067200,
"exp": 1704153600,
"iss": "https://yoursaas.com"
}When this token reaches your analytics platform, it can immediately determine:
The RLS policy in your database uses the org_id claim to filter rows. The analytics platform uses the permissions and dataset_ids claims to control UI visibility and API access.
RLS policies are database rules that filter query results before they're returned to the application. They're the enforcement mechanism that makes multi-tenancy actually secure.
The basic structure in PostgreSQL (which powers Supabase Auth and RLS integration) looks like this:
CREATE TABLE orders (
id UUID PRIMARY KEY,
org_id UUID NOT NULL,
customer_id UUID NOT NULL,
amount DECIMAL,
created_at TIMESTAMP
);
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
CREATE POLICY org_isolation ON orders
FOR SELECT
USING (org_id = current_setting('app.current_org_id')::uuid);
CREATE POLICY org_isolation_insert ON orders
FOR INSERT
WITH CHECK (org_id = current_setting('app.current_org_id')::uuid);These policies say: "Only show/insert orders where the org_id matches the current organization." The current_setting() function retrieves the org_id from your application context, which you set when the user's JWT is validated.
In practice, your analytics platform (like D23's managed Apache Superset) would:
org_id claimThis happens for every query, every time. It's not a one-time check; it's continuous enforcement.
JWT security depends entirely on proper signing and validation. There are two common approaches: symmetric signing (shared secret) and asymmetric signing (public/private key pair).
Your backend and analytics platform share a secret key. The backend uses it to sign JWTs; the analytics platform uses the same key to verify the signature.
When to use:
When to avoid:
Your backend uses a private key to sign JWTs. The analytics platform uses your public key to verify them. The private key never leaves your backend.
When to use:
When to avoid:
For embedded analytics in a multi-tenant SaaS, asymmetric signing is generally recommended. It lets you maintain tighter control over who can verify your JWTs.
Validation should always check:
exp claim)iss claim matches your expected issuerIf any check fails, reject the token immediately. Don't grant access.
A JWT is only as secure as the channel through which it's delivered. If you send it in plain HTTP, over an unencrypted connection, or store it insecurely, all the cryptography is worthless.
In transit:
At rest:
In the embedded analytics context:
Top 21 Authorization Systems and Tools for 2025 discusses tools like ZITADEL for multi-tenant SaaS authentication and authorization, which can integrate with your analytics platform's JWT requirements.
Once you have authentication (JWT) and data isolation (RLS) in place, the next layer is fine-grained access control: limiting which dashboards, datasets, and queries specific users can access.
This happens at the application layer, not the database layer. While RLS prevents unauthorized data access at the database level, you still want to hide dashboards and datasets that users shouldn't see.
Common patterns:
Role-based access control (RBAC): Users have roles (admin, analyst, viewer). Each role has a set of permissions. Your analytics platform checks the user's role before showing UI elements or allowing actions.
Attribute-based access control (ABAC):
Access decisions are based on attributes in the JWT (department, region, cost_center, etc.). A dashboard might only be visible to users with department = "sales".
Dataset-level permissions:
Include a dataset_ids array in the JWT. Only show dashboards and queries that use datasets in that array.
For example, a user from the sales department should see sales dashboards. A user from finance should see financial dashboards. This is enforced by including department: "sales" in the JWT and checking it in the analytics platform's permission logic.
The principle: the JWT carries all the information needed to make access decisions. The analytics platform (and database via RLS) use that information to enforce boundaries.
Tokens have expiration times for security: if a token is compromised, the window of exposure is limited. But you can't ask users to log in again every 15 minutes.
The solution: refresh tokens. A refresh token is a longer-lived token that can be used to obtain new short-lived access tokens.
Flow:
For embedded analytics, this typically means:
This balances security (short-lived access tokens) with usability (users don't need to re-authenticate constantly).
D23's managed Apache Superset provides native support for JWT-based authentication and RLS integration. Here's how the pieces fit together:
Backend integration: Your SaaS backend generates a JWT with the user's org_id, role, and dataset permissions
Embedded URL generation: Your backend creates an embedded analytics URL with the JWT as a parameter or in a secure cookie
Superset validation: Superset validates the JWT signature and extracts claims
RLS enforcement: Superset passes the org_id claim to your database, which enforces RLS policies
UI filtering: Superset hides dashboards and datasets based on the user's permissions
This architecture ensures that:
Pitfall 1: Storing secrets in code Never commit your JWT signing keys to version control. Use environment variables, secret management systems (AWS Secrets Manager, HashiCorp Vault), or your SaaS provider's built-in secret management.
Pitfall 2: Not validating token expiration
Always check the exp claim. A token that's been sitting on disk for a month should be rejected, even if the signature is valid.
Pitfall 3: RLS policies that are too permissive If your RLS policy accidentally allows cross-tenant access, it defeats the entire purpose. Test RLS policies thoroughly. Try to query data you shouldn't have access to; verify the database rejects it.
Pitfall 4: Forgetting to set the org_id context If you validate the JWT but forget to set the org_id in the database session, RLS policies won't work. Make this a required step in your authentication middleware.
Pitfall 5: Mixing authentication and authorization Authentication answers "Who are you?" (JWT validates this). Authorization answers "What are you allowed to do?" (RLS and fine-grained policies enforce this). Don't conflate them. A valid JWT proves identity; it doesn't automatically grant access to all data.
Pitfall 6: Not rotating keys If you use symmetric signing (shared secret), rotate the key periodically. If a key is compromised, you have a limited window to rotate it before old tokens become invalid. Plan for this operationally.
Pitfall 7: Overly complex JWT payloads Don't put everything in the JWT. Keep it to identity, organization, role, and essential context. Large JWTs add latency and increase the surface area for bugs. If you need to grant access to 500 datasets, use a permissions service instead of listing them all in the JWT.
Security is not a one-time implementation; it's ongoing. You need visibility into what's happening.
Log and monitor:
Set up alerts for:
Multi-tenant embedding in Amazon QuickSight discusses implementing RLS with tag-based rules and dataset parameters, and monitoring is a critical part of that implementation.
As your SaaS grows, authentication and RLS need to scale with it.
Token generation at scale:
RLS policy performance:
Database connection pooling:
Multi-tenant analytics for healthcare SaaS discusses scaling embedded analytics with encrypted URL embedding and server-side RLS, which applies to any SaaS vertical.
If you're serving regulated industries (healthcare, finance, data residency requirements), authentication and RLS design affects your compliance posture.
HIPAA (healthcare):
GDPR (EU data protection):
Data residency:
Embed multi-tenant analytics in applications with Amazon QuickSight discusses how to use QuickSight's features for RLS and custom permissions in compliance-sensitive scenarios.
Here's a step-by-step approach to implementing JWT and RLS for embedded analytics:
Phase 1: Foundation
Phase 2: Data Isolation
Phase 3: Fine-Grained Access Control
Phase 4: Production Hardening
Phase 5: Scaling
Secure embedded analytics in a multi-tenant SaaS requires coordinating three mechanisms: JWT for stateless authentication, RLS for database-level data isolation, and fine-grained access control at the application layer.
JWT provides the identity assertion. RLS enforces the boundary. Application-layer policies control visibility and permissions. Together, they create a security model that's both robust and performant.
The key is treating security as architectural, not an afterthought. Design your JWT schema to carry the information your analytics platform needs. Design your RLS policies to be simple and testable. Design your application layer to verify every access decision.
When these three layers work together, you get a system where users can't forge authentication, can't access unauthorized data, and can't see dashboards they shouldn't see. That's the foundation for trustworthy, scalable embedded analytics.
D23's managed Apache Superset is built with this architecture in mind. It handles JWT validation, integrates with RLS policies, and provides the fine-grained access control your embedded analytics needs. Whether you're building on D23 or another platform, the principles outlined here apply: push authentication as close to the user as possible, push data isolation as close to the database as possible, and monitor everything in between.