GaurGaur docs

Multi-tenant SaaS

Auth context for the workspace, request context for the end user. The pattern for a customer-facing dashboard.

Use case

A "Manager Dashboard" SaaS serves multiple cafe chains. Each chain gets one API key; within a chain, many store managers each see only their own store's data. Two layers of context:

  • Auth context is attached to the API key. Same for every call that key makes; the caller can't change it. Use it for the workspace / chain.
  • Request context is sent by your backend on each call. Use it for the end user.

The user can't see request context and can't set it. The frontend talks to your backend; your backend (with the authenticated session in hand) talks to Gaur.

1. Add RLS to the contract

Scope store_performance by both workspace (from auth) and store (from request):

contracts/store_performance.json (excerpt)
{
  "rls": {
    "sql": "s.workspace_id = {{ workspace_id }} AND s.store_id = {{ store_id }}",
    "parameters": {
      "workspace_id": "auth.workspace_id",
      "store_id":     "request.store_id"
    }
  }
}

Gaur injects both values as SQL parameters into every query against the contract. Neither is ever string-interpolated into SQL text.

2. Issue one API key per chain

When you create the API key for, say, "Westside Cafes", set its auth context to { "workspace_id": "ws-west" }. Store the key as a backend secret; the browser never sees it.

3. Pass request context per call

On each call, your backend derives store_id from the authenticated session and includes it in the request body:

POST /v1/api/jaffle-dashboard/query
Authorization: Bearer gaur_***************
Content-Type: application/json

{
  "contract_name": "store_performance",
  "query": {
    "dimensions": [{ "name": "ordered_at", "time_grain": "day" }],
    "measures":   ["revenue_usd"]
  },
  "context": { "store_id": "STR-008" }
}

The auth key supplies workspace_id; the request supplies store_id. Gaur returns only that store's rows.

The critical rule

Never let the browser set the request context directly. If a user can choose their own store_id, they can read other stores' data. The browser calls your API; your API calls Gaur with a backend-derived store_id. The frontend doesn't see the API key and doesn't set store_id in the request to Gaur.

Auth vs. request: how to choose

Can the value change between calls from the same caller?Use
No (chain operator → workspace)Auth context
Yes (one dashboard, many users)Request context

If the value really is per-user and you need the strength of auth context, issue one API key per user. Heavier operationally; do it only when the threat model demands.

Notes

A user with the right session can never widen their own scope. RLS-driven contracts apply through REST, chat, and MCP exactly the same way, so an MCP-connected agent for that user is also scoped to that user's store.

Source code

apikeys.json in the sample dataset shows the API key shape including an auth-context block. The contract change above is illustrative; you'd apply it to your own copy of store_performance.json.

On this page