Knowledge RBAC for enterprise agents: same gates for chat, browse, and citations
An agent that answers from documents is only as trustworthy as the document permissions behind it.
Teams often lock the chat endpoint carefully, then leave library browse and citation PDF downloads open to anyone who can sign in. That is not a knowledge boundary. That is a second door with a nicer UI.

*Figure 1. Product capabilities belong as app roles on the API. Source: Microsoft Learn: add app roles.
I have watched this pattern show up in more than one agent shell. The chat experience looks strict. The library looks helpful. Citations look harmless. Together they recreate full corpus access for anyone who is merely authenticated.
The problem shape
Enterprise agent apps usually grow a shared library: general guidance plus specialized packs. Those packs might be customer verticals, regulated topics, or internal playbooks. Product wants a few honest outcomes:
- some users only on the general agent and general docs
- some users on one specialized agent and its folder tree
- admins who can upload and reindex without becoming god mode by accident
- auditors who can explain why a person saw a file
If browse lists every folder, and /api/content/... serves any blob path to any authenticated user, specialized knowledge leaks through the UI even when the agent picker looks restricted. The model did not "hallucinate access." Your API granted it.
The core idea
Chat, browse, download, upload, reindex, and citation fetch share one authorization module.
I treat knowledge paths as categories with the same roles that gate agents. A general-only principal never sees specialized folders in the library, never downloads those PDFs, and never gets them injected into retrieval scope for a turn.
When a user opens a citation, that is still a content read. Gate it like chat. If the answer referenced a file the user cannot open, you have either over-retrieved or under-gated the citation UI. Both are design bugs.
A model that stays explainable
1. App roles on the Backend API
Declare roles for agent use and for category manage or browse. Keep the strings stable. Prefer roles that travel with the app registration over nested Entra groups for every folder. Groups are fine for assignment. Roles are better for product capabilities that must survive the next tenant.
2. Server filters before the UI gets a vote
Filter the library list on the server. Clamp retrieval paths before the model runs. Return 403 on content routes that fail the path check. The SPA should never be the only place a folder disappears.
3. Frontend speaks capabilities, not JWTs
Expose an access payload: what the user can browse, manage, and which agents are allowed. Render from that. Decoding role names in React duplicates policy and drifts the moment someone renames a role in Entra.
4. Explicit implications
Managing a category can imply using the matching agent. Or it might not. Pick one. Write it down. Unit-test it. Tribal knowledge ("admins always get the agent") becomes a production incident when a contractor gets manage without chat, or chat without manage, and nobody agrees which is correct.
5. Storage ACLs reinforce the app, they do not replace it
Blob ACLs and app roles should point the same direction. Users hit the app layer first. If the app is loose, storage never gets a chance to save you for browser downloads through your own API.

Figure 2. Sign-in is only the shell. Knowledge entitlements still need API-side checks. Source: Microsoft Learn: authentication flows and app scenarios.
Failure modes I design against
Signed-in users can browse everything because the library is helpful. Helpful is not a permission model.
Agent is filtered. Blob download is not. Same bytes, different door.
Retrieval scope is a client-supplied folder list with no server clamp. Assume a proxy and a curious user.
Storage ACLs and app roles disagree. Citations 403 for people who already saw the answer, or open for people who should not.
Break-glass admin is the default library group. Break-glass should be named, rare, and reviewed.
Trade-offs
Stricter knowledge RBAC means more Entra work and a larger test matrix: general-only, specialized-only, multi-pack, admin, break-glass. You will spend time with identity owners on who creates roles versus who assigns groups.
You may also frustrate power users who want "just show me everything." That is a product conversation. Do not solve it by quietly opening browse.
Indexing and reindex permissions need the same care as upload. A reindex-all button for someone who can only manage one category is another leak of intent, even if the files themselves stay put.
What I would put on an ADR
- Same role family for agent catalog and knowledge categories.
- Browse is not open to all signed-in users if chat is scoped.
- Citation and content routes call the same path checks as library.
- Tests for general-only, specialized-only, and admin matrices.
- Written implication rules (manage implies use, or not).
- No customer folder names in public docs or screenshots.
Primary references: Add app roles, Verify scopes and app roles, SPA calling a web API.
Closing
If your agent product has a library, draw the entitlement diagram before you ship the upload button.
Chat without corpus control is a demo. Corpus control without citation control is a leak with footnotes. Put them on the same gate.
