Most over-permission incidents are not caused by a missing login check but by an authorisation decision made at the wrong granularity: a role that says "can read storage" when the job needed "can read this one bucket". Coarse-grained authorisation answers broad questions (may this identity call this API, enter this application, assume this role). Fine-grained authorisation answers narrow ones (may this identity read this record, this field, this document chunk, right now, in this context). Both are needed, and the real design question is which layer enforces which kind of decision. This guide is for architects, application security leads and IAM engineers deciding where each belongs, including for AI agents and retrieval augmented generation (RAG). It builds on the Authorisation Models Guide, which explains RBAC, ABAC and ReBAC themselves, and does not repeat it.
Key takeaways
- Granularity is about the resource, not the model. RBAC can be fine-grained if roles are scoped to single objects, and ABAC can be coarse if the only attribute is "department". Ask what unit of data each decision protects.
- Coarse gates belong at the edge, fine checks belong next to the data. Gateways, service meshes and cloud IAM are good at "may this caller reach this service"; only the code or service that knows the object can answer "may this caller see this row".
- Coarse-only designs fail silently. A token that passes the gateway with a valid scope can still read every tenant's records if no object-level check follows, which is why broken object level authorisation tops the OWASP API Security Top 10 2023.
- Fine-grained costs are latency, data freshness and policy sprawl. Budget for them deliberately: decision caching, relationship sync and decision logs are part of the design, not afterthoughts.
- Agents and RAG need fine-grained checks by default. An agent acting for a user, or a retriever pulling chunks into a prompt, must be filtered by the user's own permissions on each object, never by the service account's broad access.
The short answer
Use coarse-grained authorisation to shrink the attack surface early and cheaply: deny unknown callers, unentitled applications and out-of-scope tokens before they reach business logic. Use fine-grained authorisation wherever a decision depends on the specific object, its owner, its tenant, its classification or the request context. Never let a coarse decision stand in for a fine one. A practical rule: if two users with the same role should see different data, you need a fine-grained check, and it must run where the data is resolved.
| Dimension | Coarse-grained authorisation | Fine-grained authorisation |
|---|---|---|
| Question answered | May this identity use this application, API, service or role? | May this identity perform this action on this object, field or chunk, in this context? |
| Typical inputs | Identity, group or role, OAuth scope, client ID, network zone | Object ID, owner, tenant, relationship, classification, time, device, transaction value |
| Typical enforcement point | API gateway, service mesh, identity provider app assignment, cloud IAM role, Kubernetes RBAC verb and resource | Application service, data access layer, database row-level security, policy engine called per object, vector store filter |
| Common models | RBAC with broad roles, scopes, group membership | ABAC, ReBAC (relationship tuples), RBAC with resource-scoped roles, policy as code |
| Decision volume | One per request or session | One per object, sometimes many per request (lists, searches, retrieval) |
| Latency profile | Usually evaluated from token claims, no extra lookup | Needs object and relationship data; can add lookups unless cached or evaluated locally |
| Audit answer | "Alice has the Finance role" | "Alice read invoice 4411 because she is approver on cost centre 22" |
| Main failure mode | Over-permission: broad access treated as enough | Missed or inconsistent checks, stale relationship data, policy sprawl |
Coarse-grained authorisation in depth
Threat model
Coarse gates defend against callers who should not reach a system at all: unauthenticated traffic, applications not assigned to a user, tokens issued for a different audience, workloads outside an allowed namespace. They also limit blast radius when a credential is stolen, provided the role or scope behind it is narrow. They do not defend against an entitled caller asking for an object that belongs to someone else, which is the horizontal escalation pattern behind most insecure direct object reference (IDOR) findings.
Architecture in words
The identity provider issues a token carrying groups, roles or scopes. A gateway or mesh sidecar validates the token, checks audience and scope against the route, and forwards or rejects. Cloud IAM attaches a role to a workload; the cloud control plane checks that role's policy on every API call. Kubernetes RBAC binds a service account to verbs on resource types in a namespace. In each case the decision is made from a small, stable set of attributes that change rarely, which is why coarse checks are fast and easy to reason about.
Failure modes
- Role explosion and its opposite: the OWASP Authorization Cheat Sheet notes that too many roles create management and header size problems, so teams collapse them into a few broad roles that over-grant.
- Policy that grows with the estate: AWS describes how traditional per-job-function policies must be edited each time resources are added, which encourages wildcards "for now".
- Workload roles scoped to the service, not the job: in the Capital One breach a firewall's cloud role could list and read many storage buckets of customer data it never needed, so one stolen credential reached them all.
- Application permissions with tenant-wide reach: in the Microsoft Midnight Blizzard breach the attacker granted an OAuth app the Exchange Online
full_access_as_approle, an application permission that reaches mailboxes without a user signing in.
Controls that make coarse checks safe
- Deny by default at the gate: routes with no explicit rule are rejected, as the OWASP cheat sheet requires of every authorisation layer.
- Narrow audiences and scopes: one audience per API, scopes per capability (
invoices:read, notapi). See the Static Scopes vs Intent-Based Authorisation for AI Agents for where scopes stop being enough. - Resource-scoped cloud roles: name the bucket, key or table in the policy, or use tag conditions so one policy covers many resources without wildcards.
- Periodic review of broad grants: roles and app permissions that reach "all" of something get the shortest review cycle. See the Access Reviews Guide.
Fine-grained authorisation in depth
Threat model
Fine-grained checks defend against an authenticated, entitled caller reaching data that is not theirs: another tenant's records, another patient's file, a document shared with a different team, a field (salary, card number) the role should not see. The OWASP API Security Top 10 2023 lists this family three times: API1 Broken Object Level Authorization, API3 Broken Object Property Level Authorization and API5 Broken Function Level Authorization. The OWASP cheat sheet's point that access to one object of a type does not imply access to others of that type is the core of the threat model.
Architecture in words
There are three common shapes. First, in-code checks: the service loads the object and compares owner, tenant or ACL before returning it. Second, an externalised policy engine: the service sends subject, action, resource and context to a policy decision point and enforces the answer. Cedar's request model is exactly this principal, action, resource and context tuple, and its algorithm denies by default and lets any matching forbid override every permit. Third, relationship-based services modelled on Google Zanzibar: relationships are stored as tuples (user, relation, object), and the application asks "check" or "list objects". OpenFGA documents these primitives, including contextual tuples sent with a request and conditions written in Common Expression Language (CEL). Where the check runs, central service or embedded library, is a separate decision covered in Centralised vs Embedded Authorisation.
Failure modes
- Selective checks: the detail endpoint checks ownership but the export, search or bulk endpoint does not. The OWASP cheat sheet's warning that an attacker only needs one way in applies directly.
- List and search leakage: per-object checks run on reads but list endpoints filter in the UI, or pagination totals reveal counts of hidden objects.
- Stale relationships: the relationship store lags the source system, so a removed team member keeps access until sync catches up. Zanzibar's design goal of decisions that respect the causal order of changes exists to prevent exactly this.
- Policy sprawl: hundreds of near-duplicate rules nobody can explain, often written per feature with no tests.
- Error handling that grants: a policy evaluation error treated as allow. Cedar skips erroring policies rather than allowing on error, and still returns the policy IDs that errored, so applications can alert on them.
Controls that make fine checks reliable
- Enforce in one data access path: repository or query layer helpers that always apply tenant and owner filters, so a new endpoint cannot skip them.
- Filter before you fetch for lists: use list-objects style queries, database row-level security or partial evaluation to turn policy into a query filter, instead of fetching everything and dropping rows.
- Test authorisation as code: unit tests for allow, deny and default deny per role and relationship. See the Authorisation Testing Automation Guide.
- Decision logs with reasons: record subject, action, resource, decision and the determining policy or relationship. Cedar returns determining policies on every decision for this purpose.
Performance and consistency trade-offs
Fine-grained decisions multiply. A page showing 50 documents needs 50 decisions, or one list query, and a RAG retrieval of 20 chunks needs 20. Plan for it.
| Technique | What it buys | What it costs |
|---|---|---|
| Evaluate coarse checks from token claims | No network hop at the gate | Claims are stale until token expiry; keep token lifetimes short for privileged scopes |
| Local policy evaluation (sidecar, library, WebAssembly) | Sub-millisecond decisions; OPA's own guidance cites budgets in the order of 1 ms for microservice API authorisation | Policy and data distribution; memory grows with data (OPA notes raw JSON uses roughly 20 times the memory of its compact form) |
| Decision caching | Fewer calls for repeated checks | Revocation delay equal to the cache lifetime, so keep it short for sensitive resources |
| List-objects or query filtering | One call for a whole result set | Needs an engine or schema that supports it; harder to explain per item |
| Consistency tokens (Zanzibar style) | Decisions at least as fresh as the change that matters | More complex client code; the Zanzibar paper reports p95 latency under 10 ms at Google scale, which most teams will not match without care |
Profile before optimising: OPA documents opa eval --profile and opa bench, and rule indexing makes equality and membership checks far cheaper than iterating over arrays.
Auditability: what each layer can prove
Auditors and incident responders ask two different questions: who could access this, and who did. Coarse layers answer the first well at the level of roles and groups, which suits access reviews and joiner, mover and leaver evidence. Fine-grained layers answer the second, if they log decisions with object identifiers and reasons. ABAC and ReBAC answers to "who could access this record?" need a reverse query (list users, expand) because access is computed, not stored; check your engine supports it before an auditor asks. NIST SP 800-162 frames ABAC as evaluating subject, object, operation and environment attributes against policy, which is precise but means the access review must review the policy and the attribute sources, not just a list of assignments.
Agents and RAG: why the fine-grained layer cannot be skipped
AI agents and retrievers usually run under a service identity with broad, coarse access: a connector that can read the whole document store, an API key with full scopes. If the agent or retriever returns whatever that identity can reach, every user effectively inherits the service account's permissions.
- Act in the user's context: OWASP LLM06:2025 Excessive Agency recommends executing extensions in the user's context with minimum privileges, and complete mediation: authorisation enforced in downstream systems, not decided by the model.
- Permission-aware retrieval: OWASP LLM08:2025 Vector and Embedding Weaknesses calls for fine-grained access controls and permission-aware vector stores, with strict partitioning between user groups, and gives cross-tenant chunk retrieval as an example attack scenario.
- Filter at query time, verify at return time: apply a tenant or ACL metadata filter in the vector query, then re-check each returned chunk's source document against the live permission system before it enters the prompt, because chunk metadata copied at index time goes stale.
- Coarse gate on the agent itself: which tools and connectors an agent may call is a coarse decision; which records a tool call may touch is fine-grained. See the AI Agent Authorisation Guide and Agentic RAG vs Classic RAG.
Decision guidance: where to enforce each
| Situation | Coarse layer | Fine layer |
|---|---|---|
| Internal admin tool, small user base, one tenant | App assignment plus a few roles | Ownership checks on destructive actions only; full ReBAC is usually overkill |
| Multi-tenant SaaS API | Gateway token, audience and scope validation | Tenant ID enforced in every query, object checks on every ID in the path or body. See the Multi-Tenant SaaS Authorisation and Tenant Isolation Guide |
| Document sharing, folders, teams | App access | ReBAC with inherited relationships (folder viewer implies document viewer) |
| Regulated data with context rules (location, time, purpose) | Role grants eligibility | ABAC conditions evaluated per request |
| Cloud workloads | One role per workload | Resource ARNs or tag conditions in that role, not wildcards |
| AI agent or RAG assistant | Tool and connector allow list for the agent identity | Per-object checks in the user's context on every tool call and retrieved chunk |
Small versus large organisations: a small team gets most of the value from strict coarse roles plus a shared data access helper that enforces tenant and owner. A large estate with many services and data owners benefits from an externalised engine, a shared schema of resource types and decision logs in the SIEM, because consistency across teams becomes the dominant risk. Use the Authorisation Maturity Model to judge which stage you are at.
Worked scenario: a claims platform with an AI assistant
An insurer runs a claims API used by adjusters, team leads and a new AI assistant that summarises claim files.
- Coarse layer: the identity provider assigns the claims application to adjusters and leads. The gateway accepts only tokens with audience
claims-apiand scopeclaims:readorclaims:write. The assistant's workload identity may call only the search and read endpoints. - Fine layer: an adjuster may read claims assigned to them or to their team; a lead may read all claims in their region; nobody outside the special investigations unit may read claims flagged for fraud review. Assignment and team membership are relationships; region and fraud flag are attributes. A
forbidstyle guardrail blocks fraud-flagged claims regardless of any other grant. - Lists: the claims queue uses a list-objects query, so hidden claims never appear in counts.
- Assistant: when an adjuster asks the assistant about a claim, the assistant passes the adjuster's identity (token exchange or on-behalf-of), and both the vector filter and the final re-check use the adjuster's permissions. The assistant cannot surface a fraud-flagged note even though its own service identity can index it.
- Evidence: every decision is logged with claim ID, decision and determining rule; quarterly reviews cover role assignments (coarse) and the policy set plus relationship sources (fine).
Implementation roadmap
- Inventory decisions, not just roles: list every endpoint and data path, and mark which decision each makes today and at what granularity. Endpoints that take an object ID with no object check are the first fix.
- Tighten the coarse layer: deny by default at the gateway, split broad scopes, scope cloud roles to named resources. Run new gateway rules in log-only mode for a week and compare against traffic before enforcing.
- Centralise the fine check path: one helper, library or policy call per resource type. Start with the highest-value object (customer, claim, document).
- Shadow-evaluate new policies: compute the new decision alongside the old one and log disagreements, then switch enforcement per resource type. Keep the old path behind a feature flag for rollback.
- Extend to lists, exports and agents: these are where checks are most often missed.
- Operationalise: decision logs to the SIEM, tests in CI, relationship sync monitoring, review cycles for policies and attribute sources.
Detection and measurement
- Object check coverage: percentage of endpoints taking an object identifier that call the fine-grained check (target 100%).
- Wildcard grants: count of roles, policies and app permissions with tenant-wide or all-resource reach, trending down.
- Denial anomalies: spikes in object-level denials per caller, a common signal of ID enumeration.
- Relationship sync lag: time between a change in the source system and the authorisation store reflecting it.
- Decision latency: p95 and p99 per engine against the agreed budget.
- Shadow disagreement rate: during migration, the share of decisions where old and new logic differ.
Framework mapping
| Framework | Reference | Relevance |
|---|---|---|
| NIST SP 800-53 Rev. 5 | AC-3 Access Enforcement; AC-3(7) Role-based Access Control; AC-3(13) Attribute-based Access Control | Coarse (role) and fine (attribute) enforcement are both enhancements of the same control |
| NIST SP 800-53 Rev. 5 | AC-6 Least Privilege; AC-24 Access Control Decisions; AU-12 Audit Record Generation | Granularity of grants, where decisions are made, and decision logging |
| NIST CSF 2.0 | PR.AA-05 | Access permissions defined in policy, enforced and reviewed with least privilege and separation of duties |
| ISO/IEC 27001:2022 Annex A | A.5.15 Access control; A.5.18 Access rights; A.8.3 Information access restriction | Policy, provisioning and restriction of access to information |
| CIS Controls v8.1 | Control 6 Access Control Management (6.8 role-based access control) | Defining and maintaining roles and their access |
| OWASP API Security Top 10 2023 | API1, API3, API5 | Object, property and function level authorisation failures |
| OWASP Top 10 for LLM Applications 2025 | LLM06, LLM08 | Agent permissions and permission-aware retrieval |
| MITRE ATT&CK | T1078 Valid Accounts; T1213 Data from Information Repositories | Broad grants turn a valid stolen credential into bulk data access |
Common failure patterns
- "The gateway checks auth": treating a valid token and scope as permission for every object behind the API.
- Service account as the user: agents, report generators and integrations fetching data with their own broad identity and showing it to whoever asked.
- Index-time ACLs only: RAG chunks carry the permissions from the day they were embedded and are never re-checked.
- Fine-grained in the UI: hiding buttons and rows in the front end while the API returns everything.
- Unreviewable policy: fine-grained rules with no tests, no owners and no decision logs, so nobody can say who can see what.
Practitioner checklist
- Document for each API which decisions are coarse (gateway, IAM) and which are fine (service, data layer).
- Confirm every endpoint, export and search that accepts an object ID performs an object-level check.
- Replace wildcard resources in workload roles with named resources or tag conditions.
- Route all fine-grained checks for a resource type through one helper or policy call.
- Use list-objects or query filters for lists so hidden objects never leak through counts.
- Run agents and retrievers in the requesting user's context and re-check retrieved chunks at return time.
- Log decisions with object ID and determining rule, and alert on policy evaluation errors.
- Set and monitor a latency budget and a relationship sync lag target.
- Review broad grants quarterly and fine-grained policy sets with their owners.
Standards and references
- NIST: SP 800-162 Guide to Attribute Based Access Control (ABAC) Definition and Considerations
- OWASP: API Security Top 10 2023
- OWASP: Top 10 for LLM Applications 2025
- OWASP: Authorization Cheat Sheet
- Google Research: Zanzibar, Google's Consistent, Global Authorization System
- Cedar: Authorization
- OpenFGA: Concepts
- Open Policy Agent: Policy Performance
Related NHI Mgmt Group resources: Authorisation Models Guide · Fine-Grained Authorisation Guide · Top 10 Authorisation Mistakes · AI Agent Authorisation Guide · Authorisation Maturity Model