Better Route ownership guards
Authentication establishes identity; ownership authorization establishes whether that identity may access this object.
Raw route
use BetterRoute\Middleware\Auth\OwnershipGuardMiddleware;
$guard = new OwnershipGuardMiddleware(
ownerResolver: static function ($context): ?int {
return my_resource_owner_id((int) $context->request->get_param('id'));
},
bypassCapability: 'manage_options',
deniedStatus: 404
);
$router->get('/records/(?P<id>\d+)', $handler)
->middleware([$auth, $guard])
->protectedByMiddleware('bearerAuth');
Run authentication before the guard. It resolves identity from the normalized auth.userId, then auth.subject, then the native WordPress current user. The owner resolver must load ownership server-side from the route resource; never trust a submitted owner ID.
Resource DSL
use BetterRoute\Resource\OwnedResourcePolicy;
Resource::make('records')
->policy(OwnedResourcePolicy::currentUserOwns(
ownerResolver: static fn (int $id): ?int => my_resource_owner_id($id),
ownedActions: ['get', 'update', 'delete'],
bypassCapability: 'manage_options',
allowListForAuthenticatedUsers: true
));
allowListForAuthenticatedUsers: true grants list permission to logged-in WordPress users; it does not filter the result. Apply an owner predicate in the repository/query, or disable the generated list permission, before exposing user-owned collections.
Rules
- Prefer denial as
404when revealing object existence would leak data. Use403only for an intentionally discoverable object. - Use narrowly scoped, reviewed bypass capabilities.
- Check ownership against the current stored record during writes, not a stale client copy.
- Cover
get,update, anddeleteindependently; list filtering is a separate control. - Combine write authorization with optimistic locking and atomic idempotency when concurrency or duplicate side effects matter.
Test another user's ID, absent object, anonymous access, subject-only identity, native WordPress identity, admin bypass, and list-result isolation.
Source references: src/Middleware/Auth/OwnershipGuardMiddleware.php, src/Resource/OwnedResourcePolicy.php.
References
- Official documentation: https://lonsdale201.github.io/better-docs/docs/better-route/agents