Skip to main content

Use case

You want each user’s queries to run under their own database identity using OAuth tokens managed by Cube Cloud. When a user’s token is unavailable or expired, Cube falls back to a service account so that connectivity checks and background operations still work. This pattern applies to any data source that supports OAuth, including Databricks and Snowflake. Because every user connects with different credentials, you also need per-user caching for data model compilation results and query orchestrator state. Without this, one user’s cached connection could leak to another.

Prerequisites

  • A Cube Cloud deployment connected to an OAuth-capable data source
  • OAuth configured in your data source so that Cube Cloud can obtain per-user tokens (via the User Credentials feature)
  • A service account credential (token or password) stored as an environment variable for fallback connectivity
The service account credential is used only as a fallback for Cube’s internal liveness checks and background operations. Grant it the minimum permissions necessary — ideally read-only access to the required schemas — to limit exposure if the credential is compromised.

Configuration

The configuration uses three options from the configuration file reference:
  • driver_factory — dynamically selects the authentication credential per request
  • context_to_app_id — ensures data model compilation results are cached per user, not globally
  • context_to_orchestrator_id — gives each user their own query orchestrator instance (database connections, execution queues, pre-aggregation table caches)

Environment variables

Set the environment variables for your data source. The examples below show Databricks and Snowflake; adapt them to your specific setup.
CUBEJS_DB_TYPE=databricks-jdbc
CUBEJS_DB_DATABRICKS_URL=jdbc:databricks://dbc-XXXXXXX-XXXX.cloud.databricks.com:443/default;transportMode=http;ssl=1;httpPath=sql/protocolv1/o/XXXXX/XXXXX;AuthMech=3;UID=token
CUBEJS_DB_DATABRICKS_TOKEN=dapi_service_account_token
CUBEJS_DB_DATABRICKS_ACCEPT_POLICY=true
# Optional: specify a catalog
CUBEJS_DB_DATABRICKS_CATALOG=my_catalog

Python

Select the tab matching your data source:
cube.py
from cube import config
import os


@config("driver_factory")
def driver_factory(ctx: dict) -> dict:
    # Extract the Cube Cloud security context, which contains
    # per-user OAuth credentials when available
    security_context = ctx.get("securityContext") or {}
    cube_cloud = security_context.get("cubeCloud") or {}
    user_creds = cube_cloud.get("userCredentials") or {}
    databricks_creds = user_creds.get("databricks") or {}

    # Only use the OAuth token when the credential status is "active".
    # An expired or revoked token falls back to the service account.
    status = databricks_creds.get("status")
    access_token = databricks_creds.get("accessToken")
    oauth_token = access_token if status == "active" else None

    return {
        "type": "databricks-jdbc",
        "url": os.environ["CUBEJS_DB_DATABRICKS_URL"],
        # Prefer the user's OAuth token; fall back to the service account token
        "token": oauth_token or os.environ["CUBEJS_DB_DATABRICKS_TOKEN"],
        "acceptPolicy": True,
        "catalog": os.environ.get("CUBEJS_DB_DATABRICKS_CATALOG"),
    }


@config("context_to_app_id")
def context_to_app_id(ctx: dict) -> str:
    # Cache data model compilation results per user so that each user's
    # connection settings are isolated
    username = (
        ctx.get("securityContext", {})
        .get("cubeCloud", {})
        .get("username", "default")
    )
    return f"CUBE_APP_{username}"


@config("context_to_orchestrator_id")
def context_to_orchestrator_id(ctx: dict) -> str:
    # Give each user a separate orchestrator instance (DB connections,
    # execution queues, pre-aggregation caches)
    username = (
        ctx.get("securityContext", {})
        .get("cubeCloud", {})
        .get("username", "default")
    )
    return f"CUBE_APP_{username}"

JavaScript

cube.js
module.exports = {
  driverFactory: ({ securityContext }) => {
    // Extract the Cube Cloud security context, which contains
    // per-user OAuth credentials when available
    const cubeCloud = securityContext?.cubeCloud ?? {};
    const userCreds = cubeCloud.userCredentials ?? {};
    const databricksCreds = userCreds.databricks ?? {};

    // Only use the OAuth token when the credential status is "active".
    // An expired or revoked token falls back to the service account.
    const oauthToken =
      databricksCreds.status === "active"
        ? databricksCreds.accessToken
        : null;

    return {
      type: "databricks-jdbc",
      url: process.env.CUBEJS_DB_DATABRICKS_URL,
      // Prefer the user's OAuth token; fall back to the service account token
      token: oauthToken || process.env.CUBEJS_DB_DATABRICKS_TOKEN,
      acceptPolicy: true,
      catalog: process.env.CUBEJS_DB_DATABRICKS_CATALOG,
    };
  },

  // Cache data model compilation results per user so that each user's
  // connection settings are isolated
  contextToAppId: ({ securityContext }) => {
    const username = securityContext?.cubeCloud?.username ?? "default";
    return `CUBE_APP_${username}`;
  },

  // Give each user a separate orchestrator instance (DB connections,
  // execution queues, pre-aggregation caches)
  contextToOrchestratorId: ({ securityContext }) => {
    const username = securityContext?.cubeCloud?.username ?? "default";
    return `CUBE_APP_${username}`;
  },
};

How it works

  1. User makes a request — Cube Cloud attaches the user’s OAuth credentials to securityContext.cubeCloud.userCredentials.<data_source> (e.g., .databricks or .snowflake).
  2. driver_factory resolves the credential — If the credential status is active, the user’s OAuth token is used. Otherwise, Cube falls back to the service account credential stored in environment variables.
  3. Per-user caching — context_to_app_id and context_to_orchestrator_id return a unique key per username. This means each user gets their own:
    • Data model compilation cache
    • Query orchestrator (database connection pool, execution queues, pre-aggregation table cache)
    Without these overrides, Cube would share a single cached connection across all users, causing one user’s credentials to be reused for another user’s queries.

Adapting to other data sources

The same pattern works with any OAuth-capable data source. To adapt the examples above:
  1. Change the credential key in userCredentials to match your data source (e.g., userCredentials.databricks, userCredentials.snowflake)
  2. Update the driver_factory return value with the correct type and driver-specific options for your data source
  3. Keep context_to_app_id and context_to_orchestrator_id as-is — they are data-source-agnostic
See the data sources reference for driver-specific configuration options.