A required tenant claim should fail token validation before a service attempts tenant authorization.
Spring JWT tenant claims: reject missing or malformed ownership before conversion
Reject absence at the decoder
The local decoder checks that tenant_id is present and matches the fixture's tenant-[a-z]+ format. A signed, unexpired token with the correct scope but no tenant_id returns 401. The controller does not receive a null principal and try to guess a tenant from the path. The format rule is only syntax; it does not prove that the issuer was allowed to grant tenant-east.
Issuer, audience, timestamps and signature are checked in the same decoder chain. A token that has a valid tenant string but the wrong audience is still not for this API. The trust fixture covers those separate rejections. The principal converter runs only after the decoder accepts the token in this bounded context.
Make claim changes a migration
If the identity provider changes claim names or casing, clients can suddenly receive 401. Publish a claim contract with allowed values and an overlap plan. Do not silently fall back to a request header during migration; that turns a missing signed claim into caller-controlled identity. Treat an unknown tenant as denied even if its string matches the syntax rule.
Checked source
jwt -> {
String tenant = jwt.getClaimAsString("tenant_id");
return tenant != null && tenant.matches("tenant-[a-z]+")
? OAuth2TokenValidatorResult.success()
: OAuth2TokenValidatorResult.failure(new OAuth2Error("invalid_tenant"));
}Verification boundary
JwtTenantBoundaryTest.missingTenantClaimIsRejectedByTheDecoder runs in the downloadable Spring source kit. The excerpt is shortened; the kit contains the complete test.
Costs and limits
The regex is a small local syntax check. This test does not fetch tenant membership, check revoked tokens or exercise a remote authorization server. Regex complexity here is linear in a short claim; production should also bound token size at the HTTP edge.
Common Mistakes
- Do not replace a missing signed claim with an untrusted header.
- Do not mistake a well-formed tenant string for authorized membership.
- Do not let a null claim reach repository selection.
Read next
Spring Security JWT tenant principal: map only a validated claim, Spring Security JWT resource server: validate trust before checking scope, Spring Security scope versus tenant ownership: two separate decisions.
