microsoft / microsoft/rayfin

Expose DAB set-session-context (native SQL RLS) and/or custom roles in Rayfin

Open
#82 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement
Dominant language
No language data
Stars
610
Forks
62
Avg merge
1d 23h
Merged PRs (30d)
3

Description

Problem statement

We run a production Fabric App (AppBackend + SQL database, Rayfin 1.34) — an
operational-metrics data-entry system for a group of ~15 organizations.
Authorization requirement: an authenticated user must only read/write rows
belonging to organizations they were granted, where grants live in a mapping
table (UserOrgAccesses: user_email × organization_id).

Problem. DAB item policies cannot express joins — the policy syntax is
limited to @claims/@item comparisons, so "row is visible if a matching
(claims.email × item.organization_id) pair exists in the mapping table" is not
expressible. Our current workaround is item.user_email eq claims.email (own
rows only), which blocks legitimate same-organization reads: a collector cannot
see values entered by a colleague for the same org. We have since relaxed
read/update to all authenticated users so teams can work together, which
means the organization boundary is now enforced only in the UI. (Update
2026-09-15: we verified on the live Fabric backend that DAB does enforce
policy.database on create — an insert with a foreign user_email is
rejected with "Could not insert row with given values", and both text-literal
and quoted-GUID predicates filter read/update correctly. So authorship is
protected; what remains inexpressible is the organization membership check,
because it needs a join.)

Proposed solution

Expose DAB's data-source.options.set-session-context.

Data API Builder for MSSQL supports set-session-context: true — it calls
sp_set_session_context with the token claims before each query. That lets us
define a native SQL SECURITY POLICY whose predicate function joins our mapping
table (UserOrgAccesses), so the organization-membership check becomes
expressible in the database itself and is enforced for every query path,
including direct GraphQL. A BLOCK ... AFTER INSERT predicate would additionally
harden create.

Rayfin generates the DAB config internally and does not surface this option in
rayfin.yml (no mention in the 1.34 guide; rayfin/.temp is regenerated on
deploy). Two things would fully cover our requirement:

  1. A passthrough for set-session-context: true (or an equivalent Rayfin knob).
  2. A documented, supported way to ship SQL migrations (CREATE FUNCTION /
    CREATE SECURITY POLICY) that survive redeploys.

Reference: https://learn.microsoft.com/azure/data-api-builder/concept/security/row-level-security

Alternatives considered

Custom roles (alternative or complement). DAB supports custom roles (roles
claim + X-MS-API-ROLE header with per-role permissions/policies), but Rayfin
restricts to the two built-ins (anonymous, authenticated). Custom roles would
let us model admin/collector without hardcoding admin emails as literals inside
policies (today an admin-list change requires a schema redeploy).
Reference: https://learn.microsoft.com/azure/data-api-builder/concept/security/authorization-overview

Workarounds we rely on today, and why each falls short:

  • item.user_email eq claims.email (own-rows-only): blocks legitimate
    same-organization reads — a collector cannot see a colleague's values for the
    same org.
  • Relax read/update to all authenticated users, enforce the org boundary in the
    UI: leaves the boundary unenforced at the API layer — any authenticated caller
    can read another org's rows via direct GraphQL.
  • Bake the full access map (user_email × organization_id) into policy predicates
    as literals and regenerate on every access change: expressible today, but
    couples every grant/revoke to a redeploy — it does not scale operationally.

None of these enforce "visible only if a matching grant exists in the mapping
table" at the database layer, because that check needs a join.

Additional context

Environment: tenant 2139481e-7233-4d97-b411-e65812d14856, workspace
12ea5e43-5aae-462c-aa40-8b6c6525a987, AppBackend
dfa02918-0e91-4c8c-baea-9eae837dcc01, Rayfin CLI/SDK 1.34.

Happy to share the schema and try preview bits.

Related but separable: staticHosting offers no way to set HTTP response
headers, so we ship CSP via a <meta http-equiv> tag. The meta form cannot
express frame-ancestors, leaving no way to restrict who may embed the app in
an iframe — while the app is designed to run inside the Fabric iframe. A way to
declare response headers (at minimum Content-Security-Policy with
frame-ancestors, ideally Strict-Transport-Security, X-Content-Type-Options)
for staticHosting would close this. Can file as a separate issue if preferred.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

The payload identifies rayfin.yml and regenerated rayfin/.temp as the configuration boundary; start by tracing how Rayfin generates DAB configuration and compare it with DAB's set-session-context and custom-role documentation. Done means a supported configuration path and durable SQL migration mechanism are defined and documented, with the separate staticHosting headers concern split into its own issue.

Written by the indexing model from the issue text.

Assessment

Tech stack
sql
Domain
authorization, backend-api-design, databases, security
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.