Prevent RAG from exposing restricted documents by enforcing trusted caller identity and document permissions before retrieved text enters model context. The model must never decide whether the user is authorized. Apply access policy in the retrieval path, preserve it through chunking and updates, and recheck it when cached or historical content is reused.
Enterprise RAG document-level security covers more than a filter field. It includes source permissions, tenant boundaries, group membership, revocation, caches, citations and operational evidence. This guide gives a trust-boundary design, a role matrix and a small runnable policy demonstration. The demonstration is not a complete authentication service or a production search integration.
In this guide
A permission-aware RAG request flow
- Authenticate the caller: validate the token through the application’s trusted identity layer, including expected issuer, audience and token validity.
- Resolve current scope: derive tenant, user and relevant group or role membership from trusted services. Never accept a browser-supplied group list as authority.
- Construct retrieval policy: combine tenant boundaries with the applicable document eligibility rules on the server.
- Retrieve permitted candidates: enforce authorization within the retrieval path and fail closed if required policy information is unavailable.
- Assemble bounded context: retain source IDs, versions and permission provenance while excluding ineligible text.
- Generate and validate: use permitted evidence, check source references and keep privileged actions behind separate authorization.
- Record minimal evidence: log the policy decision and source references without unnecessarily copying restricted content.
The key boundary is between information that the retrieval service can technically access and information that the caller may receive. A service identity might read the entire index; the application still needs an effective user-level authorization mechanism.
Microsoft’s multitenant RAG guidance describes identity-aware orchestration and restricting grounding data to authorized users. Map these principles to the exact tools and storage routes used in your application.
Roles, permissions and tenant isolation are separate concerns
Role-based access control can describe broad application responsibilities. Source document access may additionally depend on group memberships, ownership, classification or explicit grants. Do not flatten a richer source policy into a single role if that loses important exceptions.
| Actor | Permitted responsibility | Not implied | Required evidence |
|---|---|---|---|
| Reader | Ask questions over documents they may read | Access to every source in their tenant | Current identity and source eligibility |
| Source owner | Approve content and maintain its source permissions | Permission to change platform-wide security | Source version and approval history |
| Ingestion service | Copy approved content and permission metadata | Authority to grant users broader access | Scoped identity, synchronization result and errors |
| Platform operator | Maintain availability and deployment configuration | Unrestricted access to confidential query payloads | Audited administrative operations |
| Security reviewer | Review policy and access test evidence | Automatic ownership of all indexed data | Scoped review access and findings |
This is an illustrative responsibility matrix, not a replacement for your organization’s access policy. Separate read permissions from tool execution permissions. A person allowed to read an invoice is not necessarily allowed to approve its payment.
Shared index or separate tenant stores?
A shared index can simplify some operating tasks but requires reliable tenant scoping on every relevant request. Separate stores can strengthen isolation boundaries while increasing deployment and management work. Neither approach removes the need for document-level policy within each tenant. Test administrative routes and background jobs as well as the normal chat endpoint.
Carry permissions through ingestion, chunking and change
Each chunk should retain a stable parent document reference, tenant scope, source version and enough policy metadata to make an eligibility decision. A chunk without required permissions must not silently become public. Preserve source semantics when documents contain inherited permissions or exceptions.
Keep content freshness and permission freshness visible separately. An unchanged PDF can become restricted after a group or sharing change. Define a revocation objective and an operational response for a stale permission feed, including temporarily withholding affected content when necessary.
Current Azure AI Search document-access documentation distinguishes application security filters based on string matching from identity-aware permission features. Several native permission approaches are marked preview and depend on supported sources and API versions. Verify the selected mechanism and its synchronization behavior; do not assume that copying content automatically copies enforceable permissions.
Deletion must reach all derived representations
When a source is withdrawn, remove or invalidate its chunks, retrieval caches and derived summaries as required by the retention policy. Track completion. A document removed from the index can still leak through an answer cache or a conversation that retains its earlier text.
Runnable demonstration: fail closed before context assembly
The following Python example uses fictional records and a deliberately simple group-intersection rule. The caller identity and policy version are trusted inputs supplied by the test, not values accepted from a client. It demonstrates a context boundary only: a real system must also enforce search-layer filtering, token validation, source policy semantics and reliable permission synchronization.
from dataclasses import dataclass
@dataclass(frozen=True)
class Principal:
tenant: str
groups: frozenset[str]
@dataclass(frozen=True)
class Chunk:
id: str
tenant: str
allowed_groups: frozenset[str]
policy_version: int
text: str
def eligible(principal, chunk, active_policy_version):
return (
chunk.tenant == principal.tenant
and chunk.policy_version == active_policy_version
and bool(principal.groups & chunk.allowed_groups)
)
def permitted_context(principal, chunks, active_policy_version):
return [c.text for c in chunks
if eligible(principal, c, active_policy_version)]
if __name__ == '__main__':
user = Principal('alpha', frozenset({'employees'}))
chunks = [
Chunk('public-to-staff', 'alpha', frozenset({'employees'}), 3, 'Staff policy'),
Chunk('finance-only', 'alpha', frozenset({'finance'}), 3, 'Finance plan'),
Chunk('other-tenant', 'beta', frozenset({'employees'}), 3, 'Beta policy'),
Chunk('stale-acl', 'alpha', frozenset({'employees'}), 2, 'Old permission'),
Chunk('no-acl', 'alpha', frozenset(), 3, 'Missing permissions'),
]
assert permitted_context(user, chunks, 3) == ['Staff policy']
revoked = Principal('alpha', frozenset())
assert permitted_context(revoked, chunks, 3) == []
print('PASS: permitted, denied group, cross-tenant, stale, missing, revoked')
The local run passed assertions for an allowed staff document and rejected a restricted group, another tenant, stale policy metadata, missing grants and revoked membership. These are narrow unit assertions, not a penetration test or proof that a deployed database enforces the same rules.
Notice that an empty grant set denies access in this example. It does not mean “public.” If your application supports public material, represent that state explicitly and validate who may classify content as public. Likewise, a single global version counter is only a teaching simplification; real source systems can require document- or scope-specific policy versions.
Prevent permission leaks through caches and history
A cache keyed only by question text can return another user’s restricted answer. Include the relevant tenant and authorization scope, source versions and policy freshness in cache decisions, or re-authorize the referenced sources before reuse. Do not place bearer tokens directly in cache keys or logs.
Conversation history is also retained data. If a user loses access to a document, decide how previously generated content may be reused under the organization’s policy. A fresh retrieval check does not remove restricted text already present in the next prompt’s history.
Citation links need authorization too. A visible title, snippet or document identifier may itself reveal sensitive information. Validate the source-view route independently; do not rely on the fact that a citation was generated by the model.
Permission filtering does not solve prompt injection
An authorized document can contain hostile instructions. OWASP identifies indirect prompt injection through external material and explains why RAG alone does not eliminate it. Keep retrieved text separate from trusted instructions and restrict connected tools independently.
Do not let a source passage select a new tenant, remove an access filter or request privileged retrieval. If an agent expands its search, every new retrieval must pass the same server-side authorization boundary. Output filtering can provide another check, but it cannot undo disclosure to an unauthorized model context.
Security tests to run before release
| Test | Expected behavior | Evidence to inspect |
|---|---|---|
| Change tenant identifier in request | Reject invalid scope or derive scope from trusted identity | No foreign source in context or response |
| Ask for a restricted document by exact title | No restricted content or revealing snippet | Retrieval and citation outputs |
| Remove group membership | Access ends within the agreed revocation boundary | Identity refresh, policy state and cache invalidation |
| Permission service unavailable | No fallback to unfiltered search | Failure response and diagnostic event |
| Missing ACL on a new chunk | Withhold the chunk | Ingestion quarantine and alert |
| Repeat another user’s cached question | Re-authorize or isolate cached material | Cache policy and source references |
| Injected instructions in a permitted source | No authorization or tool privilege expansion | Policy decisions and tool calls |
| Delete a source document | Derived content is invalidated according to policy | Index, cache and retention completion |
Use synthetic or approved test data. Review both the final response and the actual context supplied to the model; a polite refusal after forbidden evidence entered context is still a boundary failure. Include denied cases in regression testing whenever retrieval, identity, source connectors or caching changes.
Monitor access decisions without creating a second data leak
Record request correlation, authenticated scope references, policy version, source identifiers and an allowed or denied outcome as appropriate. Restrict access to those logs and define retention. Avoid copying full prompts, tokens or document text into unrestricted telemetry.
Prepare a response for stale permissions or suspected disclosure: identify affected sources and sessions, restrict the relevant retrieval route, invalidate derived data where required, preserve approved evidence and involve the responsible security and data owners. Monitoring must have an owner and an actionable response path.
Common questions
What is permission-aware RAG?
It is retrieval-augmented generation that restricts grounding evidence according to the authenticated caller’s permitted sources before model context is assembled, and preserves those boundaries through caches, citations and updates.
Is a vector database API key enough for document security?
No. An API key can authorize an application to call a service while giving it access to more documents than the end user may read. User- and tenant-level document eligibility still needs enforcement.
Can the model decide which documents a user may see?
No. Make authorization decisions with trusted policy enforcement outside the model. Retrieved instructions or generated text must not be able to widen the caller’s access.
How should RAG handle revoked access?
Refresh the relevant identity and permission state, exclude the affected sources and invalidate or re-authorize derived caches and history according to policy. Define and test a revocation objective rather than assuming instant synchronization.
Does permission filtering prevent prompt injection?
No. It limits eligible evidence, but permitted content can still contain malicious instructions. Apply separate instruction boundaries, least-privilege tools and adversarial tests.
Review your retrieval trust boundary
Use this checklist with the enterprise RAG architecture guide and RAG versus fine-tuning comparison. To assess identity propagation, source permissions and cache behavior in your application, request an enterprise RAG architecture review.
