Real World Example

Where You See This

  • Razorpay, Stripe, Google Cloud APIs: every API call carries a bearer token that the API validates.
  • Banks (open banking / account aggregators): APIs accept tokens only with a specific audience and scope.
  • Large Indian e-commerce backends run hundreds of services; each one is a resource server trusting one central identity provider.

ShopEase order-service

Customers may only see their own orders, sellers see orders for their products, and admins see everything. The token supplies identity; the service enforces ownership:

@Service
public class OrderService {

    private final OrderRepository orders;

    public OrderService(OrderRepository orders) { this.orders = orders; }

    @PreAuthorize("hasAuthority('SCOPE_orders.read')")
    public List<OrderDto> findByCustomer(String customerId) {
        return orders.findByCustomerIdOrderByPlacedAtDesc(customerId).stream().map(OrderDto::from).toList();
    }

    // Ownership check: the order must belong to the caller, unless the caller is an admin
    @PostAuthorize("returnObject.customerId() == authentication.token.subject or hasRole('ADMIN')")
    public OrderDto findById(Long id) {
        return orders.findById(id).map(OrderDto::from)
                .orElseThrow(() -> new OrderNotFoundException(id));
    }

    @PreAuthorize("hasRole('SELLER')")
    public List<OrderDto> ordersForSeller(String sellerId) {      // controller passes jwt.getSubject()
        return orders.findBySellerId(sellerId).stream().map(OrderDto::from).toList();
    }
}

IDOR attempt blocked

Asha tries to read Ravi's order 1077:

curl -i http://localhost:8081/api/orders/1077 -H "Authorization: Bearer $ASHA_TOKEN"

Example output

HTTP/1.1 403
Content-Type: application/json

{"type":"about:blank","title":"Forbidden","status":403,"detail":"Access Denied","instance":"/api/orders/1077"}

Tip: authentication.token.subject works because the principal is a JwtAuthenticationToken. Ownership checks like this stop IDOR (insecure direct object reference), which is the #1 risk in the OWASP Top 10 (Broken Access Control).