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);
[Route("/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 NameThe 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 readsuserand 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.
A members-only part of a public page#
[Authorize(Name)] works on a component a page RENDERS, not only on a page. Put it on the part of a public page that
is for some visitors only — an "Enrol" button for members — and the public page may place it: for a visitor, or for a
signed-in user who does not satisfy the policy, nothing is there; for one who does, it renders and runs. The server
never sends the component to anyone who does not satisfy the policy, and the functions it calls keep their own
[Authorize], checked when they are called.
[Principal] entity Member { string Email; }
[Role] enum Level { Member, Coach }
[RoleGrant] entity Grant { [Required] Member Holder; [Required] Level Level; }
policy Members => Grant.Any(g => g.Holder == user && g.Level == Level.Member);
entity Enrolment { [Required, MaxLength(50)] string Course; }
[Authorize(Members)]
void Enrol(string course) { new Enrolment { Course = course }; UnitOfWork.Commit(); }
[Authorize(Members)]
component EnrolButton(string course) {
action DoEnrol() { Enrol(course); }
render { Button("Enrol in " + course, onPress: DoEnrol); }
}
[Route("/classes")]
[AllowAnonymous]
[Render(CSR)]
component Classes() {
render {
Stack {
Text("Yoga, Tuesday 18:00");
EnrolButton("yoga"); // members see the button; everyone else sees the timetable
}
}
}A bare [Authorize] names no policy and is no boundary — every component already requires a signed-in caller — so
a public page still cannot place one; name the policy (policy SignedIn => IsAuthenticated; for "anyone signed in").
When the members-only part is a single button, you do not need a component for it: put [Authorize] (or
[Authorize(Policy)]) on the action the button runs, and the public page hides the button from anyone who does
not pass and the server refuses the action for them — see [Authorize] on an action.
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 }
[RoleGrant] entity UserRole { [Required] User User; [Required] Role Role; }
policy Admins => UserRole.Any(r => r.User == user && r.Role == Role.Admin);
[Route("/admin")]
[Render(CSR)]
[Authorize(Admins)]
component AdminPanel() {
render { Text("Admin only"); }
}See also#
- routes and pages — protected-by-default routing and
[AllowAnonymous] - component — the page a policy gates
- creating & saving data — a create obeys the entity's write permissions, a related access check
- [Authorize] on an action —
[Authorize]on one action of a public page, for a single members-only button