Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Spring Security scope versus tenant ownership: two separate decisions

Last updated: 30 Sept 20264 min read
tutorial
IntermediateBy AITrove Editorial

A token scope permits a class of operation; tenant ownership decides which records that operation may touch.

Download Spring source kit

The same HTTP status can hide a different failure

In the checked web context, a signed token with receipt.write cannot call a receipt.read endpoint: the filter chain returns 403 before the service. A token with receipt.read but tenant-east cannot read the tenant-west path: the method rule returns 403. Both are forbidden, but the responsible layer differs. A valid scope does not grant all tenant rows.

The filter requires SCOPE_receipt.read. The managed reader uses @PreAuthorize to compare its tenant argument with authentication.name, which came from the validated JWT tenant_id. The JDBC query also has tenant_id in its predicate. This gives a database backstop if a caller changes routes or calls another service method. Roles and authorities have different naming rules; do not substitute a role string for a scope without an explicit mapping.

Trace decisions in order

First verify token trust and required claims. Then check endpoint scope, service ownership and row predicate. Record denial counts by layer without logging bearer tokens. If the user has access to several tenants, the identity system needs a membership rule for the selected tenant; this single-tenant fixture deliberately does not implement that model.

Checked source

Java
.authorizeHttpRequests(requests -> requests
    .requestMatchers("/contract/tenant/**").hasAuthority("SCOPE_receipt.read"))

@PreAuthorize("#p0 == authentication.name")
public String read(String tenant) { return scopedLookup(tenant); }

Verification boundary

JwtTenantBoundaryTest.wrongScopeIsForbiddenBeforeTheService and JwtTenantBoundaryTest.anotherTenantIsForbiddenAfterAuthentication runs in the downloadable Spring source kit. The excerpt is shortened; the kit contains the complete test.

Costs and limits

Two MockMvc requests check local filter and method outcomes. The fixture has no remote issuer, real tenant registry, token revocation, database row-level security or deployed gateway. Extra ownership checks add small local comparison cost; the database predicate must still be indexed for large tables.

Common Mistakes

  • Do not equate receipt.read with permission to read every receipt.
  • Do not infer the denial layer from HTTP 403 alone.
  • Do not let a URL tenant override the signed tenant identity.

Read next

Spring Security JWT tenant principal: map only a validated claim, Spring JdbcTemplate tenant predicates: put ownership in the SQL query, Spring method authorization: test the proxied service boundary, Spring Security roles and authorities: check the actual granted string.

spring
spring-boot
scope-versus-tenant-access
Storage details