Securing your data

Ensure your users can only access and modify authorised data using Security Rules.

Setting your Security Rules

Navigate to the Security tab in the Database section of the Sphere Kit Dashboard.

Following that, click the New revision button to write a new draft of the security rules. An editor will appear in the centre panel and tools to test your rules will appear on the right panel. You can use the test panel to simulate authentication state or the request/target to test if your rules work correctly.

If you would like to log errors that occurred while your rules were being evaluated (runtime errors) during actual requests to the database, click the Rule logs button. Logs will appear as errors are thrown and are kept for up to 14 days.

Writing Security Rules

Rules can be represented as JSON. Start with a blank object.

{}

Match paths

Next, specify the match paths for each rule group. You can specify as many match paths as you want (subject to the operation limit).

Matching rules will be ORed together (i.e. if one rule matches and the other does not match, the operation will be allowed).

When parts of a path are put in curly braces, it matches any value for that path segment (i.e. any document in mycollection). Additionally, document becomes a variable with the name of the document as the value.

{
    "mycollection/{document}": {
        ...
    }
}

Specify an asterisk (*) in after the path variable name to match all subpaths. The variable path becomes the whole subpath.

{
    "mycollection/{path*}": {
        ...
    }
}

Rule types

Within a rule group, you can specify different rules for each rule type. Rule types are one of: add, update, get, delete. Rule types can be combined to share the same rules using commas.

{
    "mycollection/{document}": {
        "get": {
            ...
        },
        "add,update,delete": {
            ...
        }
    }
}

Variable expressions

Variable expressions access a certain value, either stored in a variable or from the database. They start with the prefix $$. To access fields within an object, use <object>.<field>

For example, $$request.user.level or $$currentMillis.

Here are the variables that are available by default when rules are being evaluated:

  • request

    • user (available if authenticated)

      • uid (string)

      • metadata (additional metadata set by Achievements APIs, object)

      • isBanned (boolean, optional)

      • achievements (list of achievement IDs obtained)

    • body (available if provided)

  • target

    • path (string)

    • name (string)

    • data (for update and delete: available if document exists, for add: never available, for get: always available)

  • currentMillis (number)

  • null

If something is not available, it will be undefined and can be checked using the $exists operator.

Reference variables

You can also access other documents as a variable using the syntax $$ref(mycollection/mydocument).<field>. You can even embed variables in the reference path like this:

$$ref(mycollection/{$$request.user.uid}).

The value of the reference variable is similar to that of target.

  • path (string)

  • name (string)

  • data (available if document exists)

Document data is cached for the rest of the rule processing operation, or throughout a batch write.

Rule operations

Rule operations is the logic that defines whether an operation should be allowed. They can be top-level (evaluating to a value without being operated on a variable), or on-value (evaluating to a boolean upon operation on a variable).

Note that in all operations, the key of an operations object should be either a variable reference, an operator or a literal string.

Value expressions

These expressions evaluate to values that can be used with other expressions to create conditions.

$add

Adds a list of numbers together. Requires at least two specified values.

Syntax:

{
    "$add": [o1, o2, ...]
}

Example:

{
    "$add": ["$$request.user.score", 100]
}
$sub

Subtracts subsequent values from the first value. Requires at least two specified values.

Syntax:

{
    "$sub": [o1, o2, ...]
}

Example:

{
    "$sub": ["$$target.data.age", 10]
}
$mul

Multiplies a list of numbers against 1. Requires at least two specified values.

Syntax:

{
    "$mul": [o1, o2, ...]
}

Example:

{
    "$mul": ["$$request.user.score", 5]
}
$div

Divides subsequent values from the first value. Requires at least two specified values.

Syntax:

{
    "$div": [o1, o2, ...]
}

Example:

{
    "$div": ["$$target.data.payout", 3]
}
$mod

Applies a modulo operation (gets remainder) on subsequent values from the first value. Requires at least two specified values.

Syntax:

{
    "$mod": [o1, o2, ...]
}

Example:

{
    "$mod": ["$$target.data.payout", 3]
}
$and

Evaluates to true if all values are truthy.

Syntax:

{
    "$and": [v1, v2 ...]
}

Example:

{
    "$and": [
        "$$target.data.boosted",
        {
            "$$request.body.score": {
                "$gt": 12
            }
        }
    ]
}
$or

Evaluates to true if one value is truthy.

Syntax:

{
    "$or": [v1, v2 ...]
}

Example:

{
    "$and": [
        "$$target.data.boosted",
        {
            "sharpshooter": {
                "$in": "$$request.user.achievements"
            }
        }
    ]
}
$nor

Evaluates to true if all values are falsy.

Syntax:

{
    "$nor": [v1, v2 ...]
}

Example:

