This is one area where building the core of your application should enforce the policy, regardless if it a JSON API, graphql, dead view or liveview.
The best example I am aware of is the Ash framework for building your application domain model inclusive of the access policy and not reimplemented inconsistenly in each access channel. Ash has a sophisticated policy engine with a clever sat solver for efficient evaluation, as well as lots of expressiveness in the policy rules. The logging and diagnostics on how a policy decision is reached is also awesome:
A check status of `?` implies that the solver did not need to determine that check.
Some checks may look like they failed when in reality there was no need to check them.
Look for policies with `✘` and `✓` in check statuses.
A check with a `⬇` means that it didn’t determine if the policy was authorized or forbidden, and so moved on to the next check.
`🌟` and `⛔` mean that the check was responsible for producing an authorized or forbidden (respectively) status.
If no check results in a status (they all have `⬇`) then the policy is assumed to have failed. In some cases, however, the policy
may have just been ignored, as described above.
Admins and managers can create posts | ⛔:
authorize if: actor.admin == true | ✘ | ⬇
authorize if: actor.manager == true | ✘ | ⬇
An example of a policy on a resource:
# Anything you can use in a condition, you can use in a check, and vice-versa
# This policy applies if the actor is a super_user
# Additionally, this policy is declared as a `bypass`. That means that this check is allowed to fail without
# failing the whole request, and that if this check *passes*, the entire request passes.
bypass actor_attribute_equals(:super_user, true) do
# This will likely be a common occurrence. Specifically, policies that apply to all read actions
policy action_type(:read) do
# unless the actor is an active user, forbid
forbid_unless actor_attribute_equals(:active, true)
# if the record is marked as public, authorize
authorize_if attribute(:public, true)
# if the actor is related to the data via that data's `owner` relationship, authorize
They just recently added a mix task for visually describing policy evaluations for documentation, an example:
More on Ash policy basics here: Ash Framework
A comprehensive approach to domain modelling and access policy is one of the key things that got my attention with Ash, being able to build the domain model and business rules for access in a mostly declartive fashion with powerful access policy and changeset / action handling (even for nested resources) really takes the drudgery out of building very capable applications, plus you get APIs almost for free, multi tenancy, sane transactional aware notification handling (eg pub sub notifications don’t get sent unless the transaction actually suceeds), and admin UIs which are great in development and early prototyping.
It’s definitely a different way to go vs writing it all yourself or modifying outputs from code generators which you then have to maintain and test. No doubt Ash does take some getting used to but the productivity gains are real and there really isnt any lock-in and plenty of escape hatches. Ash is worth a good hard look IMO vs solving all of the “table stakes” commodity problems that customers take for granted, you actually get to focus on the real customer problem and both develop and demonstrate value quickly.