A GraphQL field resolver must check the caller's tenant against the requested record, regardless of which fields the client selects.
Spring GraphQL tenant reads: scope every resolver before returning a record
Resolve identity outside the document
The test puts tenant north in the GraphQL execution context, then asks for RC-47. The resolver returns the matching record. The same ID under tenant south returns null, even when the document requests only id. An input argument or HTTP header supplied by the caller would not be a trusted tenant identity. In a deployed application, an authentication filter must establish the principal, and a GraphQL interceptor must attach its verified tenant to execution context. The HTTP tenant boundary is a separate test of that trust path.
Every root and nested fetcher that can expose tenant data needs its own ownership rule or a service method that enforces one centrally. A schema type being named Receipt carries no access control. Passing a whole JPA entity directly can expose fields later added to the schema through default property fetching; keep returned shapes narrow and test each field.
Avoid confusing absence with permission
Returning null for a cross-tenant receipt prevents the fixture from revealing the row. A real API must decide whether unknown and forbidden IDs are intentionally indistinguishable, then keep logging and audit detail private. The fixture has no login, token validation, membership registry or row-level database security. A transport-level test must prove the context came from verified credentials, not a test-supplied map.
Checked excerpt
private Receipt receipt(DataFetchingEnvironment environment) {
Receipt found = ledger.get(environment.getArgument("id"));
return found != null && found.tenant().equals(tenant(environment))
? found : null;
}Verification boundary
The Java 21 / Spring Boot 4 source kit checks selectedFieldsAreReturnedForAuthorizedTenant and tenantCannotReadAnotherTenantsReceipt.
Cost and limits
A tenant predicate must be pushed into the database query in a real repository; loading a row and then checking it still incurs the read and can leak through timing or logging. This in-memory fixture proves only the returned field decision.
Common Mistakes
- Do not trust a tenant value embedded in the GraphQL document.
- Do not authorize only the root query while nested fetchers bypass it.
- Do not treat this direct execution-context test as authentication coverage.
Read next
Spring method authorization: reject a cross-tenant read, Spring Security filter chain: authentication, CSRF and request order, Spring GraphQL service tests: stop short of claiming an HTTP endpoint, Spring GraphQL mutation versions: reject a stale stock reservation, Spring GraphQL schema nullability: trace one field failure through the response.
