This reference guides covers Common Expression Language (CEL) syntax relevant to
creating expressions for the `@auth(expr:)`, `@check(expr:)`, and
`@refresh(onMutationExecuted:)` 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`.

```graphql
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`.

The `@auth(expr:)`, `@check(expr:)`, `@refresh(onMutationExecuted:)` 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)

And `@refresh(onMutationExecuted:)` expressions have the following binding:

- `mutation.variables`
- `mutation.auth`

#### 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>`:

```graphql
# 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/sql-connect/cel-reference.md.txt#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/sql-connect/gql/mutation.md.txt)).

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`.

```graphql
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`.

```graphql
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]
> **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.

```graphql
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.

```graphql
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.

```graphql
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`. |