Osy#the first language built for agents
Agents firstAgentic appsWorkflowsDurable Execution — built inSecurityTestingThe editorThe UI modelOne program

Reference / UI

page authorization (policies)

policy Name => <predicate>; + [Authorize(Name)] component Page() { … }

A `policy` names a reusable authorization predicate over the current user — e.g. `policy Admins => UserRole.Any(r => r.User == user && r.Role == Role.Admin)`. A page marks itself `[Authorize(Admins)]` to require it: an authenticated user who does not satisfy the predicate is refused the page. The reference is compile-checked — `[Authorize(Typo)]` is a build error, not a silent hole.

stable1 example compiled by CIuisecurityauthorization

Summary#

A policy names a reusable authorization predicate — a condition over the current user (user) that says who is allowed. A page requires a policy with [Authorize(Name)]; a logged-in user who does not satisfy the predicate is refused the page (the server returns it to no one who fails the check).

policy Admins => UserRole.Any(r => r.User == user && r.Role == Role.Admin);

[Page("/admin")]
[Authorize(Admins)]
component AdminPanel() { render { Text("secret"); } }

The reference is a checked symbol, not a string: [Authorize(Admins)] names the declared policy Admins, so a typo ([Authorize(Admin)]) is a compile error. A policy is declared once and reused across as many pages as need it.

Signature#

policy Name => <predicate over `user`>;   // a reusable named authorization predicate
[Authorize(Name)] component Page() { … }  // require it — the principal must satisfy Name

The predicate is ordinary Osy#: it reads user (the current principal) and queries your data. A role check is the common shape — RoleEntity.Any(r => r.User == user && r.Role == Role.X) — but any boolean predicate over the user works.

Description#

Pages are protected by default (see routes and pages): a routed component requires an authenticated principal unless it is [AllowAnonymous]. [Authorize(Name)] narrows that further — the authenticated principal must also satisfy the named policy. If they don't, the page is refused.

  • policy Name => <predicate>; — declares the predicate once, by name. It reads user and may query entities (typically a role-grant table). The same policy can gate many pages.
  • [Authorize(Name)] — attaches the policy to a page. The reference is compile-checked against the declared policies; an unknown name fails the build.
  • Fail closed — a policy that cannot be resolved or evaluated refuses the page. If the named policy is missing or its predicate errors, the page is refused, never served — authorization never falls open.

Authorization is enforced on the server, when the page's definition is requested — so it holds regardless of what the client does.

Examples#

A role-gated admin page — a reusable Admins policy plus a page that requires it:

[Principal] entity User { string Email; }
[Role] enum Role { Admin, Viewer }
entity UserRole { [Required] User User; [Required] Role Role; }

policy Admins => UserRole.Any(r => r.User == user && r.Role == Role.Admin);

[Page("/admin")]
[Render(CSR)]
[Authorize(Admins)]
component AdminPanel() {
  render { Text("Admin only"); }
}

See also#

Related

component

The one archetype for all UI: a bounded reactive unit — typed props, reactive members (fields, `live`…

routes and pages

How a component becomes a page: it declares a route with `[Page("/catalog/{slug}")]`, and navigating to a matching path…

creating & saving data

A UI `action` creates, updates and deletes data by writing `new Entity { … }`, assigning fields, and calling…