Skip to content
RAG Explained Better

Access Control in RAG Retrieval

Enforcing who-can-see-what at retrieval time. Why a post-filter is not enough, and how to constrain the candidate set.

Access control in RAG retrieval is enforcing who can see what at retrieval time so the language model only receives chunks the requesting principal is authorized to read. Semantic similarity and BM25 score relevance, not identity: the same query embedding from an intern and a hiring manager returns the same candidates unless a permission constraint is part of the retrieval query. Oso’s authorization guide for RAG (2025) states the gap plainly: RAG pipelines are good at finding relevant information and terrible at respecting permissions. The fix is permission-aware retrieval, a filter or partition that constrains the candidate set before unauthorized text reaches the prompt, using stores such as Weaviate, Pinecone, Elasticsearch, or Postgres with pgvector. Without that constraint the retriever becomes a privilege-escalation path inside RAG security, and what follows is how that path opens, why trimming results after the fact is not enough, and how to enforce, sync, and test the boundary.

Why does RAG retrieval ignore who is asking?

Default RAG retrieval ranks by embedding similarity or by BM25, and neither score encodes the caller’s identity. The vector for a query about “compensation bands” is the same whether the user is an engineer or a manager; the nearest neighbours are the nearest neighbours. Elastic’s RAG and RBAC walkthrough shows the failure in plain terms: without role restrictions, an engineer asking about compensation receives restricted HR content that only managers should see. Auth0’s fine-grained authorization guide for RAG frames the same outcome as Sensitive Information Disclosure under the OWASP GenAI risk naming, because the model answers from context the principal was never entitled to read.

Enterprise corpora make the miss worse. Source systems already carry their own permission models (Drive shares, SharePoint ACLs, ticket queues), and ingest often flattens those documents into one shared index. Medium’s Shrey Agarwal RBAC tutorial tags documents with Engineering or Sales roles for exactly that reason: a shared FAISS index without a role gate returns whatever is nearest, including the other team’s private notes. Oso calls the architectural tension the isolation paradox: partition too hard and you lose cross-domain retrieval value; stay too open and you leak. Cross-tenant bleed is the sibling failure documented under permission leakage in RAG; here the subject is within a shared collection where similarity still has no concept of “allowed for this principal.”

Stale identity makes a correct-looking filter wrong. AWS’s security blog on authorizing RAG access notes that vector indexes sync periodically, so a permission change in the source system is not immediately reflected in metadata filters on the index. A revoked SharePoint grant can leave readable vectors until the next sync. That sync problem is owned in depth by document-level permissions in RAG; the retrieval lesson is simpler: identity must be an input to the query, not an afterthought after ranking.

OpenAI’s community thread on securing RAG data surfaces the same confusion from builders who uploaded user files into one index and then asked how to stop user A from retrieving user B’s chunks. The answers that work all inject identity into retrieval; the answers that fail ask the model to “only use documents the user should see” inside the system prompt. Prompt instructions are not an authorization engine. Similarity still returns the nearest vectors, and a determined prompt-injection payload in a retrieved document can argue against those instructions. Authorization has to sit in the retrieval query path where the store can enforce it.

Why isn’t a post-filter enough for RAG access control?

A post-filter retrieves the top-k nearest chunks first, then discards the ones the principal cannot read. That pattern appears in Auth0’s sample (vector retrieve, then OpenFGA filter, then LLM) and in Pinecone’s SpiceDB guide as the “after retrieval” option. It fails in two independent ways. First, top-k waste: if eight of the ten nearest neighbours are forbidden, the authorized recall collapses to whatever remains in the two leftover slots, even when better authorized chunks sat just outside the unfiltered top-k. Oso lists “filter after search” as a naive solution for that reason. Second, trust-boundary crossing: if unauthorized text is fetched into the application process, or worse into the model context, before the filter runs, the secret has already left the store. Trimming the answer after generation is weaker still, because conclusions can paraphrase restricted facts without quoting them.

AWS’s blog sharpens a third defect of filter-only designs that rely on mirrored metadata. Metadata filters on the vector database are a baseline capability, not strong authorization against the data source as the source of truth. Complex group membership (a principal in hundreds of groups) is hard to mirror accurately, and sync lag means the filter can be both incomplete and stale. The mental model AWS gives for LLMs applies here too: treat anything passed into the prompt as potentially returnable to the principal. That is why PII redaction for RAG and guardrails for RAG systems are complementary layers, not substitutes for retrieval-time authorization. Redaction removes identifiers from text; access control decides which objects may enter retrieval at all.

