Firebase Security Rules leverage flexible, powerful, custom languages that support a wide
range of complexity and granularity. You can make your Rules as
specific or as general as makes sense for your app. Realtime Database rules use
a syntax that looks like JavaScript in a JSON structure.
Cloud Firestore and Cloud Storage rules use a language based on the
Common Expression Language (CEL)
, that
builds on CEL with
match
and
allow
statements that support conditionally
granted access.
Because these are custom languages, however, there is a learning
curve. Use this guide to better understand the Rules language as you
dive deeper into more complex rules.
Cloud Firestore
The basic elements of a rule in Cloud Firestore and Cloud Storage are as
follows:
- The
service
declaration: Declares the Firebase product the rules apply to.
- The
match
block: Defines a path in the database or storage bucket the rules
apply to.
- The
allow
statement: Provides conditions for granting access, differentiated
by methods. The supported methods include:
get
,
list
,
create
,
update
,
delete
, and the convenience methods
read
and
write
.
- Optional
function
declarations: Provide the ability to combine and wrap
conditions for use across multiple rules.
The
service
contains one or more
match
blocks with
allow
statements that
provide conditions granting access to requests. The
request
and
resource
variables are available for use in rule conditions.
The Firebase Security Rules language also supports
function
declarations.
Syntax version
The
syntax
statement indicates the version of the Firebase Rules language used
to write the source. The latest version of the language is
v2
.
rules_version = '2';
service cloud.firestore {
...
}
If no
rules_version
statement is supplied, your rules will be evaluated using
the
v1
engine.
Service
The
service
declaration defines which Firebase product, or service, your rules
apply to. You can only include one
service
declaration per source file.
Cloud Firestore
service cloud.firestore {
// Your 'match' blocks with their corresponding 'allow' statements and
// optional 'function' declarations are contained here
}
Cloud Storage
service firebase.storage {
// Your 'match' blocks with their corresponding 'allow' statements and
// optional 'function' declarations are contained here
}
If you're defining rules for both Cloud Firestore and Cloud Storage
using the Firebase CLI, you'll have to maintain them in separate files.
Match
A
match
block declares a
path
pattern that is matched against the
path for the requested operation (the incoming
request.path
). The body
of the
match
must have one or more nested
match
blocks,
allow
statements,
or
function
declarations. The path in nested
match
blocks is relative to
the path in the parent
match
block.
The
path
pattern is a directory-like name that may include variables or
wildcards. The
path
pattern allows for single-path segment and multi-path
segment matches. Any variables bound in a
path
are visible within the
match
scope or any nested scope where the
path
is declared.
Matches against a
path
pattern may be partial or complete:
- Partial matches:
The
path
pattern is a prefix-match of the
request.path
.
- Complete matches:
The
path
pattern matches the entire
request.path
.
When a
complete
match is made the rules within the block are evaluated. When
a
partial
match is made the nested
match
rules are tested to see whether
any nested
path
will
complete
the match.
The rules in each
complete
match
are evaluated to determine whether
to permit the request. If any matching rule grants access, the request is
permitted. If no matching rule grants access, the request is denied.
// Given request.path == /example/hello/nested/path the following
// declarations indicate whether they are a partial or complete match and
// the value of any variables visible within the scope.
service firebase.storage {
// Partial match.
match /example/{singleSegment} { // `singleSegment` == 'hello'
allow write; // Write rule not evaluated.
// Complete match.
match /nested/path { // `singleSegment` visible in scope.
allow read; // Read rule is evaluated.
}
}
// Complete match.
match /example/{multiSegment=**} { // `multiSegment` == /hello/nested/path
allow read; // Read rule is evaluated.
}
}
As the example above shows, the
path
declarations supports the following
variables:
- Single-segment wildcard:
A wildcard variable is declared in a path by
wrapping a variable in curly braces:
{variable}
. This variable is accessible
within the
match
statement as a
string
.
- Recursive wildcard:
The recursive, or multi-segment, wildcard matches
multiple path segments at or below a path. This wildcard matches all paths
below the location you set it to. You can declare it by adding the
=**
string at the end of your segment variable:
{variable=**}
. This variable is
accessible within the
match
statement as a
path
object.
Allow
The
match
block contains one or more
allow
statements. These are your actual
rules. You can apply
allow
rules to one or more methods. The conditions
on an
allow
statement must evaluate to true for Cloud Firestore or Cloud Storage
to grant any incoming request. You can also write
allow
statements without
conditions, for example,
allow read
. If the
allow
statement doesn't include
a condition, however, it always allows the request for that method.
If any of the
allow
rules for the method are satisfied, the request is
allowed. Additionally, if a broader rule grants access, Rules grant
access and ignore any more granular rules that might limit access.
Consider the following example, where any user can read or delete any of their
own files. A more granular rule only allows writes if the user requesting the
write owns the file and the file is a PNG. A user can delete any files at the
subpath ? even if they're not PNGs ? because the earlier rule allows it.
service firebase.storage {
// Allow the requestor to read or delete any resource on a path under the
// user directory.
match /users/{userId}/{anyUserFile=**} {
allow read, delete: if request.auth != null && request.auth.uid == userId;
}
// Allow the requestor to create or update their own images.
// When 'request.method' == 'delete' this rule and the one matching
// any path under the user directory would both match and the `delete`
// would be permitted.
match /users/{userId}/images/{imageId} {
// Whether to permit the request depends on the logical OR of all
// matched rules. This means that even if this rule did not explicitly
// allow the 'delete' the earlier rule would have.
allow write: if request.auth != null && request.auth.uid == userId && imageId.matches('*.png');
}
}
Method
Each
allow
statement includes a method that grants access for incoming
requests of the same method.
Method
|
Type of request
|
Convenience methods
|
read
|
Any type of read request
|
write
|
Any type of write request
|
Standard methods
|
get
|
Read requests for single documents or files
|
list
|
Read requests for queries and collections
|
create
|
Write new documents or files
|
update
|
Write to existing database documents or update file metadata
|
delete
|
Delete data
|
You can't overlap read methods in the same
match
block or
conflicting write methods in the same
path
declaration.
For example, the following rules would fail:
service bad.example {
match /rules/with/overlapping/methods {
// This rule allows reads to all authenticated users
allow read: if request.auth != null;
match another/subpath {
// This secondary, more specific read rule causes an error
allow get: if request.auth != null && request.auth.uid == "me";
// Overlapping write methods in the same path cause an error as well
allow write: if request.auth != null;
allow create: if request.auth != null && request.auth.uid == "me";
}
}
}
Function
As your security rules become more complex, you may want to wrap sets of
conditions in functions that you can reuse across your ruleset. Security rules
support custom functions. The syntax for custom functions is a bit like JavaScript,
but security rules functions are written in a domain-specific language
that has some important limitations:
- Functions can contain only a single
return
statement. They cannot
contain any additional logic. For example, they cannot execute loops or
call external services.
- Functions can automatically access functions and variables from the scope
in which they are defined. For example, a function defined within
the
service cloud.firestore
scope has access to the
resource
variable
and built-in functions such as
get()
and
exists()
.
- Functions may call other functions but may not recurse. The total call
stack depth is limited to 20.
- In rules version
v2
, functions can define variables using the
let
keyword.
Functions can have up to 10 let bindings, but must end with a return
statement.
A function is defined with the
function
keyword and takes zero or more
arguments. For example, you may want to combine the two types of conditions used
in the examples above into a single function:
service cloud.firestore {
match /databases/{database}/documents {
// True if the user is signed in or the requested data is 'public'
function signedInOrPublic() {
return request.auth.uid != null || resource.data.visibility == 'public';
}
match /cities/{city} {
allow read, write: if signedInOrPublic();
}
match /users/{user} {
allow read, write: if signedInOrPublic();
}
}
}
Here is an example showing function arguments and let assignments. Let assignment
statements must be separated by semi-colons.
function isAuthorOrAdmin(userId, article) {
let isAuthor = article.author == userId;
let isAdmin = exists(/databases/$(database)/documents/admins/$(userId));
return isAuthor || isAdmin;
}
Note how the
isAdmin
assignment enforces a lookup of the admins collection. For
lazy evaluation without requiring unneeded lookups, take advantage of the
short-circuiting nature of
&&
(AND) and
||
(OR) comparisons to call a second
function only if
isAuthor
is shown to be true (for
&&
comparisons) or false
(for
||
comparisons).
function isAdmin(userId) {
return exists(/databases/$(database)/documents/admins/$(userId));
}
function isAuthorOrAdmin(userId, article) {
let isAuthor = article.author == userId;
// `||` is short-circuiting; isAdmin called only if isAuthor == false.
return isAuthor || isAdmin(userId);
}
Using functions in your security rules makes them more maintainable as the
complexity of your rules grows.
Cloud Storage
The basic elements of a rule in Cloud Firestore and Cloud Storage are as
follows:
- The
service
declaration: Declares the Firebase product the rules apply to.
- The
match
block: Defines a path in the database or storage bucket the rules
apply to.
- The
allow
statement: Provides conditions for granting access, differentiated
by methods. The supported methods include:
get
,
list
,
create
,
update
,
delete
, and the convenience methods
read
and
write
.
- Optional
function
declarations: Provide the ability to combine and wrap
conditions for use across multiple rules.
The
service
contains one or more
match
blocks with
allow
statements that
provide conditions granting access to requests. The
request
and
resource
variables are available for use in rule conditions.
The Firebase Security Rules language also supports
function
declarations.
Syntax version
The
syntax
statement indicates the version of the Firebase Rules language used
to write the source. The latest version of the language is
v2
.
rules_version = '2';
service cloud.firestore {
...
}
If no
rules_version
statement is supplied, your rules will be evaluated using
the
v1
engine.
Service
The
service
declaration defines which Firebase product, or service, your rules
apply to. You can only include one
service
declaration per source file.
Cloud Firestore
service cloud.firestore {
// Your 'match' blocks with their corresponding 'allow' statements and
// optional 'function' declarations are contained here
}
Cloud Storage
service firebase.storage {
// Your 'match' blocks with their corresponding 'allow' statements and
// optional 'function' declarations are contained here
}
If you're defining rules for both Cloud Firestore and Cloud Storage
using the Firebase CLI, you'll have to maintain them in separate files.
Match
A
match
block declares a
path
pattern that is matched against the
path for the requested operation (the incoming
request.path
). The body
of the
match
must have one or more nested
match
blocks,
allow
statements,
or
function
declarations. The path in nested
match
blocks is relative to
the path in the parent
match
block.
The
path
pattern is a directory-like name that may include variables or
wildcards. The
path
pattern allows for single-path segment and multi-path
segment matches. Any variables bound in a
path
are visible within the
match
scope or any nested scope where the
path
is declared.
Matches against a
path
pattern may be partial or complete:
- Partial matches:
The
path
pattern is a prefix-match of the
request.path
.
- Complete matches:
The
path
pattern matches the entire
request.path
.
When a
complete
match is made the rules within the block are evaluated. When
a
partial
match is made the nested
match
rules are tested to see whether
any nested
path
will
complete
the match.
The rules in each
complete
match
are evaluated to determine whether
to permit the request. If any matching rule grants access, the request is
permitted. If no matching rule grants access, the request is denied.
// Given request.path == /example/hello/nested/path the following
// declarations indicate whether they are a partial or complete match and
// the value of any variables visible within the scope.
service firebase.storage {
// Partial match.
match /example/{singleSegment} { // `singleSegment` == 'hello'
allow write; // Write rule not evaluated.
// Complete match.
match /nested/path { // `singleSegment` visible in scope.
allow read; // Read rule is evaluated.
}
}
// Complete match.
match /example/{multiSegment=**} { // `multiSegment` == /hello/nested/path
allow read; // Read rule is evaluated.
}
}
As the example above shows, the
path
declarations supports the following
variables:
- Single-segment wildcard:
A wildcard variable is declared in a path by
wrapping a variable in curly braces:
{variable}
. This variable is accessible
within the
match
statement as a
string
.
- Recursive wildcard:
The recursive, or multi-segment, wildcard matches
multiple path segments at or below a path. This wildcard matches all paths
below the location you set it to. You can declare it by adding the
=**
string at the end of your segment variable:
{variable=**}
. This variable is
accessible within the
match
statement as a
path
object.
Allow
The
match
block contains one or more
allow
statements. These are your actual
rules. You can apply
allow
rules to one or more methods. The conditions
on an
allow
statement must evaluate to true for Cloud Firestore or Cloud Storage
to grant any incoming request. You can also write
allow
statements without
conditions, for example,
allow read
. If the
allow
statement doesn't include
a condition, however, it always allows the request for that method.
If any of the
allow
rules for the method are satisfied, the request is
allowed. Additionally, if a broader rule grants access, Rules grant
access and ignore any more granular rules that might limit access.
Consider the following example, where any user can read or delete any of their
own files. A more granular rule only allows writes if the user requesting the
write owns the file and the file is a PNG. A user can delete any files at the
subpath ? even if they're not PNGs ? because the earlier rule allows it.
service firebase.storage {
// Allow the requestor to read or delete any resource on a path under the
// user directory.
match /users/{userId}/{anyUserFile=**} {
allow read, delete: if request.auth != null && request.auth.uid == userId;
}
// Allow the requestor to create or update their own images.
// When 'request.method' == 'delete' this rule and the one matching
// any path under the user directory would both match and the `delete`
// would be permitted.
match /users/{userId}/images/{imageId} {
// Whether to permit the request depends on the logical OR of all
// matched rules. This means that even if this rule did not explicitly
// allow the 'delete' the earlier rule would have.
allow write: if request.auth != null && request.auth.uid == userId && imageId.matches('*.png');
}
}
Method
Each
allow
statement includes a method that grants access for incoming
requests of the same method.
Method
|
Type of request
|
Convenience methods
|
read
|
Any type of read request
|
write
|
Any type of write request
|
Standard methods
|
get
|
Read requests for single documents or files
|
list
|
Read requests for queries and collections
|
create
|
Write new documents or files
|
update
|
Write to existing database documents or update file metadata
|
delete
|
Delete data
|
You can't overlap read methods in the same
match
block or
conflicting write methods in the same
path
declaration.
For example, the following rules would fail:
service bad.example {
match /rules/with/overlapping/methods {
// This rule allows reads to all authenticated users
allow read: if request.auth != null;
match another/subpath {
// This secondary, more specific read rule causes an error
allow get: if request.auth != null && request.auth.uid == "me";
// Overlapping write methods in the same path cause an error as well
allow write: if request.auth != null;
allow create: if request.auth != null && request.auth.uid == "me";
}
}
}
Function
As your security rules become more complex, you may want to wrap sets of
conditions in functions that you can reuse across your ruleset. Security rules
support custom functions. The syntax for custom functions is a bit like JavaScript,
but security rules functions are written in a domain-specific language
that has some important limitations:
- Functions can contain only a single
return
statement. They cannot
contain any additional logic. For example, they cannot execute loops or
call external services.
- Functions can automatically access functions and variables from the scope
in which they are defined. For example, a function defined within
the
service cloud.firestore
scope has access to the
resource
variable
and built-in functions such as
get()
and
exists()
.
- Functions may call other functions but may not recurse. The total call
stack depth is limited to 20.
- In rules version
v2
, functions can define variables using the
let
keyword.
Functions can have up to 10 let bindings, but must end with a return
statement.
A function is defined with the
function
keyword and takes zero or more
arguments. For example, you may want to combine the two types of conditions used
in the examples above into a single function:
service cloud.firestore {
match /databases/{database}/documents {
// True if the user is signed in or the requested data is 'public'
function signedInOrPublic() {
return request.auth.uid != null || resource.data.visibility == 'public';
}
match /cities/{city} {
allow read, write: if signedInOrPublic();
}
match /users/{user} {
allow read, write: if signedInOrPublic();
}
}
}
Here is an example showing function arguments and let assignments. Let assignment
statements must be separated by semi-colons.
function isAuthorOrAdmin(userId, article) {
let isAuthor = article.author == userId;
let isAdmin = exists(/databases/$(database)/documents/admins/$(userId));
return isAuthor || isAdmin;
}
Note how the
isAdmin
assignment enforces a lookup of the admins collection. For
lazy evaluation without requiring unneeded lookups, take advantage of the
short-circuiting nature of
&&
(AND) and
||
(OR) comparisons to call a second
function only if
isAuthor
is shown to be true (for
&&
comparisons) or false
(for
||
comparisons).
function isAdmin(userId) {
return exists(/databases/$(database)/documents/admins/$(userId));
}
function isAuthorOrAdmin(userId, article) {
let isAuthor = article.author == userId;
// `||` is short-circuiting; isAdmin called only if isAuthor == false.
return isAuthor || isAdmin(userId);
}
Using functions in your security rules makes them more maintainable as the
complexity of your rules grows.
Realtime Database
As outlined above, Realtime Database Rules include three basic elements:
the database location as a mirror of the database's JSON structure, the
request type, and the condition granting access.
Database location
The structure of your rules should follow the structure of the data you have stored
in your database. For example, in a chat app with a list of
messages, you might have data that looks like this:
{
"messages": {
"message0": {
"content": "Hello",
"timestamp": 1405704370369
},
"message1": {
"content": "Goodbye",
"timestamp": 1405704395231
},
...
}
}
Your rules should mirror that structure. For example:
{
"rules": {
"messages": {
"$message": {
// only messages from the last ten minutes can be read
".read": "data.child('timestamp').val() > (now - 600000)",
// new messages must have a string content and a number timestamp
".validate": "newData.hasChildren(['content', 'timestamp']) &&
newData.child('content').isString() &&
newData.child('timestamp').isNumber()"
}
}
}
}
As the example above shows, Realtime Database Rules support a
$location
variable to match path segments. Use the
$
prefix in front of your path
segment to match your rule to any child nodes along the path.
{
"rules": {
"rooms": {
// This rule applies to any child of /rooms/, the key for each room id
// is stored inside $room_id variable for reference
"$room_id": {
"topic": {
// The room's topic can be changed if the room id has "public" in it
".write": "$room_id.contains('public')"
}
}
}
}
}
You can also use the
$variable
in parallel with constant path
names.
{
"rules": {
"widget": {
// a widget can have a title or color attribute
"title": { ".validate": true },
"color": { ".validate": true },
// but no other child paths are allowed
// in this case, $other means any key excluding "title" and "color"
"$other": { ".validate": false }
}
}
}
Method
In Realtime Database, there are three types of rules. Two of these rule types ?
read
and
write
? apply to the method of an incoming request. The
validate
rule type enforces data structures and validates the format and content of data.
Rules run
.validate
rules after verifying that a
.write
rule
grants access.
Rule Types
|
.read
|
Describes if and when data is allowed to be read by users.
|
.write
|
Describes if and when data is allowed to be written.
|
.validate
|
Defines what a correctly formatted value will look like, whether it
has child attributes, and the data type.
|
By default, if there isn't a rule allowing it, access at a path is denied.
Cloud Firestore
A condition is a boolean expression that determines whether a particular
operation should be allowed or denied. The
request
and
resource
variables
provide context for those conditions.
The
request
variable
The
request
variable includes the following fields and corresponding
information:
request.auth
A JSON Web Token (JWT) that contains authentication credentials from
Firebase Authentication.
auth
token contains a set of standard claims and any
custom claims you create through Firebase Authentication. Learn more about
Firebase Security Rules and Authentication
.
request.method
The
request.method
may be any of the standard methods or a custom method. The
convenience methods
read
and
write
also exist to simplify writing rules that
apply to all read-only or all write-only standard methods respectively.
request.params
The
request.params
include any data not specifically related to the
request.resource
that might be useful for evaluation. In practice, this map
should be empty for all standard methods, and should contain non-resource data
for custom methods. Services must be careful not to rename or modify the type
of any of the keys and values presented as params.
request.path
The
request.path
is the path for the target
resource
. The path is relative
to the service. Path segments containing non-url safe characters such as
/
are url-encoded.
The
resource
variable
The
resource
is the current value within the service represented as a map
of key-value pairs. Referencing
resource
within a condition will result in at
most one read of the value from the service. This lookup will count against any
service-related quota for the resource. For
get
requests, the
resource
will
only count toward quota on deny.
Operators and operator precedence
Use the table below as a reference for operators and their corresponding
precedence in Rules for Cloud Firestore and Cloud Storage.
Given arbitrary expressions
a
and
b
, a field
f
, and an index
i
.
Operator
|
Description
|
Associativity
|
a[i] a() a.f
|
Index, call, field access
|
left to right
|
!a -a
|
Unary negation
|
right to left
|
a/b a%b a*b
|
Multiplicative operators
|
left to right
|
a+b a-b
|
Additive operators
|
left to right
|
a>b a>=b a
<> |
Relational operators
|
left to right
|
a in b
|
Existence in list or map
|
left to right
|
a is type
|
Type comparison, where
type
can be bool, int, float,
number, string, list, map, timestamp, duration, path or latlng
|
left to right
|
a==b a!=b
|
Comparison operators
|
left to right
|
a && b
|
Conditional AND
|
left to right
|
a || b
|
Conditional OR
|
left to right
|
a ? true_value : false_value
|
Ternary expression
|
left to right
|
Cloud Storage
A condition is a boolean expression that determines whether a particular
operation should be allowed or denied. The
request
and
resource
variables
provide context for those conditions.
The
request
variable
The
request
variable includes the following fields and corresponding
information:
request.auth
A JSON Web Token (JWT) that contains authentication credentials from
Firebase Authentication.
auth
token contains a set of standard claims and any
custom claims you create through Firebase Authentication. Learn more about
Firebase Security Rules and Authentication
.
request.method
The
request.method
may be any of the standard methods or a custom method. The
convenience methods
read
and
write
also exist to simplify writing rules that
apply to all read-only or all write-only standard methods respectively.
request.params
The
request.params
include any data not specifically related to the
request.resource
that might be useful for evaluation. In practice, this map
should be empty for all standard methods, and should contain non-resource data
for custom methods. Services must be careful not to rename or modify the type
of any of the keys and values presented as params.
request.path
The
request.path
is the path for the target
resource
. The path is relative
to the service. Path segments containing non-url safe characters such as
/
are url-encoded.
The
resource
variable
The
resource
is the current value within the service represented as a map
of key-value pairs. Referencing
resource
within a condition will result in at
most one read of the value from the service. This lookup will count against any
service-related quota for the resource. For
get
requests, the
resource
will
only count toward quota on deny.
Operators and operator precedence
Use the table below as a reference for operators and their corresponding
precedence in Rules for Cloud Firestore and Cloud Storage.
Given arbitrary expressions
a
and
b
, a field
f
, and an index
i
.
Operator
|
Description
|
Associativity
|
a[i] a() a.f
|
Index, call, field access
|
left to right
|
!a -a
|
Unary negation
|
right to left
|
a/b a%b a*b
|
Multiplicative operators
|
left to right
|
a+b a-b
|
Additive operators
|
left to right
|
a>b a>=b a
<> |
Relational operators
|
left to right
|
a in b
|
Existence in list or map
|
left to right
|
a is type
|
Type comparison, where
type
can be bool, int, float,
number, string, list, map, timestamp, duration, path or latlng
|
left to right
|
a==b a!=b
|
Comparison operators
|
left to right
|
a && b
|
Conditional AND
|
left to right
|
a || b
|
Conditional OR
|
left to right
|
a ? true_value : false_value
|
Ternary expression
|
left to right
|
Realtime Database
A condition is a boolean expression that determines whether a particular
operation should be allowed or denied. You can define those conditions in
Realtime Database Rules in the following ways.
Pre-defined variables
There are a number of helpful, pre-defined variables that can be accessed inside a
rule definition. Here is a brief summary of each:
Predefined Variables
|
now
|
The current time in milliseconds since Linux epoch. This works particularly
well for validating timestamps created with the SDK's
firebase.database.ServerValue.TIMESTAMP.
|
root
|
A
RuleDataSnapshot
representing the root path in the Firebase database as it exists before the attempted operation.
|
newData
|
A
RuleDataSnapshot
representing the data as it would exist after the attempted operation. It includes the new data being written and existing data.
|
data
|
A
RuleDataSnapshot
representing the data as it existed before the attempted operation.
|
$ variables
|
A wildcard path used to represent ids and dynamic child keys.
|
auth
|
Represents an authenticated user's token payload.
|
These variables can be used anywhere in your rules. For example, the security
rules below ensure that data written to the
/foo/
node must be a string
less than 100 characters:
{
"rules": {
"foo": {
// /foo is readable by the world
".read": true,
// /foo is writable by the world
".write": true,
// data written to /foo must be a string less than 100 characters
".validate": "newData.isString() && newData.val().length < 100"
}
}
}
Data-based rules
Any data in your database can be used in your rules. Using the predefined
variables
root
,
data
, and
newData
, you can access any path as it would
exist before or after a write event.
Consider this example, which allows write operations as long as the value of the
/allow_writes/
node is
true
, the parent node does not have a
readOnly
flag set, and there is a child named
foo
in
the newly written data:
".write": "root.child('allow_writes').val() === true &&
!data.parent().child('readOnly').exists() &&
newData.child('foo').exists()"
Query-based rules
Although you can't use rules as filters, you can limit access to subsets of data
by using query parameters in your rules. Use
query.
expressions in your
rules to grant read or write access based on query parameters.
For example, the following query-based rule uses
user-based security rules
and query-based rules to restrict access to data in the
baskets
collection
to only the shopping baskets the active user owns:
"baskets": {
".read": "auth.uid !== null &&
query.orderByChild === 'owner' &&
query.equalTo === auth.uid" // restrict basket access to owner of basket
}
The following query, which includes the query parameters in the rule, would
succeed:
db.ref("baskets").orderByChild("owner")
.equalTo(auth.currentUser.uid)
.on("value", cb) // Would succeed
However, queries that do not include the parameters in the rule would fail with
a
PermissionDenied
error:
db.ref("baskets").on("value", cb) // Would fail with PermissionDenied
You can also use query-based rules to limit how much data a client downloads
through read operations.
For example, the following rule limits read access to only the first 1000
results of a query, as ordered by priority:
messages: {
".read": "query.orderByKey &&
query.limitToFirst <= 1000"
}
// Example queries:
db.ref("messages").on("value", cb) // Would fail with PermissionDenied
db.ref("messages").limitToFirst(1000)
.on("value", cb) // Would succeed (default order by key)
The following
query.
expressions are available in Realtime Database Security Rules.
Query-based rule expressions
|
Expression
|
Type
|
Description
|
query.orderByKey
query.orderByPriority
query.orderByValue
|
boolean
|
True for queries ordered by key, priority, or value. False otherwise.
|
query.orderByChild
|
string
null
|
Use a string to represent the relative path to a child node. For example,
query.orderByChild === "address/zip"
. If the query isn't
ordered by a child node, this value is null.
|
query.startAt
query.endAt
query.equalTo
|
string
number
boolean
null
|
Retrieves the bounds of the executing query, or returns null if there
is no bound set.
|
query.limitToFirst
query.limitToLast
|
number
null
|
Retrieves the limit on the executing query, or returns null if there is
no limit set.
|
Operators
Realtime Database Rules support a number of
operators
you can use to combine variables in the condition statement. See the full list
of
operators in the reference documentation
.
Your actual conditions will vary based on the access you want to grant.
Rules intentionally offer an enormous degree of flexibility, so your
app's rules can ultimately be as simple or as complex as you need them to be.