<br />

Firebase Data Connect provides robust client-side security with:

- Mobile and web client authorization
- Individual query- and mutation-level authorization controls
- App attestation with Firebase App Check.

Data Connect extends this security with:

- Server-side authorization
- Firebase project and Cloud SQL user security with IAM.

## Authorize client queries and mutations

Data Connect is fully integrated with Firebase Authentication, so you can
use rich data about users who are accessing your data (authentication) in
your design for what data those users can access (authorization).

Data Connect provides an `@auth` directive for queries and
mutations that lets you set the level of authentication required to authorize
the operation. This guide
[introduces the `@auth` directive, with examples](https://firebase.google.com/docs/data-connect/authorization-and-security#auth-directive).

In addition, Data Connect supports execution of queries embedded in
mutations, so you can retrieve additional authorization criteria you've stored
in your database, and use those criteria in `@check` directives to decide if
enclosing mutations are authorized. For this authorization case, the `@redact`
directive allows you control whether query results are returned to clients in
the wire protocol and the embedded query omitted in generated SDKs. Find the
[introduction to these directives, with examples](https://firebase.google.com/docs/data-connect/authorization-and-security#check-redact-directives).

### Understand the `@auth` directive

You can parameterize the `@auth` directive to follow one of several preset
access levels that cover many common access scenarios. These levels range from
`PUBLIC` (which allows queries and mutations from all clients without
authentication of any kind) to `NO_ACCESS` (which disallows queries and
mutations outside of [privileged server environments using the Firebase Admin
SDK](https://firebase.google.com/docs/data-connect/admin-sdk)). Each of these levels is correlated with authentication
flows provided by Firebase Authentication.

| Level | Definition |
|---|---|
| `PUBLIC` | The operation can be executed by anyone with or without authentication. |
| `PUBLIC` | The operation can be executed by anyone with or without authentication. |
| `USER_ANON` | Any identified user, including those who have logged in anonymously with Firebase Authentication, is authorized to perform the query or mutation. |
| `USER` | Any user who has logged in with Firebase Authentication is authorized to perform the query or mutation except anonymous sign-in users. |
| `USER_EMAIL_VERIFIED` | Any user who has logged in with Firebase Authentication with a verified email address is authorized to perform the query or mutation. |
| `NO_ACCESS` | This operation cannot be executed outside an Admin SDK context. |

Using these preset access levels as a starting point, you can define complex and
robust authorization checks in the `@auth` directive using `where` filters and
Common Expression Language (CEL) expressions evaluated on the server.
| **Note:** All queries and mutations in your connector must have an `@auth` directive to be accessible by client applications. Queries and mutations default to `NO_ACCESS` if the `@auth` directive is not specified.
| **Tip:** See the [directives reference for the `@auth` directive](https://firebase.google.com/docs/reference/data-connect/gql/directive#auth) and the [complete reference for access levels](https://firebase.google.com/docs/data-connect/authorization-and-security#preset-authz-levels).

### Use the `@auth` directive to implement common authorization scenarios

The preset access levels are the *starting point* for authorization.

The `USER` access level is the most widely useful basic level to start with.

Fully secure access will build on the `USER` level plus filters and expressions
that check user attributes, resource attributes, roles and other checks. The
`USER_ANON` and `USER_EMAIL_VERIFIED` levels are variations on the `USER` case.

Expression syntax lets you evaluate data using an `auth` object representing
authentication data passed with operations, both standard data in auth tokens
and custom data in tokens. For the list of fields available in the `auth`
object, see the [reference section](https://firebase.google.com/docs/data-connect/authorization-and-security#auth-cel-reference).
| **Note:** You can augment authorization with `@auth` by looking up authorization roles and other data stored in your database, as described in the [section on adding `@check` and `@redact` for lookups](https://firebase.google.com/docs/data-connect/authorization-and-security#check-redact-directives).

There are of course use cases where `PUBLIC` is the correct access level to
start with. Again, an access level is always a starting point, and additional
filters and expressions are needed for robust security.

This guide now gives examples of how to build on `USER` and `PUBLIC`.

#### A motivating example

The following best practice examples refer to the following schema for a
blogging platform with certain content locked behind a payment plan.

Such a platform would likely model `Users` and`Posts`.  

    type User @table(key: "uid") {
      uid: String!
      name: String
      birthday: Date
      createdAt: Timestamp! @default(expr: "request.time")
    }

    type Post @table {
      author: User!
      text: String!
      # "one of 'draft', 'public', or 'pro'"
      visibility: String! @default(value: "draft")
      # "the time at which the post should be considered published. defaults to
      # immediately"
      publishedAt: Timestamp! @default(expr: "request.time")
      createdAt: Timestamp! @default(expr: "request.time")
      updatedAt: Timestamp! @default(expr: "request.time")
    }

#### User-owned resources

Firebase recommends that you write filters and expressions that test user
ownership of a resource, in the following cases, ownership of `Posts`.

In the following examples, data from auth tokens is read and compared using
expressions. The typical pattern is to use expressions like `where: {authorUid:
{eq_expr: "auth.uid"}}` to compare a stored `authorUid` to the `auth.uid`
(user ID) passed in the authentication token.

##### Create

This authorization practice starts by adding the `auth.uid` from the
auth token to each new `Post` as an `authorUid` field to allow comparison in
subsequence authorization tests.  

    # Create a new post as the current user
    mutation CreatePost($text: String!, $visibility: String) @auth(level: USER) {
      post_insert(data: {
        # set the author's uid to the current user uid
        authorUid_expr: "auth.uid"
        text: $text
        visibility: $visibility
      })
    }

##### Update

When a client attempts to update a `Post`, you can test the passed `auth.uid`
against the stored `authorUid`.  

    # Update one of the current user's posts
    mutation UpdatePost($id: UUID!, $text: String, $visibility: String) @auth(level:USER) {
      post_update(
        # only update posts whose author is the current user
        first: { where: {
          id: {eq: $id}
          authorUid: {eq_expr: "auth.uid"}
        }}
        data: {
          text: $text
          visibility: $visibility
          # insert the current server time for updatedAt
          updatedAt_expr: "request.time"
        }
      )
    }

##### Delete

The same technique is used to authorize delete operations.  

    # Delete one of the current user's posts
    mutation DeletePost($id: UUID!) @auth(level: USER) {
      post_delete(
        # only delete posts whose author is the current user
        first: { where: {
          id: {eq: $id}
          authorUid: {eq_expr: "auth.uid"}
        }}
      )
    }

**Note:** For these query examples, the following fragment is used to select common fields on `Post`.  

    # Common display information for a post
    fragment DisplayPost on Post {
      id, text, createdAt, updatedAt
      author { uid, name }
    }

##### List

    # List all posts belonging to the current user
    query ListMyPosts @auth(level: USER) {
      posts(where: {
        userUid: {eq_expr: "auth.uid"}
      }) {
        # See the fragment above
        ...DisplayPost
        # also show visibility since it is user-controlled
        visibility
      }
    }

##### Get

    # Get a post only if it belongs to the current user
    query GetMyPost($id: UUID!) @auth(level: USER) {
      post(key: {id: $id},
        first: {where: {
          id: {eq: $id}
          authorUid: {eq_expr: "auth.uid"}}
          }}, {
          # See the fragment above
          ...DisplayPost
          # also show visibility since it is user-controlled
          visibility
      }
    }

#### Filter Data

Data Connect's authorization system lets you write sophisticated
filters combined with preset access levels like `PUBLIC` as well as by using
data from auth tokens.

The authorization system also lets you use expressions only, without a
base access level, as shown in some of the following examples.

##### Filter by resource attributes

Here, authorization is not based on auth tokens since the base security level
is set to `PUBLIC`. But, we can explicitly set records in our database as
suitable for public access; assume we have `Post` records in our database with
`visibility` set to "public".  

    # List all posts marked as 'public' visibility
    query ListPublicPosts @auth(level: PUBLIC) {
      posts(where: {
        # Test that visibility is "public"
        visibility: {eq: "public"}
        # Only display articles that are already published
        publishedAt: {lt_expr: "request.time"}
      }) {
        # see the fragment above
        ...DisplayPost
      }
    }

##### Filter by user claims

Here, assume you've set up custom user claims that pass in auth tokens to
identify users in a "pro" plan for your app, flagged with an `auth.token.plan`
field in the auth token. Your expressions can test against this field.  

    # List all public or pro posts, only permitted if user has "pro" plan claim
    query ProListPosts @auth(expr: "auth.token.plan == 'pro'") {
      posts(where: {
        # display both public posts and "pro" posts
        visibility: {in: ['public', 'pro']},
        # only display articles that are already published
        publishedAt: {lt_expr: "request.time"},
      }) {
        # see the fragment above
        ...DisplayPost
        # show visibility so pro users can see which posts are pro\
        visibility
      }
    }

##### Filter by order + limit

Or again, you may have set `visibility` in `Post` records to identify they are
content available for "pro" users, but for a preview or teaser listing of data,
further limit the number of records returned.  

    # Show 2 oldest Pro post as a preview
    query ProTeaser @auth(level: USER) {
      posts(
        where: {
          # show only pro posts
          visibility: {eq: "pro"}
          # that have already been published more than 30 days ago
          publishedAt: {lt_time: {now: true, sub: {days: 30}}}
        },
        # order by publish time
        orderBy: [{publishedAt: DESC}],
        # only return two posts
        limit: 2
      ) {
        # See the fragment above
        ...DisplayPost
      }
    }

##### Filter by role

If your custom claim defines an `admin` role, you can test and authorize
operations accordingly.  

    # List all posts unconditionally iff the current user has an admin claim
    query AdminListPosts @auth(expr: "auth.token.admin == true") {
      posts { ...DisplayPost }
    }

### Add the `@check` and `@redact` directives to look up authorization data

A common authorization use case involves storing custom authorization roles in
your database, for example in a special permissions table, and using those
roles to authorize mutations to create, update, or delete data.

Using authorization data lookups, you can query for roles based on a userID and
use CEL expressions to decide if the mutation is authorized. For example, you
might want to write an `UpdateMovieTitle` mutation that lets an authorized
client update movie titles.

For the rest of this discussion, assume the movie review app database stores an
authorization role in a `MoviePermission` table.  

    # MoviePermission
    # Suppose a user has an authorization role with respect to records in the Movie table
    type MoviePermission @table(key: ["movie", "user"]) {
      movie: Movie! # implies another field: movieId: UUID!
      user: User!
      role: String!
    }

#### Use in mutations

In the following example implementation, the `UpdateMovieTitle` mutation
includes a `query` field to retrieve data from `MoviePermission`, and the
following directives to ensure the operation is secure and robust:

- A `@transaction` directive to ensure all authorization queries and checks are completed or fail atomically.
- The `@redact` directive to omit query results from the response, meaning our authorization check is performed on the Data Connect server but sensitive data is not exposed to the client.
- A pair of `@check` directives to evaluate authorization logic on query
  results, such as testing that a given userID has an appropriate role to make
  modifications.

  | **Note:** The `message` argument for `@check` is optional, but it's useful in combination with the `@redact` directive, so you can define messages to return to client code even though specific fields are redacted from the response.

    mutation UpdateMovieTitle($movieId: UUID!, $newTitle: String!) @auth(level: USER) @transaction {
      # Step 1: Query and check
      query @redact {
        moviePermission( # Look up a join table called MoviePermission with a compound key.
          key: {movieId: $movieId, userId_expr: "auth.uid"}
        # Step 1a: Use @check to test if the user has any role associated with the movie
        # Here the `this` binding refers the lookup result, i.e. a MoviePermission object or null
        # The `this != null` expression could be omitted since rejecting on null is default behavior
        ) @check(expr: "this != null", message: "You do not have access to this movie") {
          # Step 1b: Check if the user has the editor role for the movie
          # Next we execute another @check; now `this` refers to the contents of the `role` field
          role @check(expr: "this == 'editor'", message: "You must be an editor of this movie to update title")
        }
      }
      # Step 2: Act
      movie_update(id: $movieId, data: {
        title: $newTitle
      })
    }

| **Tip:** Refer to the [directives reference guide](https://firebase.google.com/docs/reference/data-connect/gql/directive) and [CEL reference for the `this` binding](https://firebase.google.com/docs/data-connect/authorization-and-security#cel-this-binding).

#### Use in queries

Authorization data lookups are also useful to restrict queries based on roles
or other restrictions.

In the following example, which also uses the `MoviePermission` schema, the
query checks whether a requestor has an appropriate "admin" role to view users
who can edit a movie.  

    query GetMovieEditors($movieId: UUID!) @auth(level: PUBLIC) {
      moviePermission(key: { movieId: $movieId, userId_expr: "auth.uid" }) @redact {
        role @check(expr: "this == 'admin'", message: "You must be an admin to view all editors of a movie.")
      }
      moviePermissions(where: { movieId: { eq: $movieId }, role: { eq: "editor" } }) {
        user {
          id
          username
        }
      }
    }

| **Tip:** Refer to the [directives reference guide](https://firebase.google.com/docs/reference/data-connect/gql/directive) and [CEL reference for the `this` binding](https://firebase.google.com/docs/data-connect/authorization-and-security#cel-this-binding).

### Antipatterns to avoid in authorization

The previous section covers patterns to follow when using the `@auth`
directive.

You should also be aware of important antipatterns to avoid.

Avoid passing user attributes IDs and auth token parameters in query and mutation arguments

Firebase Authentication is a powerful tool for presenting authentication flows and
securely capturing authentication data such as registered user IDs and numerous
fields stored in auth tokens.

It's not a recommended practice to pass user IDs and auth token data in query
and mutation arguments.
| **Tip:** Instead, in the filters and expressions you use to augment basic access levels, test against `auth.uid` and the fields in `auth.token` to enhance security. For more information about `auth` data, see the [expression
reference](https://firebase.google.com/docs/data-connect/authorization-and-security#auth-cel-reference).  

    # Antipattern!
    # This incorrectly allows any user to view any other user's posts
    query AllMyPosts($userId: String!) @auth(level: USER) {
      posts(where: {authorUid: {eq: $userId}}) {
        id, text, createdAt
      }
    }

Avoid using the `USER` access level without any filters

As discussed several times in the guide, the core access levels like `USER`,
`USER_ANON`, `USER_EMAIL_VERIFIED` are baselines and starting points for
authorization checks, to be enhanced with filters and expressions. Using these
levels without a corresponding filter or expression that checks *which* user is
performing the request is essentially equivalent to using the `PUBLIC` level.
**Tip:** Firebase recommends that you *always* augment basic access levels with expressions.  

    # Antipattern!
    # This incorrectly allows any user to view all documents
    query ListDocuments @auth(level: USER) {
      documents {
        id
        title
        text
      }
    }

Avoid using `PUBLIC` or `USER` access level for prototyping

To speed up development, it can be tempting to set all operations to the
`PUBLIC` access level or to `USER` access level without further enhancements to
authorize all operations and let you quickly test your code.
| **Tip:** Before you deploy your operations to production, when you prototype in the console and with the tooling in the Data Connect VS Code extension, set your queries and mutations to the `NO_ACCESS` access level. You'll be able to execute these operations in the safe console and VS Code prototyping environments before they are deployed. Following this practice means that even if you deploy such operations to production, they cannot be executed.

When you've done very initial prototyping this way, begin to switch from
`NO_ACCESS` to production-ready authorization with `PUBLIC` and `USER` levels.
However, don't deploy them as `PUBLIC` or `USER` without adding additional
logic as shown in this guide.  

    # Antipattern!
    # This incorrectly allows anyone to delete any post
    mutation DeletePost($id: UUID!) @auth(level: PUBLIC) {
      post: post_delete(
        id: $id,
      )
    }

Avoid basing authorization on unverified email addresses

Granting access to users on a particular domain is a great way to limit access.
However, anyone can claim to own an email during sign-in. Ensure you only grant
access to email addresses that have been verified through Firebase
Authentication.  

    # Antipattern!
    # Anyone can claim an email address during sign-in
    mutation CreatePost($text: String!, $visibility: String) @auth(expr: "auth.token.email.endsWith('@example.com')") {
      post_insert(data: {
        # set the author's uid to the current user uid
        authorUid_expr: "auth.uid"
        text: $text
        visibility: $visibility
      })
    }

Also check `auth.token.email_verified`  

    mutation CreatePost($text: String!, $visibility: String) @auth(expr: "auth.token.email_verified && auth.token.email.endsWith('@example.com')") {
      post_insert(data: {
        # set the author's uid to the current user uid
        authorUid_expr: "auth.uid"
        text: $text
        visibility: $visibility
      })
    }

### Audit authorization with the Firebase CLI

As indicated earlier, preset access levels such as `PUBLIC` and
`USER` are the starting point for robust authorization, and
should be used with additional filter and expression-based authorization checks.
They shouldn't be used on their own without careful consideration of the use
case.

Data Connect helps you audit your authorization strategy by analyzing
your connector code when you deploy to the server using `firebase deploy` from
the Firebase CLI. You can use this audit to help you review your codebase.

When you deploy your connectors, the CLI will output assessments for existing,
modified and new operation code in your connector.

For modified and new operations, the CLI issues warnings and prompts you
for confirmation when you use certain access levels in your new operations, or
when you modify existing operations to use those access levels.
| **Note:** Insecure operation warnings are suppressed if you annotate the `@auth` directive in the operation with the [`insecureReason` argument](https://firebase.google.com/docs/data-connect/authorization-and-security#insecureReasons).

Warnings and prompts always occur for:

- `PUBLIC`

And, warnings and prompts occur on the following access levels when you
*don't augment them* with filters using `auth.uid`:

- `USER`
- `USER_ANON`
- `USER_EMAIL_VERIFIED`

#### Suppress insecure operation warnings with the `@auth(insecureReason:)` argument

In many cases, you will conclude that using the `PUBLIC` and `USER*` access
levels is perfectly appropriate.

When your connector contains many operations, you may want clearer, more
relevant security audit output that omits operations that would normally trigger
a warning, but you know have the correct access level.

You can suppress warnings for such operations with `@auth(insecureReason:)`.
For example:  

    query listItem @auth(level: PUBLIC, insecureReason: "This operation is safe to expose to the public.")
      {
        items {
          id name
        }
      }

## Use Firebase App Check for app attestation

Authentication and authorization are critical components of
Data Connect security. Authentication and authorization combined with
app attestation makes for a very robust security solution.

With attestation through Firebase App Check, devices
running your app will use an app or device attestation provider that attests
that Data Connect operations originate from your authentic app and
requests originate from an authentic, untampered device. This attestation is
attached to every request your app makes to Data Connect.

To learn how to enable App Check for Data Connect and include its
client SDK in your app, have a [look at the App Check
overview](https://firebase.google.com/docs/app-check).

## Authentication levels for the `@auth(level)` directive

The following table lists all standard access levels and their CEL equivalents.
Authentication levels are listed from broad to narrow -- each level encompasses
all users who match following levels.
| **Warning:** These access levels are only the starting point for security, upon which you implement additional filter and expression-based authorization checks like those [discussed above](https://firebase.google.com/docs/data-connect/authorization-and-security#auth-best-practices).

| Level | Definition |
|---|---|
| `PUBLIC` | The operation can be executed by anyone with or without authentication. <br /> **Considerations:** Data can be read or modified by any user. Firebase recommends this level of authorization for publicly-browsable data like product or media listings. See the [best practice examples and alternatives](https://firebase.google.com/docs/data-connect/authorization-and-security#auth-best-practices). <br /> Equivalent to `@auth(expr: "true")` <br /> `@auth` filters and expressions cannot be used in combination with this access level. Any such expressions will fail with a 400 bad request error. |
| `USER_ANON` | Any identified user, including those who have logged in anonymously with Firebase Authentication, is authorized to perform the query or mutation. <br /> Note: `USER_ANON` is a superset of `USER`. <br /> **Considerations:** Note that you must carefully design your queries and mutations for this level of authorization. This level allows user to be logged in **anonymously** (automatic sign-in tied only to a user device) with Authentication, and does not on its own perform other checks on, for example, whether data belongs to the user. See the [best practice examples and alternatives](https://firebase.google.com/docs/data-connect/authorization-and-security#auth-best-practices). <br /> Since Authentication anonymous login flows issue a `uid`, the `USER_ANON` level is equivalent to `@auth(expr: "auth.uid != nil")` |
| `USER` | Any user who has logged in with Firebase Authentication is authorized to perform the query or mutation except anonymous sign-in users. <br /> **Considerations:** Note that you must carefully design your queries and mutations for this level of authorization. This level only checks that the user is logged in with Authentication, and does not on its own perform other checks on, for example, whether data belongs to the user. See the [best practice examples and alternatives](https://firebase.google.com/docs/data-connect/authorization-and-security#auth-best-practices). <br /> Equivalent to `@auth(expr: "auth.uid != nil && auth.token.firebase.sign_in_provider != 'anonymous'")"` |
| `USER_EMAIL_VERIFIED` | Any user who has logged in with Firebase Authentication with a verified email address is authorized to perform the query or mutation. <br /> **Considerations:** Since email verification is performed using Authentication, it's based on a more robust Authentication method, thus this level provides additional security compared to `USER` or `USER_ANON`. This level only checks that the user is logged in with Authentication with a verified email, and does not on its own perform other checks on, for example, whether data belongs to the user. See the [best practice examples and alternatives](https://firebase.google.com/docs/data-connect/authorization-and-security#auth-best-practices). <br /> Equivalent to `@auth(expr: "auth.uid != nil && auth.token.email_verified")"` |
| `NO_ACCESS` | This operation cannot be executed outside an Admin SDK context. <br /> Equivalent to `@auth(expr: "false")` |

## CEL Reference for `@auth(expr)`

As shown in examples elsewhere in this guide, you can and should use
expressions defined in Common Expression Language (CEL) to control authorization
for Data Connect using `@auth(expr:)` and `@check` directives.

This section covers CEL syntax relevant to creating expressions for these
directives.

Complete reference information for CEL is provided in the
[CEL specification](https://github.com/google/cel-spec).

### Test variables passed in queries and mutations

`@auth(expr)` syntax allows you access and test variables from queries and
mutations.

For example, you can include an operation variable, such as `$status`, using
`vars.status`.  

    mutation Update($id: UUID!, $status: Any) @auth(expr: "has(vars.status)")

### Data available to expressions: request, response, this

You use data for:

- Evaluation with CEL expressions in `@auth(expr:)` and `@check(expr:)` directives
- Assignment using server expressions, `<field>_expr`.

Both `@auth(expr:)` and `@check(expr:)` CEL expressions can evaluate the
following:

- `request.operationName`
- `vars` (alias for `request.variables`)
- `auth` (alias for `request.auth`)

In mutations, you can access and assign the contents of:

- `response` (to check partial results in multi-step logic)

Additionally, `@check(expr:)` expressions can evaluate:

- `this` (the value of the current field)
- `response` (to check partial results in multi-step logic)

#### The request.operationName binding

The `request.operarationName` binding stores the type of operation, either query
or mutation.

#### The `vars` binding (request.vars)

The `vars` binding allows your expressions to access all variables
passed in your query or mutation.

You can use `vars.<variablename>` in an expression as an alias for the
fully-qualified `request.variables.<variablename>`:  

    # The following are equivalent
    mutation StringType($v: String!) @auth(expr: "vars.v == 'hello'")
    mutation StringType($v: String!) @auth(expr: "request.variables.v == 'hello'")

#### The `auth` binding (request.auth)

Authentication identifies users requesting access to your data and provides
that information as a binding you can build on in your expressions.

In your filters and expressions, you can use `auth` as an alias for
`request.auth`.

The auth binding contains the following information:

- `uid`: A unique user ID, assigned to the requesting user.
- `token`: A map of values collected by Authentication.

For more details on the contents of `auth.token` see
[Data in auth tokens](https://firebase.google.com/docs/data-connect/authorization-and-security#auth-token-contents)

#### The `response` binding

The `response` binding contains the data being assembled by the server in
response to a query or mutation *as that data is being assembled*.

As the operation proceeds, as each step is completed successfully,
`response` contains response data from successfully-completed steps.

The `response` binding is structured according the shape of its associated
operation, including (multiple) nested fields and (if applicable) embedded
queries.

Note that when you access **embedded query response data** , fields can contain
any data type, depending on the data requested in the embedded query; when you
access data returned by mutation fields like `_insert`s and `_delete`s, they may
contain UUID keys, number of deletes, nulls (see the
[mutations reference](https://firebase.google.com/docs/reference/data-connect/gql/mutation)).

For example:

- In a mutation that contains an embedded query, the `response` binding contains lookup data at `response.query.<fieldName>.<fieldName>....`, in this case, `response.query.todoList` and `response.query.todoList.priority`.

    mutation CheckTodoPriority(
      $uniqueListName: String!
    ) {
      # This query is identified as `response.query`
      query @check(expr: "response.query.todoList.priority == 'high'", message: "This list is not for high priority items!") {
        # This field is identified as `response.query.todoList`
        todoList(where: { name: $uniqueListName }) {
          # This field is identified as `response.query.todoList.priority`
          priority
        }
      }
    }

- In a multi-step mutation, for example with multiple `_insert` fields, the `response` binding contains partial data at `response.<fieldName>.<fieldName>....`, in this case, `response.todoList_insert.id`.

    mutation CreateTodoListWithFirstItem(
      $listName: String!,
      $itemContent: String!
    ) @transaction {
      # Step 1
      todoList_insert(data: {
        id_expr: "uuidV4()",
        name: $listName,
      })
      # Step 2:
      todo_insert(data: {
        listId_expr: "response.todoList_insert.id" # <-- Grab the newly generated ID from the partial response so far.
        content: $itemContent,
      })
    }

#### The `this` binding

| **Note:** The `this` binding is valid for `@check` expressions, not `@auth` expressions.

The binding `this` evaluates to the field that the `@check` directive
is attached to. In a basic case, you might evaluate single-valued query
results.  

    mutation UpdateMovieTitle (
      $movieId: UUID!,
      $newTitle: String!)
      @auth(level: USER)
      @transaction {
      # Step 1: Query and check
      query @redact {
        moviePermission( # Look up a join table called MoviePermission with a compound key.
          key: {movieId: $movieId, userId_expr: "auth.uid"}
        ) {
          # Check if the user has the editor role for the movie. `this` is the string value of `role`.
          # If the parent moviePermission is null, the @check will also fail automatically.
          role @check(expr: "this == 'editor'", message: "You must be an editor of this movie to update title")
        }
      }
      # Step 2: Act
      movie_update(id: $movieId, data: {
        title: $newTitle
      })
    }

If the returned field occurs multiple times because any ancestor is a list, each
occurrence is tested with `this` bound to each value.

For any given path, if an ancestor is `null` or `[]`, the field won't be
reached and the CEL evaluation will be skipped for that path. In other words,
evaluation only takes place when `this` is `null` or non-`null`, but never
`undefined`.

When the field itself is a list or object, `this` follows the same structure
(including all descendants selected in case of objects), as illustrated in the
following example.  

    mutation UpdateMovieTitle2($movieId: UUID!, $newTitle: String!) @auth(level: USER) @transaction {
      # Step 1: Query and check
      query {
        moviePermissions( # Now we query for a list of all matching MoviePermissions.
          where: {movieId: {eq: $movieId}, userId: {eq_expr: "auth.uid"}}
        # This time we execute the @check on the list, so `this` is the list of objects.
        # We can use the `.exists` macro to check if there is at least one matching entry.
        ) @check(expr: "this.exists(p, p.role == 'editor')", message: "You must be an editor of this movie to update title") {
          role
        }
      }
      # Step 2: Act
      movie_update(id: $movieId, data: {
        title: $newTitle
      })
    }

### Complex expression syntax

You can write more complex expressions by combining with the `&&` and `||`
operators.  

    mutation UpsertUser($username: String!) @auth(expr: "(auth != null) && (vars.username == 'joe')")

The following section describes all available operators.

### Operators and operator precedence

Use the following table as a reference for operators and their corresponding
precedence.

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<b a<=b` | Relational operators | left to right |
| `a in b` | Existence in list or map | left to right |
| `type(a) == t` | Type comparison, where `t` can be bool, int, float, number, string, list, map, timestamp, or duration | 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 |

### Data in auth tokens

The `auth.token` object may contain the following values:

| 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`. |
| `firebase.tenant` | The tenantId associated with the account, if present. For example, `tenant2-m6tyz` |

#### Additional fields in JWT ID tokens

You can also access the following `auth.token` fields:

| Custom Token Claims |||
|---|---|---|
| `alg` | Algorithm | `"RS256"` |
| `iss` | Issuer | Your project's service account email address |
| `sub` | Subject | Your project's service account email address |
| `aud` | Audience | `"https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"` |
| `iat` | Issued-at time | The current time, in seconds since the UNIX epoch |
| `exp` | Expiration time | The time, in seconds since the UNIX epoch, at which the token expires. It can be a **maximum of 3600 seconds later** than the `iat`. Note: this only controls the time when the *custom token* itself expires. But once you sign a user in using `signInWithCustomToken()`, they will remain signed in into the device until their session is invalidated or the user signs out. |
| `<claims>` (optional) |   | Optional custom claims to include in token, which can be accessed through `auth.token` (or `request.auth.token`) in expressions. For example, if you create a custom claim `adminClaim`, you can access it with `auth.token.adminClaim`. |

## What's next?

- Firebase Data Connect provides an Admin SDK to let you perform [queries and mutations from privileged environments](https://firebase.google.com/docs/data-connect/admin-sdk).
- Learn about IAM security in the [guide for managing services and databases](https://firebase.google.com/docs/data-connect/manage-services-and-databases).