OAuth scopes answer: "what APIs may this token call?"
They do not answer: "may this user read this document?"
A token with documents:read can still be used against every document ID the holder can guess, unless your API binds subject + action + resource on every request.
Treat scopes as a coarse gate on the credential. Keep resource-level authorization inside your app — RBAC/ReBAC/ABAC checks that use the authenticated subject from the token, not a client-supplied owner id.
Same idea for "the JWT is valid": authentication succeeded. Authorization is a separate decision.
Top comments (1)
A useful test: Alice and Bob both have
documents:read, but belong to different tenants. The scope check passes for both; the API still needs to check access to the specific document.I work on Cedarling, where we express those rules as Cedar policies and evaluate principal, action, resource, and context. The backend enforces the decision before returning data: cedarling.dev (to see how to integrate Cedarling into js app) or docs.jans.io/stable/cedarling/ for all other bindings (rust, java, go, python, etc).
NB: With Cedarling you still can use RBAC/ReBAC/ABAC and even TBAC (Token-Based Access Control)