Common Expression Language syntax reference for Data Connect

This reference guides covers Common Expression Language (CEL) syntax relevant to creating expressions for the @auth(expr:) and @check(expr:) directives.

Complete reference information for CEL is provided in the CEL specification.

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

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

  • request.operationName
  • vars (alias for request.variables)
  • auth (alias for request.auth)

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

  • this (the value of the current field)

The request.operationName object

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

The vars object

The vars object 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 object

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

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

The auth object 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 contenst of auth.token see Data in auth tokens

The this binding

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