{
    "$nor": [
        "$$target.data.receivedPrize",
        "$$request.user.isBanned"
    ]
}
$typeof

Returns the data type of a value. Possible data types are one of: string, number, boolean, object, array, null, undefined.

Syntax:

{
    "$typeof": <expression or value>
}

Example:

{
    "$typeof": "$$request.user.uid" // string
}
$alttypeof

Returns the alternate data type of a value. Some objects can be interpreted as more specialised types, like GeoJSON expressions (discussed later). The possible alternate type is only geojson. The undefined value is returned when there is no alternate data type.

Syntax:

{
    "$alttypeof": <expression or value>
}

Example:

{
    "$alttypeof": "$$request.body.location"
}

On-value operations

These expressions evaluate to booleans, with the key being a variable expression and the value being a dictionary with the operations. If you would like multiple operations on the same variable expression, either put multiple dictionaries in an $and operator, or put the operations within the dictionary value of the key. See Example 2 in the Examples section.

$eq

Returns a boolean on whether the two values are equal, or when provided an array, whether the parent array contains the child element.

Syntax:

{
    <value or variable expression>: {
        "$eq": <value or expression>
    }
}

Example:

{
    "$$request.body.number": {
        "$eq": "$$ref(constants/game).data.magicNumber"
    }
}
{
    "$$request.body.numbers": {
        "$eq": 3 // Checks if 3 is an element of $$request.body.numbers
    }
}
$ne

Returns a boolean on whether the two values are not equal.

Syntax:

{
    <value or variable expression>: {
        "$ne": <value or expression>
    }
}

Example:

{
    "$$request.body.number": {
        "$ne": "$$ref(constants/game).data.badNumber"
    }
}
$gt/gte/lt/lte
Operator
Definition

gt

Greater than

gte

Greater than or equal to

lt

Lesser than

lte

Lesser than or equal to

Syntax:

{
    <number value or variable expression>: {
        "$gt/gte/lt/lte": <number value or expression>
    }
}

Example:

{
    "$$request.body.age": {
        "$gt": 50
    }
} 
$in

Returns a boolean on whether the parent value is one of the elements in the child array, or if the parent value is an array, whether the parent array contains at least one of the elements in the child array.

Syntax:

{
    <value or variable expression>: {
        "$in": <array value or expression>
    }
}

Example:

{
    "$$request.body.number": {
        "$in": "$$ref(constants/game).data.magicNumbers"
    }
}
$nin

Opposite of $in. Returns a boolean.

Syntax:

{
    <value or variable expression>: {
        "$nin": <array value or expression>
    }
}

Example:

{
    "$$request.body.number": {
        "$nin": "$$ref(constants/game).data.badNumbers"
    }
} 
$not

Returns the opposite value of the operation.

Syntax:

{
    <value or variable expression>: {
        "$not": <value or expression>
    }
}

Example:

{
    "$$target.data.age": {
        "$not": {
            "$gt": 20
        }  
    }
}
$exists

Returns a boolean on whether the field exists condition is true or false.

Syntax:

{
    <value or variable expression>: {
        "$exists": <boolean value or expression>
    }
}

Example:

{
    "$$request.body.age": {
        "$exists": true
    }
}
$type

Returns a boolean on whether the type of the parent value matches the specified types (literal only). Possible data types are one of: string, number, boolean, object, array, null, undefined.

Syntax:

{
    <value or variable expression>: {
        "$type": <string/array value>
    }
}

Example:

{
    "$$request.body.age": {
        "$type": ["number", "array"]
    }
}
{
    "$$request.body.age": {
        "$type": "number"
    }
}
$alttype

Returns a boolean on whether the type of the parent value matches the specified alternate types (literal only). The possible alternate type is only geojson.

Syntax:

{
    <value or variable expression>: {
        "$alttype": <string/array value>
    }
}

Example:

{
    "$$request.body.location": {
        "$alttype": ["geojson"]
    }
}
{
    "$$request.body.location": {
        "$alttype": "geojson"
    }
}
$geoIntersects

Returns a boolean on whether one GeoJSON object intersects another.

Syntax:

{
    <geojson value or variable expression>: {
        "$geoIntersects": {
            "$geometry": <geojson value or expression>
        }
    }
}

Example:

{
    "$$request.body.territory": {
        "$geoIntersects": {
            "$geometry": "$$ref(constants/map).data.bossTerritory"
        }
    }
}
$geoWithin

Returns a boolean on whether the parent GeoJSON object is within the child GeoJSON object.

{
    <geojson value or variable expression>: {
        "$geoWithin": {
            "$geometry": <geojson value or expression>
        }
    }
}

Example:

{
    "$$target.data.location": {
        "$geoWithin": {
            "$geometry": "$$ref(constants/map).data.bossTerritory"
        }
    }
}
$geoContains

