# page authorization (policies)

> 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.

<!-- id: ui-authorize · area: ui · stability: stable · html: https://osysharp.com/reference/ui/authorize/ -->

## Summary        {#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).

```osy syntax
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      {#signature}
```osy syntax
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    {#description}
Pages are **protected by default** (see [routes and pages](https://osysharp.com/reference/ui/routing/)): 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       {#examples}
A role-gated admin page — a reusable `Admins` policy plus a page that requires it:

```osy title="admin-page" test app=ui-authorize
[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       {#see-also}
- [routes and pages](https://osysharp.com/reference/ui/routing/) — protected-by-default routing and `[AllowAnonymous]`
- [component](https://osysharp.com/reference/ui/component/) — the page a policy gates
- [creating & saving data](https://osysharp.com/reference/ui/data-mutation/) — a create obeys the entity's write permissions, a related access check
