alepha@docs:~/docs/reference/primitives$
cat $owns.md | pretty
2 min read

#$owns

#Import

typescript
1import { $owns } from "alepha/security";

#Overview

Resource-scoped authorization gate.

Roles and permissions answer "what kind of user is this?". They cannot answer "does this user own row 42?", so that check ends up inline in every handler — where nothing enforces its presence and a forgotten call is a silent authorization hole.

$owns loads the row named by a route param, checks the caller against it, and publishes it via OwnedResourceProvider so the handler does not re-fetch what the gate already read.

Two checks, applied in order:

  1. Ownerrow[owner] === user.id.
  2. Membership — when via is set, a row in the join entity links the caller to this resource.

A privileged identity (user.ownership === false) bypasses both, matching the ownership semantics $secure already applies: an admin whose grant is not narrowed to rows they own. Note this is deliberately strict — an undefined ownership does not bypass, because undefined only means "no permission check ran", not "this caller is privileged".

typescript
 1class CampaignController { 2  read = $action({ 3    path: "/campaigns/:id", 4    use: [ 5      $secure(), 6      $owns({ 7        repository: () => this.campaigns, 8        param: "id", 9        owner: "createdBy",10        cast: Number,11        via: {12          repository: () => this.characters,13          resource: "campaignId",14          user: "userId",15        },16      }),17    ],18    handler: async () => this.owned.get<Campaign>(),19  });20}

#Options

Option Type Required Description
repository Object Yes Repository the guarded resource is loaded from, as a thunk
param string Yes Route param holding the resource id.
owner string Yes Column on the resource holding the owner's user id.
via Object No Membership fallback: a join entity linking users to resources
cast Object No Coerce the raw string route param before querying
message string No Message used for both the owner and the membership denial
secure Omit&lt;SecureOptions, "guard"&gt; No Additional $secure checks layered on top — roles, permissions, issuers.