A caller-supplied tenant header is request data and must not replace the tenant identity established by authentication.
Spring tenant headers: prove they cannot override a signed identity
The header changes nothing in the fixture
A valid tenant-east token requests the east row while sending X-Tenant: tenant-west. The result is still tenant-east:accepted. The same token requests the tenant-west route and sets the header to tenant-west; the service rule returns 403. Those cases show that this local controller ignores the header for ownership. The URL path is also only a requested resource, not authority.
A reverse proxy may inject a trusted tenant header after authentication in some architectures. That requires a strict network trust boundary, header stripping at the edge and a documented mapping to the authenticated principal. This source kit has no such proxy. Treat its headers as untrusted. The signed claim supplies the fixture's principal, and the SQL predicate carries it to storage.
Test the negative route
A positive request with an irrelevant spoofed header is not enough: the cross-tenant route must still fail when the attacker aligns both path and header. The test checks both. It does not prove that every ingress path strips a tenant header; deployed proxy tests are still needed before relying on header-based identity anywhere in the system.
Checked source
client.perform(get("/contract/tenant/tenant-west")
.header("Authorization", "Bearer " + token("tenant-east", "receipt.read"))
.header("X-Tenant", "tenant-west"))
.andExpect(status().isForbidden());Verification boundary
JwtTenantBoundaryTest.trustedTenantClaimPassesTheMatchingServiceRule and JwtTenantBoundaryTest.anotherTenantIsForbiddenAfterAuthentication runs in the downloadable Spring source kit. The excerpt is shortened; the kit contains the complete test.
Costs and limits
The test runs one local filter chain and controller. It does not include a CDN, gateway or reverse proxy. No additional database work is performed for the denied call; a production service should verify this side-effect boundary explicitly.
Common Mistakes
- Do not accept a tenant header from the open internet as identity.
- Do not treat path parameters as proof of ownership.
- Do not test only the positive request when spoofing is the risk.
Read next
Spring Security JWT tenant principal: map only a validated claim, Spring Security scope versus tenant ownership: two separate decisions, Spring JdbcTemplate tenant predicates: put ownership in the SQL query.