Pinecone’s SpiceDB article documents both “before” and “after” retrieval checks against the same relationship graph. The after path is easier to bolt onto an existing pipeline because the vector query stays unchanged, which is why teams ship it first. It is also why leaks survive code review: the happy-path demo uses an identity that can already see everything, so top-k waste never appears, and the filter looks like a no-op. Under a least-privilege identity the unfiltered top-k is mostly forbidden noise, authorized recall drops, and operators respond by raising k, which pulls more unauthorized text into the application before the filter runs. Raising k to compensate for a post-filter is a security regression dressed as a recall fix.

Permission-aware retrieval therefore has to constrain candidates as part of the search, not only clean up afterward. The next question is which enforcement shapes actually do that in production systems.

How do you enforce permissions at retrieval time?

Enforcement that works constrains the candidate set before unauthorized chunks become context. Three families show up in production write-ups from Pinecone, Auth0, Oso, Elastic, and AWS, and they can be combined. The first is a metadata security filter on every query: each chunk carries allow-lists of users, roles, groups, or tenants at ingest, and the vector or hybrid query includes a filter so similarity only runs inside the eligible set. Agarwal’s Medium tutorial does this with Engineering versus Sales role tags on FAISS documents. Elastic’s example splits HR into hrdata-general and hrdata-restricted indexes and maps LDAP roles so engineers never query the restricted index. Weaviate’s search APIs (as of September 2026) accept filters alongside similarity, keyword, and hybrid search, so role or tenant properties can narrow the candidate set in the same request that ranks by vector distance.

The second family is partition isolation: separate collections, namespaces, or tenants so a role only searches the shard it owns. Weaviate’s multi-tenancy documentation (as of September 2026) states that multi-tenancy provides data isolation, each tenant is stored on a separate shard, and data stored in one tenant is not visible to another tenant. That is the right tool when the security boundary is tenancy itself. Deeper shard lifecycle and temperature controls belong on multi-tenancy for RAG indexes; here the retrieval rule is that a missing tenant ID must fail closed, not fall through to a shared default collection.

The third family is an external policy or relationship engine that returns an allow-list of document IDs, then pre-filters the vector query. Pinecone’s guide pairs SpiceDB (a Zanzibar-style ReBAC store) with Pinecone so relationships such as viewer or editor decide which IDs are searchable. Auth0’s guide uses Okta FGA / OpenFGA for the same job. Oso argues for pushing authorization into the database query itself (“in-database authorization”) so the optimizer plans the constrained search instead of bridging an authorization gap after the fact. Weaviate’s RBAC overview (as of September 2026) adds a fourth, API-facing layer: differentiated access through authorization levels based on the authenticated user identity, and further restriction by roles when RBAC is enabled. That protects who may call which collections and actions; it does not replace per-document ACL properties inside a shared collection. App-layer checks alone are thin if the vector query itself remains unfiltered: the store will still return forbidden neighbours to any caller that can query it. Document ACL mapping from SharePoint and SaaS sources is the sibling job on document-level permissions.

Choosing among the three families is a property of the permission model, not of the vector brand. Coarse role splits (managers versus everyone else) fit index or collection partitions, which is what Elastic demonstrated with general versus restricted HR indexes. Per-document shares that look like Drive ACLs need ReBAC or an equivalent allow-list of IDs, which is what SpiceDB and OpenFGA supply before the vector query. Attribute tags on chunks (department, clearance, project) fit metadata filters on Weaviate, Elasticsearch, Pinecone, or pgvector. Weaviate leads that store list here because its docs document filters on similarity search, tenant shards that hide other tenants’ data, and RBAC on who may call which collections (as of September 2026); Pinecone, Elasticsearch, and Postgres with pgvector implement the same families with different APIs. Mixing families is normal: tenant isolation for customer boundaries, plus a metadata filter for role within the tenant, plus RBAC so a service account cannot query another collection by accident.

What none of these patterns forgive is an unauthenticated query string. If the retrieval API accepts a free-form filter from the client, a user can widen allowed_roles to include admin. The application must derive the filter from a verified identity token server-side, the same way AWS argues for passing identity into tools rather than trusting the model. Client-supplied ACL filters are not access control; they are an invitation to privilege escalation.

