Docs Menu

Sync Rules and Permissions

On this page

Atlas App Services enforces data access rules for all requests to a synced cluster. Sync rules are dynamic JSON expressions that specify a given user's ability to view and modify synced data.

The way Sync rules work depends on whether you are using Partition-Based Sync or Flexible Sync.

Important
Non-Sync Rules

This page describes data access rules for synced clusters. Non-synced cluster use a different rules model that sync rules override. If sync is enabled for a cluster, any non-sync rules defined for the cluster do not apply.

If your app does not use sync, check out MongoDB Collection Rules for more information on rules for non-synced clusters.

Whenever a user opens a synced realm from a client app, App Services evaluates your app's rules and determines if the user has read and write permissions for the partition. Users must have read permission to sync and read data in a realm and must have write permission to create, modify, or delete objects. Write permission implies read permission, so if a user has write permission then they also have read permission.

Sync rules apply to specific partitions and are coupled to your app's data model by the partition key. Consider the following behavior when designing your schemas to ensure that you have appropriate data access granularity and don't accidentally leak sensitive information.

  • Sync rules apply dynamically based on the user. One user may have full read & write access to a partition while another user has only read permissions or is unable to access the partition entirely. You control these permissions by defining JSON expressions.
  • Sync rules apply equally to all objects in a partition. If a user has read or write permission for a given partition then they can read or modify all synced fields of any object in the partition.
  • Write permissions require read permissions, so a user with write access to a partition also has read access regardless of the defined read permission rules.

App Services enforces dynamic, user-specific read and write permissions to secure the data in each partition. You define permissions with JSON rule expressions that control whether or not a given user has read or write access to the data in a given partition. App Services evaluates a user's permissions every time they open a synced realm.

Tip

Your rule expressions can use JSON expansions like %%partition and %%user to dynamically determine a user's permissions based on the context of their request.

A user with read permissions for a given partition can view all fields of any object in the corresponding synced realm. Read permissions do not permit a user to modify data.

A user with write permissions for a given partition can modify the value of any field of any object in the corresponding synced realm. Write permissions require read permissions, so any user that can modify data can also view that data before and after it's modified.

You can structure your read and write permission expressions as a set of permission strategies that apply to your partition strategy. The following strategies outline common approaches that you might take to define sync read and write permissions for your app.

You can define global permissions that apply to all users for all partitions. This is, in essence, a choice to not implement user-specific permissions in favor of universal read or write permissions that apply to all users.

To define a global read or write permission, specify a boolean value or a JSON expression that always evaluates to the same boolean value.

Example
Description
true
The expression true means that all users have the given access permissions for all partitions.
false
The expression false means that no users have the given access permissions for any partitions.
{ "%%true": true }
This expression always evaluates to true, so it's effectively the same as directly specifying true.

You can define permissions that apply to a specific partition or a groups of partitions by explicitly specifying their partition values.

Example
Description
{ "%%partition": "PUBLIC" }
This expression means that all users have the given access permissions for data with a partition value of "Public".
{
"%%partition": {
"$in": [
"PUBLIC (NA)",
"PUBLIC (EMEA)",
"PUBLIC (APAC)"
]
}
}
This expression means that all users have the given access permissions for data with any of the specified partition values.

You can define permissions that apply to a specific user or a group of users by explicitly specifying their ID values.

Example
Description
{ "%%user.id": "5f4863e4d49bd2191ff1e623" }
This expression means that the user with id "5f4863e4d49bd2191ff1e623" has the given access permissions for data in any partition.
{
"%%user.id": {
"$in": [
"5f4863e4d49bd2191ff1e623",
"5f48640dd49bd2191ff1e624",
"5f486417d49bd2191ff1e625"
]
}
}
This expression means that any user with one of the specified user ID values has the given access permissions for data in any partition.

You can define permissions that apply to users based on specific data defined in their custom user data document, metadata fields, or other data from an authentication provider.

Example
Description
{ "%%user.custom_data.readPartitions" : "%%partition" }
This expression means that a user has read access to a partition if the partition value is listed in the readPartitions field of their custom user data.
{ "%%user.data.writePartitions" : "%%partition" }
This expression means that a user has write access to a partition if the partition value is listed in the data.writePartitions field of their user object.

You can define complex dynamic permissions by evaluating a function that returns a boolean value. This is useful for permission schemes that require you to access external systems or other custom logic that you cannot express solely in JSON expressions.

