Summary#
Two built-in when predicates describe the request's principal, for use in an entity security { } block:
IsAuthenticated— a signed-in user is making the request.IsAnonymous— no one is signed in (the unauthenticated visitor).IsAuthenticated || IsAnonymous— everyone, stated explicitly.
They exist because a bare unqualified allow — any verb, with no when guard and no where row filter — is a
compile error. It hands that verb to every caller, anonymous included, and the runtime consults nothing: on a read
it is the shape by which a [Principal] leaks its own credential columns, and on a delete it is worse. So you must
say who: a role, a where filter, or one of these predicates.
[Principal] entity User {
string Email;
security {
allow read when IsAuthenticated; // any signed-in user may read the directory (not the world)
}
}Signature#
allow read when IsAuthenticated; // a signed-in user
allow read when IsAnonymous; // an unauthenticated visitor
allow read when IsAuthenticated || IsAnonymous; // everyone, explicitly
allow read when IsAuthenticated || IsStaff; // compose with your own policiesUsable in a when guard or a policy body (a per-request fact about the principal — not a where row filter), and
usable whether or not the app declares a [Principal]: an app with none has exactly one kind of caller, so
IsAnonymous is true there and IsAuthenticated is false. IsAuthenticated and IsAnonymous are reserved — a
policy may not take those names.
Description#
The rule applies to every app and all four verbs, whether or not a [Principal] is declared. It once fired only
for read, and only on apps that had a principal; both exemptions are gone. The principal exemption in particular was
a trap rather than a kindness — a principal-less app was let off, and then silently inherited every unqualified grant
the day it declared a [Principal], having never been asked. These two predicates are always bound, so an app with
no principal can still say exactly what it means (when IsAnonymous).
deny has its own rule rather than an exemption: an entity-level deny with no when/where is refused too — under deny-all it subtracts from nothing, or silently cancels the grant above it. A FIELD-scoped one (deny read Secret;) stays legal: property rules replace the entity's rather than layering on them, so it is the only way to say "nobody, ever". ⚠ A CREDENTIAL column is the one exception, and it is refused for a reason worth knowing: masking it from everyone masks it from your own sign-in too, so Security.VerifyPassword compares against null and a correct password is refused exactly like a wrong one. Condition that one on your auth policy — deny read PasswordHash when !IsAuthenticator;.
The predicates read as intent:
IsAuthenticatedgrants the read to anyone signed in, regardless of role — the common "members can see the directory, the public cannot" case, without inventing a role for it.IsAnonymousgrants it to the not-signed-in visitor — a genuinely public page or catalog. (It is the identity of the request, distinct from any role a user account might hold.)IsAuthenticated || IsAnonymousis the honest way to write "everyone": broad, but deliberate, and greppable — a reader sees that the openness was chosen, not forgotten.
Prefer the narrowest that is true. Reach for the || form only when a read really is public; reach for a role or a
where Owner == user filter when it is not.
Examples#
A [Principal] with a credential column, a members-only directory read, and a genuinely public catalog:
[Role] enum AppRole { Authenticator }
entity RoleGrant { User User; [Required] AppRole Role; }
// The leash the auth flow is checked against — the credential is masked from everyone EXCEPT this.
policy IsAuthenticator => RoleGrant.Any(g => g.User == user && g.Role == AppRole.Authenticator);
[Principal] entity User {
string Email;
security {
allow read when IsAuthenticated; // signed-in users see the directory; the world does not
}
}
entity Announcement {
string Body;
security {
allow read when IsAuthenticated || IsAnonymous; // truly public — everyone, said out loud
}
}See also#
- security { } — the
security { }block these predicates live in - secure by default (deny-all) — the deny-all posture that makes an unmentioned grant a denial
- auth bootstrap (login, before anyone is signed in) — login/signup before a principal exists, run as a declared role
- public pages (what a signed-out visitor can see and do) —
IsAnonymousin practice: what a signed-out visitor actually sees, and the half that is easy to forget