# GraphQL resolver missing object authorization

Resolvers acting on an object ID without checking caller ownership, role, or tenant

_Area: Web access control and IDOR. Seen in 15 disclosed reports; 6 confirmed bounties, median $2,500, range $500-$25,000; latest 2026-06-29. Updated 2026-10-23._

## How it works
Authorization is enforced somewhere in the request path but not at the resolver that actually returns or mutates the object. The parent query, the UI, or a field-level scope decorator is trusted to gate access, while the resolver behind `node(id:)`, a bare-ID query, or an undocumented mutation acts on whatever object is named. The broken assumption: that knowing an ID is not enough, and that the caller's identity, role, and tenant are checked wherever data is fetched. The invariant that must hold: an object is returned or changed only if the authenticated principal is authorized for that specific object, regardless of how the ID was obtained. Object-level authorization is distinct from field-level scope decorators (a scope may permit reading a field type while saying nothing about which object instance the caller may touch) and from function-level authorization (BFLA), where a resolver is reachable by a role that should never call it at all.

## Where it shows up
- `node(id:)` / global-ID (`gid://...`) resolvers, especially newly added types [E1](https://bugbountylibrary.com/r/h1%3A1618347.md)
- Queries or mutations taking bare sequential integer IDs [E2](https://bugbountylibrary.com/r/blog%3Awiz-io-blog-red-agent-pov-bola.md)[E4](https://bugbountylibrary.com/r/h1%3A2528293.md)
- Persisted operations whose client-controlled variables name a resource (e.g. subredditName) [E3](https://bugbountylibrary.com/r/h1%3A1658418.md)
- Mutations authorizing on a nested object ID (e.g. `socialLinks[].id`) against the parent session rather than object ownership [E7](https://bugbountylibrary.com/r/h1%3A1661113.md)
- Mutations taking a global ID for an object the caller does not administer [E8](https://bugbountylibrary.com/r/h1%3A1501611.md)
- Resolvers whose access check diverges by object lifecycle state (e.g. pending invite) [E6](https://bugbountylibrary.com/r/h1%3A2312029.md)
- Anonymous or lowest-privilege sessions reaching full resolvers [E1](https://bugbountylibrary.com/r/h1%3A1618347.md)[E2](https://bugbountylibrary.com/r/blog%3Awiz-io-blog-red-agent-pov-bola.md)

## How to review
For every resolver that returns or mutates an object, confirm the caller's authorization is evaluated against that specific object before data is returned or changed: ownership, role/scope, and tenant/namespace must all be checked at the resolver, not only at the parent query or UI. For multi-tenant resolvers, the object's tenant must equal the caller's tenant (e.g. a model's project must belong to the caller's namespace) [E4](https://bugbountylibrary.com/r/h1%3A2528293.md). Concretely:
- Schema diff: enumerate every query/mutation, including undocumented ones, and replay each from a zero-permission account.
- Resolver-vs-parent authz diff: flag any resolver that inherits the parent's check without its own.
- Distinguish from BFLA: BFLA is a role that should never reach the resolver; object-level is a legitimate role reaching an object it does not own. Test both.
- Lowest-privilege token (including anonymous) against resources you do not own [E1](https://bugbountylibrary.com/r/h1%3A1618347.md)[E2](https://bugbountylibrary.com/r/blog%3Awiz-io-blog-red-agent-pov-bola.md).
- Persisted-operation variable binding: variables are client-controlled even when the operation is fixed [E3](https://bugbountylibrary.com/r/h1%3A1658418.md).
- For mutations, re-fetch the object to confirm side effects rather than trusting the response, since serializers can mask them; a misleading "not found" response can hide a successful change [E8](https://bugbountylibrary.com/r/h1%3A1501611.md).

## Variants seen
- Unauthenticated `node(id:)` returning private asset groups and report titles [E1](https://bugbountylibrary.com/r/h1%3A1618347.md)
- Anonymous session reading/modifying itineraries via sequential booking IDs [E2](https://bugbountylibrary.com/r/blog%3Awiz-io-blog-red-agent-pov-bola.md)
- Mod-log operation missing moderator check via variable swap [E3](https://bugbountylibrary.com/r/h1%3A1658418.md)
- ML model queries missing project/namespace authz on incremental IDs [E4](https://bugbountylibrary.com/r/h1%3A2528293.md)
- Private report title leaked unauthenticated when a pending invite exists (lifecycle-state divergence) [E6](https://bugbountylibrary.com/r/h1%3A2312029.md)
- Social-link update authorizing by link ID, not ownership [E7](https://bugbountylibrary.com/r/h1%3A1661113.md)
- Archive/unarchive mutation on any program's scope by global ID [E8](https://bugbountylibrary.com/r/h1%3A1501611.md)

## Impact and severity
Exposed private program data, passenger PII, moderation logs, private ML models, and cross-user writes. Severity rises with unauthenticated reachability [E1](https://bugbountylibrary.com/r/h1%3A1618347.md)[E6](https://bugbountylibrary.com/r/h1%3A2312029.md), PII exposure [E2](https://bugbountylibrary.com/r/blog%3Awiz-io-blog-red-agent-pov-bola.md), and destructive side effects hidden behind benign-looking mutations [E8](https://bugbountylibrary.com/r/h1%3A1501611.md). Unguessable IDs are not a control: identifier enumeration on an unthrottled endpoint can confirm existence [E1](https://bugbountylibrary.com/r/h1%3A1618347.md). These are descriptive observations, not a scoring rubric.

## Evidence
- E1: https://bugbountylibrary.com/r/h1%3A1618347.md
- E2: https://bugbountylibrary.com/r/blog%3Awiz-io-blog-red-agent-pov-bola.md
- E3: https://bugbountylibrary.com/r/h1%3A1658418.md
- E4: https://bugbountylibrary.com/r/h1%3A2528293.md
- E5: https://bugbountylibrary.com/r/h1%3A528940.md
- E6: https://bugbountylibrary.com/r/h1%3A2312029.md
- E7: https://bugbountylibrary.com/r/h1%3A1661113.md
- E8: https://bugbountylibrary.com/r/h1%3A1501611.md