Example
Description
{
"%%true": {
"%function": {
"name": "canReadPartition",
"arguments": ["%%partition"]
}
}
}
// canReadPartition
exports = async (partition) => {
const cluster = context.services.get("mongodb-atlas");
const permissions = cluster
.db("myApp")
.collection("permissions");
const { canRead } = await permissions.findOne({ partition });
return canRead.includes(context.user.id);
}
This expression calls the canReadPartition function and passes in the partition value as its first and only argument. The function looks up the user's permissions for the partition from a MongoDB collection and then returns a boolean that indicates if the user can read data in the requested partition.

In flexible sync, a session role determines which permissions apply for the duration of a sync session.

You define Flexible Sync session roles on a per-collection basis. Each "collection" when using Atlas Device Sync corresponds to a Realm Object type.

When a user begins a session by opening a synced realm, App Services determines which session role applies for the user for each collection in play. App Services considers session roles in the order that you defined them in the configuration. It evaluates each session role's "apply when" expression until one evaluates to true. If no session role applies, the user has no permissions for that collection.

Tip

Your session role "apply when" expressions can use JSON expansions to refer to user metadata and custom user data and even call functions to determine the given user's role. However, because App Services only evaluates session roles at the start of a session -- that is, before any query for specific documents -- you can't refer to a document or its field values to determine whether the session role applies.

The session role stays assigned for the duration of the session. If you make changes to an applied session role while a user is in the middle of a session, App Services does not evaluate the updated session role until the next time the user starts a session. Likewise, if something changes that would qualify the user for a different session role, the user's current session role does not change until the next session.

Important

App Services triggers a client reset if anything about the session role changed since the previous session.

At the start of a session, App Services expands all JSON expansions in the "apply when", read, and write expressions and stores the result. This has the following implications:

  • If the value changes during a session, App Services continues to use the value as it was at the time of session start.
  • On the next session, if the value is different from what it was at the start of this session, App Services triggers a client reset.
  • You cannot use the %function operator in read and write rules. Functions would not operate on a per-document basis.
  • You cannot store permissions information (such as "which document IDs may this user access?") in the user object.

A Role object can specify a role for a specific collection or the default role applied to all collections. You define field-level permissions with a Fields object in the Role.

A Role object has the following properties:

Key
Value Type
Required
Notes
name
string
Yes
The name of the role.
applyWhen
JSON expression
Yes
The rules system evaluates the expression at session start time and assigns the first role whose applyWhen expression evaluates to true.
write
JSON expression
No
The rules system evaluates the expression for each document to determine if the user may write to the given document. If true, the system implicitly grants read access. The expression may only refer to the pre-specified queryable fields of the document.
read
JSON expression
No
The rules system evaluates the expression for each document to determine if the user may write to the given document. If write evaluates to true, the system implicitly grants read access regardless of the result of read. The expression may only refer to the pre-specified queryable fields of the document.
fields
Fields object
No
Specifies field-level permissions in addition to document-level permission. If omitted, document-level rules apply.
additional_fields
Object
No
Specifies permissions for fields not explicitly listed in the fields object.
additional_fields.read
boolean
No
Specifies whether read access should be granted for fields not explicitly listed in the fields object.
additional_fields.write
boolean
No
Specifies whether write access should be granted for fields not explicitly listed in the fields object.

The Fields object defines permissions for specific fields. The keys of the fields object are names of the field:

Key
Value Type
Required
Notes
arbitrary field name
Permissions object
No
Specifies the field-level permissions for the given field.

The Permissions object has the following properties:

Key
Value Type
Required
Notes
write
boolean
No

If the user has write permission at the document level:

  • When write is true or undefined, the system grants read and write access.
  • When write is false, the system revokes write access.
read
boolean
No

First, if the user has write access, the user always has read access. If the user does not have write access but does have document-level read permission:

  • When read is true or undefined, the system grants read access.
  • When read is false, the system revokes read access.
fields
Fields object
No
For embedded object fields. Specifies field-level permissions.
additional_fields
Object
No
For embedded object fields. Specifies permissions for fields not explicitly listed in the fields object.
additional_fields.read
boolean
No
For embedded object fields. Specifies whether read access should be granted for fields not explicitly listed in the fields object.
additional_fields.write
boolean
No
For embedded object fields. Specifies whether write access should be granted for fields not explicitly listed in the fields object.
Example

The following JSON object describes a Role for a team admin:

