RAG Access Control and Provenance is an architectural pattern that enforces strict authorization checks during the retrieval phase and attaches source citations to generated answers, ensuring users only receive information they are permitted to see. Filter candidate documents by authorization before ranking or synthesis, and carry citation metadata into every generated answer.
Understanding RAG Access Control
Most RAG implementations ask one question at retrieval time: what documents are relevant to this query? The question they do not ask is: is this user or this agent allowed to see those documents?
The retrieval system does not know the difference between a query from an internal analyst and a query from a customer-facing agent unless the harness tells it. If the index includes documents with different classification levels, sensitivity categories, lifecycle states, or audience restrictions, retrieval can silently bypass access control. Vector similarity is not authorization — a semantically relevant document can still be the wrong document to expose. The gap appears when access checks happen before indexing, after answer generation, or only in the application UI. The RAG pipeline itself needs authorization as part of retrieval.
Implementing RAG Access Control and Provenance
Filter candidate documents by authorization before ranking or answer synthesis, and return source identifiers, lifecycle state, and citation metadata with the answer.
flowchart TD
A[RetrievalRequest] --> B[Load candidate documents]
B --> C{Tenant match?}
C -->|no| X[Exclude]
C -->|yes| D{Domain match?}
D -->|no| X
D -->|yes| E{Lifecycle active?}
E -->|no| X
E -->|yes| F{ACL intersects user roles?}
F -->|no| X
F -->|yes| G[Score and rank]
G --> H[Synthesize answer with citations]
In LangGraph, the retrieve node filters candidate documents through the RetrievalAuthorizer before anything reaches the model, then routes on whether any authorized source survived — generate synthesizes a cited answer, no_answer returns a grounded refusal rather than hallucinating from content the user was not entitled to see:
def retrieve(state: AgentState) -> dict:
candidates = [
RetrievedDocument("1", "Order consumer runbook", "public", "uri1", {}),
RetrievedDocument("2", "Signing keys", "private", "uri2", {}),
]
allowed = [tracker.sign_document(d) for d in authorizer.filter_documents(candidates)]
return {"authorized": allowed}
def route_retrieval(state) -> Literal["generate", "no_answer"]:
return "generate" if state["authorized"] else "no_answer"
builder.add_edge(START, "retrieve")
builder.add_conditional_edges("retrieve", route_retrieval,
{"generate": "generate", "no_answer": "no_answer"})
Considerations for Secure RAG Retrieval
Consider the following factors when you implement this pattern:
- Access model. A baseline uses tenant, domain, role ACL, and lifecycle status. Microsoft Zero Trust principles require you to Verify Explicitly—Copilot should require that users have legitimate access to the data it retrieves.
- Oversharing and Blast Radius. Copilot acts as a “force multiplier” for existing permissions. If a user has broad access to sensitive files they shouldn’t, the AI will make that data easier to surface. You must clean file permissions and access controls before rolling out RAG.
- Provenance tracking. Each retrieved item should carry document id, tenant, domain, lifecycle, and whether ACL was checked. The answer returns citations and validates that cited sources are still active.