Cloud Firestore Security Rules Reference

Cloud Firestore Security Rules are used to determine who has read and write access to collections and documents stored in Cloud Firestore, as well as how documents are structured and what fields and values they contain. Cloud Firestore Security Rules are composed of rules that consider the request and resource being accessed to allow or deny a desired action, such as creating new documents or querying for existing documents. These reference docs cover the types of rules, the properties of a request and a resource, functions specific to Cloud Firestore, the data types used by Cloud Firestore Security Rules, and how errors occur.

Rule

A rule is an expression that is evaluated to determine if a request is allowed to perform a desired action.

Types

Allow

allow rules consist of a method, such as read or write, as well as an optional condition. When a rule is executed, the condition is evaluated, and if the condition evaluates to true, the desired method is allowed; otherwise, the method is denied. An allow rule with no condition always allows the desired method.

// Always allow method
allow <method>;

// Allow method if condition is true
allow <method>: if <condition>;

Currently, allow is the only supported type of rule.

Request Methods

Reads

There are two operations for reads and queries in Cloud Firestore: get and list. These correspond to the get() and where().get() (query), methods in the client libraries. For your convenience, the read operation allows both of these.

// Reads are divided into get and list operations
allow get, list: if <condition>;

// This is equivalent to using the read operation
allow read: if <condition>;

Writes

There are three operations for writes in Cloud Firestore: create, update, and delete. These correspond to the set(), add(), update(), remove(), and transaction() methods in the client libraries. For your convenience the write operation allows all of these.

// Writes are divided into create, update, and delete operations
allow create, update, delete: if <condition>;

// This is equivalent to using the write operation
allow write: if <condition>;

Match

Rules are executed when a user request (such as a document set or get) matches a document path covered by a rule. A match consists of a path and a body, which must contain at least one allow rule. If no path is matched, the request is rejected.

You can match a fully named path, or you can insert wildcards to match all paths that fit a certain pattern.

Path Segments

single_segment

You can use single path segments to create a rule that matches a document stored in Cloud Firestore.

// Allow read at "path" if condition evaluates to true
match /path {
  allow read: if <condition>;
}

Multiple path segments and nested paths are also allowed:

// Allow read at "path/to/object" if condition evaluates to true
match /path {
  match /to {
    match /object {
      allow read: if <condition>;
    }
  }
}

{single_segment_wildcard}

If you want to apply a rule to multiple documents at the same path, you can use a wildcard path segment to match all documents at a certain path. 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.

// Allow read at any path "/*", if condition evaluates to true
match /{single_path} {
  // Matches "path", "to", or "object" but not "path/to/object"
  allow read: if <condition>;
}

Multiple path segments and nested paths may also have wildcards:

// Allow read at any path "/path/*/newPath/*", if condition evaluates to true
match /path/{first_wildcard} {
  match /newPath/{second_wildcard} {
    // Matches "path/to/newPath/newObject" or "path/from/newPath/oldObject"
    allow read: if <condition>;
  }
}

{multi_segment_wildcard=**}

If you want to match any number of path segments at or below a path, you can use a multi segment wildcard, which will match all requests to and below the location. This can be useful for providing a user his or her own free form storage space, or creating rules that match many different path segments (such as creating a publicly readable set of documents, or requiring authentication for all writes).

A multi segment wildcard path is declared similarly to a single segment wildcard, with the addition of the =** at the end of the variable: {variable=**}. A multi-segment wildcard variable is available within the match statement as a path object.

// Allow read at any path "/**", if condition evaluates to true
match /{multi_path=**} {
  // Matches anything at or below this, from "path", "path/to", "path/to/object", ...
  allow read: if <condition>;
}

Request

The request variable is provided within a condition to represent the request being made at that path. The request variable has a number of properties which can be used to decide whether to allow the incoming request.

Properties

auth

When an authenticated user performs a request against Cloud Firestore, the auth variable is populated with the user's uid (request.auth.uid) as well as the claims of the Firebase Authentication JWT (request.auth.token).

When an unauthenticated user performs a request, request.auth is null.

token

request.auth.token contains some or all of the following keys:

Field Description
email The email address associated with the account, if present.
email_verified true if the user has verified they have access to the email address. Some providers automatically verify email addresses they own.
phone_number The phone number associated with the account, if present.
name The user's display name, if set.
sub The user's Firebase UID. This is unique within a project.
firebase.identities Dictionary of all the identities that are associated with this user's account. The keys of the dictionary can be any of the following: email, phone, google.com, facebook.com, github.com, twitter.com. The values of the dictionary are arrays of unique identifiers for each identity provider associated with the account. For example, auth.token.firebase.identities["google.com"][0] contains the first Google user ID associated with the account.
firebase.sign_in_provider The sign-in provider used to obtain this token. Can be one of the following strings: custom, password, phone, anonymous, google.com, facebook.com, github.com, twitter.com.