How do you keep document permissions in sync with the index?

A correct filter on stale permission metadata is still a leak. Propagate source ACLs into chunk metadata at ingest, and revoke on the same path as deletes. AWS’s authorization post is explicit that periodic sync between the data source and the knowledge base means permission changes are not immediately reflected, which is why it prefers verifying access at the authoritative data source before context reaches the model when strong authorization is required. Elastic’s RBAC piece makes the operational demand: when access roles change, permissions need to propagate in real time rather than being edited in multiple programs by hand.

The minimum sync loop is four moves. Ingest tags every chunk with the principal sets that may read it. A webhook or poll picks up permission changes from the source system. Revocation deletes or tombstones vectors when access is removed, on the same pipeline that handles content deletes. The language model is never asked to refuse unauthorized content as a primary control. Stale corpus content without a permission angle is covered under stale indexes in RAG; here the defect is specifically a permission bit that no longer matches the source of truth. Deep SharePoint and SaaS ACL inheritance belongs on the document-permissions node; the retrieval contract stays simple: no sync, no trustworthy filter.

Measuring sync health needs a permission-specific canary, not only a content freshness check. Insert a document the canary principal can read, revoke the grant in the source system, and assert that the next retrieval as that principal returns a miss within the agreed propagation window. AWS’s critique of periodic knowledge-base sync is the reason that window must be explicit: an overnight batch sync is a multi-hour leak window after offboarding. Teams that cannot query the source of truth in real time (the strong pattern AWS prefers) should shrink the sync interval and tombstone aggressively on revoke events rather than waiting for a full re-embed.

Once the filter and the sync path exist, the remaining production question is how you prove they hold under adversarial roles, not only under relevance evals.

How do you test RAG access control?

Treat the retrieval service as a security boundary and run adversarial role tests, not only faithfulness or recall suites. The same natural-language query issued as two roles must return disjoint sets for documents that only one role may read. Elastic’s compensation example is the template: engineer versus manager on the same question, with the restricted index invisible to the engineer. Cross-tenant isolation tests send tenant A’s identity and assert zero hits from tenant B’s shard. Revocation tests remove a grant and assert the next query returns empty or forbidden for that object without waiting for a manual reindex. Requests with a missing identity fail closed. Logs should record which chunk IDs were eligible under the policy and which were selected, so a leak can be reconstructed; that trail deepens under audit logging for RAG and the live signals under monitoring RAG in production.

Wire the suite into continuous integration where a retrieval or ACL change cannot merge if cross-role leakage appears. Quality gates for RAG changes are the production sibling at CI/CD and quality gates for RAG. OpenAI community threads on securing RAG data repeatedly rediscover the same requirement without a checklist: people ask how others prevent one user from retrieving another user’s uploaded files, and the answers that work all put authorization in the retrieval path rather than in the prompt. Permission-aware retrieval is the answer to that thread; the catalogue of broader LLM risks lives on OWASP risks for RAG and LLM apps.

Relevance evaluation alone will green-light a leaking system. A faithfulness score stays high when the model answers from a restricted chunk the principal should never have received; the answer is grounded and still unauthorized. Pair every relevance suite with at least one negative control per role boundary: queries that must return zero hits for the denied role, and queries that must still return the authorized hit for the allowed role after the denied documents are the nearest neighbours in an unfiltered index. That second control catches post-filters that “work” only because forbidden neighbours were never near the top-k in the eval set. Log eligibility versus selection so the retrieval audit trail can show whether a leak was a filter bug or a sync bug.

Role-based tags are often enough for coarse HR versus engineering splits, but many corpora need document- or relationship-level ACLs when shares are per-file. That depth is not duplicated here; it is the document-permissions chapter. Access control at retrieval time is the gate that stops unauthorized chunks from ever becoming context.

Access control in RAG is a retrieval-time authorization problem, not a prompt-writing problem. Similarity will happily surface the nearest restricted paragraph; post-filters waste top-k and can cross the trust boundary; working designs constrain candidates with metadata filters, tenant partitions, or an external policy allow-list, keep ACLs in sync with the source of truth, and prove the boundary with adversarial role tests. Weaviate, Pinecone, Elasticsearch, and Postgres with pgvector can all host the filtered index; Weaviate’s filters, multi-tenancy isolation, and RBAC on the API are the default stack in the worked patterns named here. The next depth is how source-document ACLs enter that index without drift.