Authorization Boundaries in APIs
Authorization boundaries define what an authenticated caller is permitted to do with a particular API resource or operation. In a Java backend, they should be enforced at trusted server-side boundaries using the caller identity, requested action, and resource context.
Authentication answers who the caller is; authorization answers what that caller may do. An API authorization boundary sits between an incoming request and the protected operation, where the server evaluates the caller's identity, required permission, and relevant resource or tenant context.
A common model is to express a decision as `isAllowed(subject, action, resource)`. For example, a caller may be allowed to read an order but not modify it, or may modify only orders belonging to the caller's tenant. The authorization check should use trusted server-side data rather than relying on identifiers, roles, or permissions supplied by the client.
In Java applications, authorization can be enforced at several layers, such as controller methods, service methods, or framework security rules. The service layer is often important because business operations may be reachable through multiple controllers or entry points; placing critical authorization only in one HTTP route can leave another path insufficiently protected.
Resource ownership and tenant isolation require more than checking a broad role. A rule such as `ROLE_USER` may establish that a caller has a general capability, while the resource check determines whether that specific user can access order `123`. For multi-tenant systems, the authorization decision should also bind the resource to the caller's permitted tenant or organization.
Authorization failures should be handled deliberately. A request from an authenticated user who lacks permission commonly results in `403 Forbidden`, while an unauthenticated request commonly results in `401 Unauthorized`; exact behavior depends on the API's security design.
Key points
- Authentication establishes identity; authorization determines permitted actions.
- Authorization should evaluate the subject, requested action, and protected resource.
- Do not trust client-supplied roles, ownership claims, or tenant identifiers.
- Enforce critical business authorization at a layer that covers every relevant entry point.
- Object-level and tenant-level checks are necessary when access depends on resource ownership or tenancy.
- Use `401` for missing/invalid authentication and `403` when an authenticated caller is not permitted, according to the API's contract.
Interview questions
What is the difference between authentication and authorization?
Authentication verifies who a caller is, while authorization determines what that caller is allowed to do. For an API, authentication might establish a user identity from a token, and authorization might determine whether that user can update an order. Authentication can succeed while authorization still fails. The authorization decision should be based on trusted identity and server-side policy.
Why is checking only a user's role insufficient for some API endpoints?
A role usually describes a broad capability, but many operations depend on the particular resource. For example, two users might both have an editor role while being restricted to different tenants. The authorization decision therefore needs object-level or tenant-level context in addition to the role. This prevents a valid role from becoming unrestricted access to every resource.
Where would you enforce authorization in a Java backend?
I would use framework-level security for broad request and method policies, but I would also protect business operations where resource-specific authorization is required. A service-layer check can prevent bypass through another controller, scheduled job, or internal entry point. The exact layering depends on the architecture, but critical authorization should not depend on a single HTTP route. Tests should exercise both permitted and forbidden resource combinations.
How would you design authorization for a multi-tenant API?
I would make tenant context an explicit part of the authorization model rather than treating it as an ordinary request parameter. The authenticated identity should establish which tenants or organizations the caller may access, and resource queries should constrain results to that authorized scope. For sensitive operations, I would verify both the caller's capability and the target resource's tenant association. Database-level constraints or query scoping can provide an additional defense against accidental cross-tenant access.
What is the difference between `401 Unauthorized` and `403 Forbidden`?
In common HTTP API security semantics, `401` indicates that the request lacks acceptable authentication credentials and the server cannot treat the caller as authenticated. `403` indicates that the server understands the caller's identity but refuses the requested operation because it is not permitted. The precise API behavior should follow its documented security contract. Authorization logic should not rely on clients interpreting these statuses as a substitute for server-side enforcement.