If using custom authentication, request.auth.token also contains any custom claims specified by the developer.

// Allow requests from authenticated users
allow read, write: if request.auth != null;

path

The path variable contains the path that a request is being performed against. The path variable contains the full path. In this case, it begins with /projects/projectId/databases/(default)/documents/.... path supports array access.

// Allow a request if the user ID in the path is 12345
// e.g. /projects/projectId/databases/(default)/documents/users/{userId}

// Array accessor on numbered segment
allow read, write: if request.path[6] == '12345';

query

The query variable contains the static properties of a query being requested. This is useful for limiting which queries can be issued by a client.

Field Type Description
limit int A limit on the number of items being requested
offset int The query offset value, useful for paginating
orderBy map<string, string> A map containing fields and values indicating the sort order; "ASC" for ascending, "DESC" for descending
// Allow a query if the user is requesting less than or equal to 10 items
allow list: if request.query.limit <= 10;

// Allow a query starting at the 10th item
allow list: if request.query.offset == 10;

// Only allow ascending queries on timestamps
allow list: if request.query.orderBy['timestamp'] == 'ASC';

resource

The resource variable contains data and metadata about the document being written. It is closely related to the resource variable, which contains the current document at the requested path, as opposed to the document being written.

Developer provided data is surfaced in request.resource.data, which is a map containing the fields and values. Fields not provided in the request which exist in the resource are added to request.resource.data. You can test whether a field is modified by comparing request.resource.data.foo. This works if the request does not modify foo.

// Allow a write if the incoming document contains certain fields
allow write: if request.resource.data.keys().hasAll(['name', 'age'])
             && request.resource.data.size() == 2
             && request.resource.data.name is string
             && request.resource.data.age is int;

time

The time variable contains a timestamp representing the current server time a request is being evaluated at. You can use this to provide time-based access to files, such as: only allowing files to be uploaded until a certain date, or only allowing files to be read up to an hour after they were uploaded.

// Allow a read if the file was created less than one hour ago
allow read: if request.time < resource.data.timeCreated + duration.value(1, 'h');

Many functions are provided to write rules using timestamps and durations.

writeFields

The writeFields variable contains a list of the field paths that the current request is updating.

// Allow a write if the request writes only to the name field
allow write: if ((request.writeFields.size() == 1) && ('name' in request.writeFields));

Resource

The resource represents a Cloud Firestore document. It contains a map of the fields and values stored in a document in resource.data.

It also contains Cloud Firestore provided metadata, such as the document name and id. With the exception of id, these properties are prefixed and suffixed with __, such as __name__, __created_at__, and __updated_at__.

// Allow a read if the document being read contains certain fields
allow read: if resource.data.keys().hasAll(['name', 'age'])
            && resource.data.size() == 2
            && resource.data.name is string
            && resource.data.age is int
            && resource.id is string       // user@domain.com
            && resource.__name__ is path;  // projects/projectId/databases/(default)/documents/users/user@domain.com

Functions

Service Defined

Cloud Firestore provides several built in functions that allow developers to better use Cloud Firestore Security Rules to secure their applications. The exists() and get() functions allow a developer to check if a document exists, or retrieve the document at the given path.

exists

exists() takes a path and returns a bool, indicating whether a document exists at that path. The path provided must begin with /databases/$(database)/documents.

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow a user to read a message if the user is in the room
    match /rooms/{roomId} {
      match /messages/{messageId} {
        allow read: if exists(/databases/$(database)/documents/rooms/$(roomId)/users/$(request.auth.uid));
      }
      match /users/{userId} {
        // rules to allow users to join a room
      }
    }
  }
}

get

get() takes a path and returns the resource at that path. Like exists(), the path provided must begin with /databases/$(database)/documents.

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow an admin user to moderate content
    match /rooms/{roomId} {
      match /messages/{messageId} {
        allow update, delete: if get(/databases/$(database)/documents/rooms/$(roomId)/users/$(request.auth.uid)).data.isAdmin == true;
      }
      match /users/{userId} {
        // rules to allow users to join a room
      }
    }
  }
}

getAfter

getAfter() takes a path and returns the resource at that path as it would look if the current operation succeeded. This allows you to enforce that certain writes must occur together in batches or transactions. The path provided must begin with /databases/$(database)/documents.