Returns a boolean on whether the child GeoJSON object is within the parent GeoJSON object.

Syntax:

{
    <geojson value or variable expression>: {
        "$geoContains": {
            "$geometry": <geojson value or expression>
        }
    }
}

Example:

{
    "$$ref(constants/map).data.bossTerritory": {
        "$geoContains": {
            "$geometry": "$$target.data.location"
        }
    }
}
$nearSphere

Returns a boolean on whether a GeoJSON Point is within a minimum distance (optional) and maximum distance from the specified GeoJSON Point. Accounts for the curvature of the Earth.

Syntax:

{
    <geojson point value or variable expression>: {
        "$nearSphere": {
            "$geometry": <geojson point value or expression>,
            "$minDistance": <number value or expression, metres, optional>
            "$maxDistance": <number value or expression, metres>
        }
    }
}

Example:

{
    "$$target.data.location": {
        "$nearSphere": {
            "$geometry": "$$ref(constants/map).data.bossTerritory",
            "$maxDistance": 100
        }
    }
}
$all

Returns a boolean on whether the array value contains all the specified elements. When provided with an array of a nested array as the child value, the $all operator will match if the inner array is an element of the parent array, or if the parent array is deep equal to the inner array.

Syntax:

{
    <array value or variable expression>: {
        "$all": <array value or expression>
    }
}

Example:

{
    "$$request.user.achievements": {
        "$all": ["sharpshooter", "veteran"]
    }
}
{
    "$$request.user.achievements": {
        "$all": [["sharpshooter", "veteran"]] // Matches if $$request.user.achievements deep equals ["sharpshooter", "veteran"] or if $$request.user.achievements contains ["sharpshooter", "veteran"]
    }
}
$elemMatch

Returns a boolean on whether at least one element in the parent array satisfies the rule operation provided.

Syntax:

{
    <array value or variable expression>: {
        "$elemMatch": <rule expression>
    }
}

Example:

{
    "$$request.body.numbers": {
        "$elemMatch": {
            "$gt": 50
        }
    }
}
$size

Returns a boolean on whether the length of the array is equal to the specified length.

Syntax:

{
    <array value or variable expression>: {
        "$size": <number value or expression>
    }
}

Example:

{
    "$$request.body.numbers": {
        "$size": 3
    }
}
$regex

Returns a boolean on whether the regular expression provided matches the value.

Syntax:

{
    <string value or variable expression>: {
        "$regex": <literal string regex>
    }
}

Example:

{
    "$$request.body.name": {
        "$regex": "^[a-zA-Z]+$"
    }
}

Implicit operations

When both the key and the value of a rule object is not an on-value operator, an implicit equal operation is performed between the resolved key and value. This is equivalent to performing the $eq operation.

Example:

{
    "$$request.body.number": {
        "$add": [2, "$$ref(constants/game).data.magicNumber"]
    }
}

// is equivalent to

{
    "$$request.body.number": {
        "$eq": {
            "$add": [2, "$$ref(constants/game).data.magicNumber"]
        }
    }
}

Additional notes

  • There is a maximum of 1000 operations for security rules.

  • GeometryCollection GeoJSON objects are not supported for security rules.

Examples

  1. A set of rules that restricts players to only get location documents where the treasure box is near them.

{
    "locations/{document}": {
        "get": {
            "$$target.data.location": {
                "$nearSphere": {
                    "$geometry": {
                        "type": "Point",
                        "coordinates": [103.7749, 1.3338]
                    },
                    "$maxDistance": 100
                }
            }
        }
    }
}
  1. A set of rules that allows players to update their own weapons, but only allowed weapons, and only if skillLevel is greater than 10 but lesser than 20.

// Method 1 (preferred)
{
    "players/{document}": {
        "get": true,
        "update": {
            "$$request.body.weapons": {
                "$all": ["sword", "shield"]
            },
            "$$request.body.skillLevel": {
                "$gt": 10,
                "$lt": 20
            }
        }
    }
}


// Method 2 (more flexible)
{
    "players/{document}": {
        "get": true,
        "update": {
            "$$request.body.weapons": {
                "$all": ["sword", "shield"]
            },
            "$and": [
                {
                    "$$request.body.skillLevel": {
                        "$gt": 10
                    }
                },
                {
                    "$$request.body.skillLevel": {
                        "$lt": 20
                    }
                }
            ]
        }
    }
}
  1. A set of rules that ensures a power levels list has an element with at least 20 when the players are updating their document.

{
    "players/{document}": {
        "get": true,
        "update": {
            "$$request.body.powerLevels": {
                "$elemMatch": {
                    "$gte": 20
                }
            }
        }
    }
}

Last updated