{
"name": "TeamAdmin",
"applyWhen": { "%%user.custom_data.isAdmin": true },
"read": {},
"write": { "teamId": "%%user.custom_data.teamId" },
"fields": {
"address": {
"fields": {
"zipCode": {
"write": false,
"read": true
}
},
"additional_fields": {
"write": true
}
},
"name": {}
},
"additional_fields": {
"write": false,
"read": false
}
}

The following table explains the effect of each part of the Role:

Role JSON
Effect
"read": {}
May access any document for reading.
"write": { "teamId": "%%user.custom_data.teamId" }
May access a document for writing if the document's teamId field equals the user's custom data teamId. This requires teamId to be declared as a queryable field in the Sync configuration.
"fields": {
"address": {
"fields": {
"zipCode": {
"write": false,
"read": true
}
},
"additional_fields": {
"write": true
}
},
"name": {}
},
May write to any field of the address embedded object except zipCode. May also write to the name field.
"additional_fields": {
"write": false,
"read": false
}
May not read or write any other fields despite having document-level access to the document.
Expansion
Can Use in "Apply When"?
Can Use in Read & Write Rules?
Yes
Yes
Yes
Yes with an important caveat
No
No
Yes
Yes with an important caveat
No. These expansions refer to the document. App Services evaluates "apply when" expressions at session start, so there's no document to evaluate.
No. These expansions might access non-queryable fields of the document, which is not possible.
No
No
Yes
No
Yes
No. App Services expands the expansions at the start of the session, so the function would not operate on a per-document basis.
Yes
Yes
Yes
Yes. However, note that you cannot currently have an array field as a queryable field on a document.
Yes
Yes

You can create one or more default session roles that apply across all collections. If a collection does not have any custom session roles defined, session role resolution reverts to default session roles.

Tip

Because session roles apply only at the session level and not on a per-document level, most apps only need one (default) session role with per-document logic in that session role's read and write rules. With Flexible Sync, session roles can be thought of as a way to group read and write rule expressions to organize your code.

You define a read and write rule expression pair for every session role.

Rule expressions can refer to the queryable fields of your data model.

In response to a Flexible Sync subscription query, App Services evaluates the read and write rule expressions for each document that matches the query. The client only receives documents where the rule expression evaluates to "true".

Consider the following behavior when designing your schemas to ensure that you have appropriate data access granularity and don't accidentally leak sensitive information.

  • Sync rules apply dynamically based on the value of the queryable field or user metadata. One user may have full read & write access to a document while another user has only read permissions or is unable to access the document. You control these permissions by defining JSON expressions.
  • Write permissions require read permissions, so a user with write access to a collection also has read access regardless of the defined (or undefined) read permission rule.
Tip
See also:

For a guide to setting up flexible sync with common permissions models, see Flexible Sync Permissions Guide.

A user with read permissions for a given collection can view all fields of any object matching the client-side query. Read permissions do not permit a user to modify data.

A user with write permissions for a given collection can modify the value of any field of any object matching the client-side query. Write permissions require read permissions, so any user that can modify data can also view that data before and after it's modified.

You can refine document-level permissions on a per-field basis with the fields and additional_fields properties of the Role configuration object.

If the user does not have document-level permissions to a document, field-level permissions cannot grant access to that document. Field-level permissions can only refine the permissions on documents that a user can access. For example, you can use field-level permissions to forbid writes on a specific field of a document that a user may otherwise write to.

The additional_fields property defines the field-level permissions for fields that you don't explicitly define in the fields object. If you don't define additional_fields or its read/write properties, the system uses the corresponding document-level permission for any field not mentioned in fields.

In practice, you can use additional_fields to start with all fields restricted and use fields to grant access to a few specific fields. Conversely, you can omit additional_fields to start with all fields unrestricted and use fields to restrict a few specific fields.

In addition to read and write, you can specify fields and additional_fields rules for embedded object fields. In this case, read and write are "document-level" to the embedded object field, while fields and additional_fields are "field-level". In other words, read and write determine permissions on the embedded object as a whole, while fields and additional_fields refine those permissions on a per-field basis.

←  PartitionsFlexible Sync Permissions Guide →
Give Feedback
© 2022 MongoDB, Inc.

About

  • Careers
  • Investor Relations
  • Legal Notices
  • Privacy Notices
  • Security Information
  • Trust Center
© 2022 MongoDB, Inc.