service cloud.firestore {
  match /databases/{database}/documents {
    // Make sure related data is consistent
    match /messages/{messageId} {
      allow create: if request.resource.data.timestamp == request.time &&
                    getAfter(/databases/$(database)/documents/users/$(request.auth.uid)).data.lastMessage == messageId;
    }
    match /users/{userId} {
      allow update: if request.auth.uid == userId &&
                    getAfter(/databases/$(database)/documents/messages/$(request.resource.data.lastMessage)).data.timestamp == request.time;
    }
  }
}

With these rules, operations which create a new message fail unless they also update the user's lastMessage field with the message's timestamp.

var user = firebase.auth().currentUser;

var newMessageRef = db.collection("messages").doc();
var userRef = db.collection("users").doc(user.uid);

// These writes would both fail
newMessageRef.set({content: "Hi!", timestamp: firebase.firestore.FieldValue.serverTimestamp()});
userRef.update({"lastMessage": newMessageRef.id});

// These writes would succeed
var batch = db.batch();
batch.set(newMessageRef, {content: "Hi!", timestamp: firebase.firestore.FieldValue.serverTimestamp()});
batch.update(userRef, {"lastMessage": newMessageRef.id});
batch.commit();

Developer Defined

In addition to built-in functions, developers can write their own functions to provide additional functionality and/or re-use rules.

A function is defined using the function keyword, the name of the function, and zero or more parameters (without types) in parenthesis, and a function scope. Functions contain a single return statement, which may return a single result of any type.

// Check if the request is authenticated
function isAuthenticated() {
  return request.auth != null;
}

// Check if a particular user made a request
function isUserAuthenticated(userId) {
  return request.auth.uid == userId;
}

Limits

Developer defined functions have the following limits:

  1. No recursive function calls
  2. The call depth is limited to 10 function invocations

Service

The service is the first declaration in a Cloud Firestore Security Rules file, and specifies which service these rules will apply to.

Name

name

The name of the service rules will be apply to. The only current value is cloud.firestore.

// Specify the service name
service cloud.firestore {
  match /databases/{database}/documents {
    ...
  }
}

Data Types

null

The null data type represents a value not existing.

allow read: if request.auth != null;

bool

The bool type represents a boolean true or false value.

allow read: if true;   // always succeeds
allow write: if false; // always fails

Comparison

Boolean values can be compared using the == and != operators.

Boolean Operations

Operation Expression
AND x && y
OR x || y
NOT !x

Operations short circuit, and can return either true, false, or an Error.

allow read: if true || false;   // always succeeds, short circuits at true
allow write: if false && true; // always fails, short circuits at false

int, float, and number

The int and float types represent numbers. Ints are: 0, 1, -2, etc. , while floats are: 1.0, -2.0, 3.33, etc.

Ints are signed 64-bit values, and floats are 64-bit IEEE 754 compliant values. Values of type int will be coerced to float when used in comparisons and arithmetic operations with a float value.

Values of type int or float are also of type number. The number type is useful for validating that a field value is numeric when you don't care about precision:

allow write: if resource.data.age is number

Comparison

Ints and floats can be compared and ordered using the ==, !=, >, <, >=, and <= operators.

Arithmetic

Ints and floats can be added, subtracted, multiplied, divided, moduloed, and negated:

Operation Expression
Addition x + y
Subtraction x - y
Multiplication x * y
Division x / y
Modulo x % y
Negation -x

Mathematical functions

Cloud Firestore Security Rules also provides a number of mathematics helper functions to simplify expressions:

Function Description
math.ceil(x) Ceiling of the numeric value
math.floor(x) Floor of the numeric value
math.round(x) Round the input value to the nearest int
math.abs(x) Absolute value of the input
math.isInfinite(x) Test whether the value is ±∞, returns a bool
math.isNaN(x) Test whether the value is not a number NaN, returns a bool

string

Comparison

Strings can be lexographically compared and ordered using the ==, !=, >, <, >=, and <= operators.

Concatenation

Strings can be concatenated using the + operator.

// Concatenate a username and email domain
'username' + '@domain.com'

Index and Range

The index operator, string[], returns a string that contains the character at the provided index in the string.

// Allow reads of documents that begin with 'a'
match /{document} {
  allow read: if document[0] == 'a';
}

The range operator, string[i:j], returns a string that contains the characters between the specified indices, from i (inclusive) until j (exclusive). If i or j are not specified, they default to 0 and the size of the string, respectively, but at least i or j must be specified for the range to be valid.

// Allow reads of documents that begin with 'abcdef'
match /{document} {
  allow read: if document[0:6] == 'abcdef';
}

