Skip to content
Reeflow
Start Building

Security

Reeflow provides a granular security model for data access, designed for both internal BI and embedded analytics.

Analytics platforms serve diverse users (internal teams, external customers, partners) each with different access requirements. Reeflow’s security model is flexible enough to support this diversity while remaining secure by default to prevent data leaks.

This page explains how Reeflow secures data access (SQL queries). You can condition access per user on connections, tables, columns (CLS), and rows (RLS). While some concepts also apply to other API operations, the examples here are specific to data queries.

Reeflow’s security model is built on four principles:

  • Deny by default: Users cannot access data unless explicitly permitted. No access is granted implicitly.
  • Least privilege: Grant the minimum access needed for each user. Granular scopes (connections, tables, columns, rows) let you define exactly what each user can access.
  • Role-based access control (RBAC): Permissions are grouped into roles, which are then assigned to users. This simplifies management and makes it easier to audit who has access to what.
  • Policy-level control: Define access rules at the role level, not per workbook or chart. A rule like “only access data from this organization” applies to all data access automatically. This prevents data leaks from configuration mistakes.

A principal is any entity that accesses Reeflow. Reeflow recognizes three types of principals, each authenticated differently:

  • Platform users: Team members who sign in to the Console. Authenticated via Reeflow or your identity provider.
  • Embedded users: End users in your application who view embedded analytics. Manage these through the API (recommended) or the Console.
  • API keys: Credentials for programmatic access, such as creating session tokens or running scheduled jobs. Support HMAC signatures or Basic authentication.

Embedded users authenticate using session tokens: short-lived JWTs generated by your backend. Because these tokens are exposed to the browser, they expire quickly (typically within minutes) to limit the window of potential misuse.

Roles are the central mechanism for controlling access. A role bundles permissions with user attribute rules, allowing you to define both what a principal can do and what context they must provide. Roles can be assigned to teams, API keys, and embedded users.

For details on role components, permissions, and how to configure them, see Roles.

User attributes are key-value pairs attached to principals. They provide the dynamic context that makes row-level and column-level security possible.

For example, a tenant_id attribute lets you filter data so each customer only sees their own records, without creating separate roles for each customer.

When a principal makes a request, Reeflow evaluates it through two stages: authorization and data filtering.

flowchart TD
    subgraph Auth [**Authorization**]
        B[Resolve roles assigned to principal] --> C[Merge permissions from all roles]
        C --> D{Action permitted?}
    end

    subgraph DF [**Data Filtering**]
        F[Merge user attributes from principal and roles] --> G[Restrict tables to allowed list]
        G --> H[Restrict columns to allowed list]
        H --> I[Inject row filters into query]
        I --> J[Return filtered data]
    end

    D -->|Yes| F
    D -->|No| E[Denied]

The first stage determines whether the principal can perform the requested action:

  1. Resolve roles: Identify which roles the principal can assume. A principal can only assume a role if they provide all of its required user attributes.
  2. Merge permissions: Combine permissions from all assumable roles. Access is granted if any role permits the action on the target resource.
  3. Check action: Verify the requested action is permitted. If not, the request is denied.

For details on how roles resolve and permissions merge, see Roles.

Once authorization succeeds, the second stage determines what data the principal can see. This only applies to data queries:

  1. Resolve user attributes: Merge principal user attributes with role fixed user attributes. Fixed values take precedence.
  2. Apply table scope: Remove any tables not in the allowed list from the query.
  3. Apply column scope: For each table, restrict visible columns to the allowed list.
  4. Apply row filters: Inject row-level constraints into the query using resolved user attributes.

After these steps, the query executes against the data source with all filters applied. The principal only sees data they are explicitly permitted to access.

Query permissions control data access at four levels, from broadest to most granular.

LevelWhat it controlsWhen to useExample
ConnectionWhich connections can be queriedSeparate data sources by use case or environmentAllow production queries only for internal dashboards
TableWhich tables can be queried within allowed connectionsRestrict access to entire datasetsPrevent external users from querying internal analytics tables
Column (CLS)Which columns are visible within allowed tablesHide sensitive fields without blocking the tableHide salary and ssn columns from the employees table
Row (RLS)Which rows are returned, based on dynamic filtersIsolate data by tenant, user, or attributeFilter orders by tenant_id so each customer sees only their own

All four levels apply together. A query must target an allowed connection, then an allowed table, then only permitted columns are returned, and finally row filters restrict which records appear.

ScenarioResult
No assumable roles (missing required user attributes)403 Forbidden
Action not permitted on connection403 Forbidden
Query references a table not in allowed list400 Bad Request
Query explicitly selects a column not in allowed list400 Bad Request
No rows match row-level security filtersSuccess with empty result set