The index and range operators will yield an error if the indices provided exceed the string bounds.

size

Returns the number of characters in the string.

// Allow documents with names less than 10 characters
match /{document} {
  allow write: if document.size() < 10;
}

trim

Returns a version of the input string with leading and trailing spaces removed.

// Only allow documents that have no leading or trailing spaces.
// e.g. "document with spaces" allowed, "  document with spaces  " disallowed
match /{document} {
  allow write: if document.size() == document.trim().size();
}

upper

Returns an uppercase version of the input string.

// Only allows uppercase documents
// e.g. "UPPERCASE" allowed, "camelCase" and "snake_case" disallowed
match /{document} {
  allow write: if document == document.upper();
}

lower

Returns an lowercase version of the input string.

// Only allows lowercase documents
// e.g. "lowercase" allowed, "camelCase" and "SCREAMING_SNAKE_CASE" disallowed
match /{document} {
  allow write: if document == document.lower();
}

matches

Performs a regular expression match, returns true if the whole string matches the given regular expression. Uses Google RE2 syntax.

// Allow writes to documents which end in "@domain.com"
match /{document} {
  allow write: if document.matches('.*@domain[.]com')
}

split

Splits a string according to a provided regular expression and returns a list of strings. Uses Google RE2 syntax.

// Allow documents named "user.*" to be uploaded
match /{document} {
  allow write: if user.split('.*\..*')[0] == 'user'
}

path

Paths are directory-like names with optional pattern matching. The presence of a forward slash / denotes the start of a path segment.

path

Converts a string argument to a path.

// Allow reads on all documents at and below a certain location
match /{allDocuments=**} {
  allow read: if allDocuments == path('/path/to/document');
}

timestamp

Timestamps are in UTC, with possible values beginning at 0001-01-01T00.00.00Z and ending at 9999-12-31T23.59.59Z.

Comparison

Timestamps can be compared and ordered using the ==, !=, >, <, >=, and <= operators.

Arithmetic

Timestamps support addition and subtraction between timestamps and durations as follows:

Expression Result
timestamp + duration timestamp
duration + timestamp timestamp
timestamp - duration timestamp
timestamp - timestamp duration
duration + duration duration
duration - duration duration

date

A timestamp value containing the year, month, and day only.

// Allow reads on the same day that the resource was created.
allow read: if request.time.date() == resource.data.timeCreated.date()

year

The year value as an int, from 1 to 9999.

// Allow reads on all requests made before 2017
allow read: if request.time.year() < 2017

month

The month value as an int, from 1 to 12.

// Allow reads on all requests made during the month of January
allow read: if request.time.month() == 1;

day

The current day of the month as an int, from 1 to 31.

// Allow reads on all requests made during the first day of each month
allow read: if request.time.day() == 1;

time

A duration value containing the current time.

// Allow reads on all requests made before 12PM
allow read: if request.time.time() < duration.time(12, 0, 0, 0);

hours

The hours value as an int, from 0 to 23.

// Allow reads on all requests made before 12PM
allow read: if request.time.hours() < 12;

minutes

The minutes value as an int, from 0 to 59.

// Allow reads during even minutes of every hour
allow read: if request.time.minutes() % 2 == 0;

seconds

The seconds value as an int, from 0 to 59.

// Allow reads during the second half of each minute
allow read: if request.time.seconds() > 29;

nanos

The fractional seconds in nanos as an int.

// Allow reads during the first 0.1 seconds of each second
allow read: if request.time.nanos() < 100000000;

dayOfWeek

The day of the week, from 1 (Monday) to 7 (Sunday).

// Allow reads on weekdays (Monday to Friday)
allow read: if request.time.dayOfWeek() < 6;

dayOfYear

The day of the current year, from 1 to 366.

// Allow reads every fourth day
allow read: if request.time.dayOfYear() % 4 == 0;

toMillis

Returns the current number of milliseconds since the Unix epoch.

// Allow reads if the request is made before a specified time
allow read: if request.time.toMillis() < <milliseconds>;

duration

Duration values are represented as seconds plus fractional seconds in nanoseconds.

Comparison

Durations can be compared and ordered using the ==, !=, >, <, >=, and <= operators.

Arithmetic

Durations support addition and subtraction between timestamps and durations as follows:

Expression Result
timestamp + duration timestamp
duration + timestamp timestamp
timestamp - duration timestamp
timestamp - timestamp duration
duration + duration duration
duration - duration duration

seconds

The number of seconds in the current duration. Must be between -315,576,000,000 and +315,576,000,000 inclusive.

nanos

The number of fractional seconds (in nanoseconds) of the current duration. Must be beween -999,999,999 and +999,999,999 inclusive. For non-zero seconds and non-zero nanonseconds, the signs of both must be in agreement.

duration.value

Durations can be created using the duration.value(int magnitude, string units) function, which creates a time duration from the given magnitude and unit.

// All of these durations represent one hour:
duration.value(1, "h")
duration.value(60, "m")
duration.value(3600, "s")

Possible units are:

Duration unit
Weeks w
Days d
Hours h
Minutes m
Seconds s
Milliseconds ms
Nanoseconds ns

duration.time

Durations can be created using the duration.time(int hours, int minutes, int seconds, int nanoseconds) function, which creates a time duration of the given hours, minutes, seconds, and nanoseconds.

// Create a four hour, three minute, two second, one nanosecond duration
duration.time(4, 3, 2, 1)

list

A list contains an ordered array of values, which can of type: null, bool, int, float, string, path, list, map, timestamp, or duration.

Given x and y of type list and i and j of type int

Creation

To create a list, add values between brackets:

// Create a list of strings
['apples', 'grapes', 'bananas', 'cheese', 'goats']

Comparison

Lists can be compared using the == and != operators . Equality of two lists requires all values to be equal.

Index and Range

The index operator, list[], returns the item at the provided index in the list.

// Allow reads of all documents that begin with 'a'
match /{document} {
  allow read: if document[0] == 'a';
}

The range operator, list[i:j], returns all items in a list between the specified indices, from i (inclusive) until j (exclusive). If i or j are not specified, they default to 0 and the size of the list, respectively, but at least i or j must be specified for the range to be valid.

// Allow reads of all documents that begin with 'abcdef'
match /{document} {
  allow read: if document[0:6] == 'abcdef';
}

in

Returns true if the desired value is present in the list or false if not present.

// Allow read if a document has the string 'user' in it
match /{document} {
  allow read: if 'user' in document.split('\.');
}

join

Combines a list of strings into a single string, separated by the given string.

// Allow reads if the joined array is 'user:12345'
allow read: if ['user', '12345'].join(':') == 'user:12345';

size

The number of items in the list.

// Allow read if there are three items in our list
allow read: if ['foo', 'bar', 'baz'].size() == 3;

hasAny

Returns true if any given values are present in the list.

// Allow read if a document has the correct permissions
allow read: if resource.data.permissions.hasAny(['reader', 'admin']);

hasAll

Returns true if all values are present in the list.

// Allow write if a document contains all properties
allow write: if request.resource.data.keys().hasAll(['username', 'age']);

map

A map contains key/value pairs, where keys are strings and values can be any of: null, bool, int, float, string, path, list, map, timestamp, or duration.

Creation

To create a map, add key/value pairs between braces:

// Create a map of strings to strings
{
  'mercury': 'mars',
  'rain': 'cloud',
  'cats': 'dogs',
}

Comparison

Maps can be compared using the == and != operators. Equality of two maps requires that all keys are present in both maps and that all values are equal.

Index

Values in a map are accessed by using either bracket or dot notation:

// Access document fields (including nested properties)
allow read: if resource.data.property == 'property'
allow write: if resource.data['otherProperty'] == 'otherProperty'

If a key is not present, an error will be returned.

in

Returns true if the desired key is present in the map or false if not present.

// Allow reads if a property is present in an array of options
allow read: if property in ['foo', 'bar', 'baz'];

size

The number of keys in the map.

// Allow reads if there's exactly one field in a document
allow read: if resource.data.size() == 1;

keys

A list of all keys in the map.

// Allow reads if the first metadata key is 'myKey'
allow read: if resource.data.keys()[0] == 'myKey';

values

A list of all values in the map, in key order.

// Allow reads if the first metadata value is 'myValue'
allow read: if resource.data.values()[0] == 'myValue';

Errors

Error Evaluation

Cloud Firestore Security Rules continue evaluation when errors are encountered. This is useful because conditional && or || expressions may absorb an error if the conditional would otherwise short-circuit to false or true respectively. For instance:

Expression Result
error && true error
error && false false
error || true true
error || false error

Common places where errors are raised are: division by zero, accessing values in a list or map that don't exist, and passing values of the incorrect type to a function.

// Error if resource.data is zero
allow read: if 1000000 / resource.data;

// Error, key doesn't exist
allow read: if resource.data.nonExistentKey == 'value';

// Error, no unit 'y' exists
allow read: if request.time < resource.data.timeCreated + duration.value(1, 'y');

フィードバックを送信...

ご不明な点がありましたら、Google のサポートページをご覧ください。