Secure Data Connect with authorization and attestation Stay organized with collections Save and categorize content based on your preferences.
Firebase Data Connect provides robust client-side security with:
- Mobile and web client authorization
- Individual query- and mutation-level authorization controls
- App attestation withFirebase 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 withFirebase Authentication, so you canuse rich data about users who are accessing your data (authentication) inyour design for what data those users can access (authorization).
Data Connect provides an@auth directive for queries andmutations that lets you set the level of authentication required to authorizethe operation. This guideintroduces the@auth directive, with examples.
In addition,Data Connect supports execution of queries embedded inmutations, so you can retrieve additional authorization criteria you've storedin your database, and use those criteria in@check directives to decide ifenclosing mutations are authorized. For this authorization case, the@redactdirective allows you control whether query results are returned to clients inthe wire protocol and the embedded query omitted in generated SDKs. Find theintroduction to these directives, with examples.
Understand the@auth directive
You can parameterize the@auth directive to follow one of several presetaccess levels that cover many common access scenarios. These levels range fromPUBLIC (which allows queries and mutations from all clients withoutauthentication of any kind) toNO_ACCESS (which disallows queries andmutations outside ofprivileged server environments using the Firebase AdminSDK). Each of these levels is correlated with authenticationflows provided byFirebase 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 withFirebase Authentication, is authorized to perform the query or mutation. |
USER | Any user who has logged in withFirebase Authentication is authorized to perform the query or mutation except anonymous sign-in users. |
USER_EMAIL_VERIFIED | Any user who has logged in withFirebase 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 androbust authorization checks in the@auth directive usingwhere filters andCommon Expression Language (CEL) expressions evaluated on the server.
@authdirective to be accessible by client applications. Queries and mutationsdefault toNO_ACCESS if the@auth directive is not specified.Tip: See thedirectives reference for the@auth directiveand thecomplete reference for access levels.Use the@auth directive to implement common authorization scenarios
The preset access levels are thestarting point for authorization.
TheUSER access level is the most widely useful basic level to start with.
Fully secure access will build on theUSER level plus filters and expressionsthat check user attributes, resource attributes, roles and other checks. TheUSER_ANON andUSER_EMAIL_VERIFIED levels are variations on theUSER case.
Expression syntax lets you evaluate data using anauth object representingauthentication data passed with operations, both standard data in auth tokensand custom data in tokens. For the list of fields available in theauthobject, see thereference section.
@auth by looking up authorizationroles and other data stored in your database, as described in thesection on adding@check and@redact for lookups.There are of course use cases wherePUBLIC is the correct access level tostart with. Again, an access level is always a starting point, and additionalfilters and expressions are needed for robust security.
This guide now gives examples of how to build onUSER andPUBLIC.
A motivating example
The following best practice examples refer to the following schema for ablogging platform with certain content locked behind a payment plan.
Such a platform would likely modelUsers andPosts.
typeUser@table(key:"uid"){uid:String!name:Stringbirthday:DatecreatedAt:Timestamp!@default(expr:"request.time")}typePost@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 userownership of a resource, in the following cases, ownership ofPosts.
In the following examples, data from auth tokens is read and compared usingexpressions. The typical pattern is to use expressions likewhere: {authorUid:{eq_expr: "auth.uid"}} to compare a storedauthorUid to theauth.uid(user ID) passed in the authentication token.
Create
This authorization practice starts by adding theauth.uid from theauth token to each newPost as anauthorUid field to allow comparison insubsequence authorization tests.
# Create a new post as the current usermutationCreatePost($text:String!,$visibility:String)@auth(level:USER){post_insert(data:{# set the author's uid to the current user uidauthorUid_expr:"auth.uid"text:$textvisibility:$visibility})}Update
When a client attempts to update aPost, you can test the passedauth.uidagainst the storedauthorUid.
# Update one of the current user's postsmutationUpdatePost($id:UUID!,$text:String,$visibility:String)@auth(level:USER){post_update(# only update posts whose author is the current userfirst:{where:{id:{eq:$id}authorUid:{eq_expr:"auth.uid"}}}data:{text:$textvisibility:$visibility# insert the current server time for updatedAtupdatedAt_expr:"request.time"})}Delete
The same technique is used to authorize delete operations.
# Delete one of the current user's postsmutationDeletePost($id:UUID!)@auth(level:USER){post_delete(# only delete posts whose author is the current userfirst:{where:{id:{eq:$id}authorUid:{eq_expr:"auth.uid"}}})}Post.# Common display information for a postfragmentDisplayPostonPost{id,text,createdAt,updatedAtauthor{uid,name}}List
# List all posts belonging to the current userqueryListMyPosts@auth(level:USER){posts(where:{userUid:{eq_expr:"auth.uid"}}){# See the fragment above...DisplayPost# also show visibility since it is user-controlledvisibility}}Get
# Get a post only if it belongs to the current userqueryGetMyPost($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-controlledvisibility}}Filter Data
Data Connect's authorization system lets you write sophisticatedfilters combined with preset access levels likePUBLIC as well as by usingdata from auth tokens.
The authorization system also lets you use expressions only, without abase 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 levelis set toPUBLIC. But, we can explicitly set records in our database assuitable for public access; assume we havePost records in our database withvisibility set to "public".
# List all posts marked as 'public' visibilityqueryListPublicPosts@auth(level:PUBLIC){posts(where:{# Test that visibility is "public"visibility:{eq:"public"}# Only display articles that are already publishedpublishedAt:{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 toidentify users in a "pro" plan for your app, flagged with anauth.token.planfield in the auth token. Your expressions can test against this field.
# List all public or pro posts, only permitted if user has "pro" plan claimqueryProListPosts@auth(expr:"auth.token.plan=='pro'"){posts(where:{# display both public posts and "pro" postsvisibility:{in:['public','pro']},# only display articles that are already publishedpublishedAt:{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 setvisibility inPost records to identify they arecontent 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 previewqueryProTeaser@auth(level:USER){posts(where:{# show only pro postsvisibility:{eq:"pro"}# that have already been published more than 30 days agopublishedAt:{lt_time:{now:true,sub:{days:30}}}},# order by publish timeorderBy:[{publishedAt:DESC}],# only return two postslimit:2){# See the fragment above...DisplayPost}}Filter by role
If your custom claim defines anadmin role, you can test and authorizeoperations accordingly.
# List all posts unconditionally iff the current user has an admin claimqueryAdminListPosts@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 inyour database, for example in a special permissions table, and using thoseroles to authorize mutations to create, update, or delete data.
Using authorization data lookups, you can query for roles based on a userID anduse CEL expressions to decide if the mutation is authorized. For example, youmight want to write anUpdateMovieTitle mutation that lets an authorizedclient update movie titles.
For the rest of this discussion, assume the movie review app database stores anauthorization role in aMoviePermission table.
# MoviePermission# Suppose a user has an authorization role with respect to records in the Movie tabletypeMoviePermission@table(key:["movie","user"]){movie:Movie!# implies another field: movieId: UUID!user:User!role:String!}Use in mutations
In the following example implementation, theUpdateMovieTitle mutationincludes aquery field to retrieve data fromMoviePermission, and thefollowing directives to ensure the operation is secure and robust:
- A
@transactiondirective to ensure all authorization queries and checks arecompleted or fail atomically. - The
@redactdirective to omit query results from the response, meaning ourauthorization check is performed on theData Connect server butsensitive data is not exposed to the client. A pair of
Note: The@checkdirectives to evaluate authorization logic on queryresults, such as testing that a given userID has an appropriate role to makemodifications.messageargument for@checkis optional, but it's useful incombination with the@redactdirective, so you can define messages to returnto client code even though specific fields are redacted from the response.
mutationUpdateMovieTitle($movieId:UUID!,$newTitle:String!)@auth(level:USER)@transaction{# Step 1: Query and checkquery@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` fieldrole@check(expr:"this == 'editor'",message:"You must be an editor of this movie to update title")}}# Step 2: Actmovie_update(id:$movieId,data:{title:$newTitle})}this binding.Use in queries
Authorization data lookups are also useful to restrict queries based on rolesor other restrictions.
In the following example, which also uses theMoviePermission schema, thequery checks whether a requestor has an appropriate "admin" role to view userswho can edit a movie.
queryGetMovieEditors($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{idusername}}}this binding.Antipatterns to avoid in authorization
The previous section covers patterns to follow when using the@authdirective.
You should also be aware of important antipatterns to avoid.
Avoid passing user attributes IDs and authtoken parameters in query and mutation arguments
Firebase Authentication is a powerful tool for presenting authentication flows andsecurely capturing authentication data such as registered user IDs and numerousfields stored in auth tokens.
It's not a recommended practice to pass user IDs and auth token data in queryand mutation arguments.
Tip: Instead, in the filters and expressions you use to augment basic accesslevels, test againstauth.uid and the fields inauth.token to enhancesecurity. For more information aboutauth data, see theexpressionreference.# Antipattern!# This incorrectly allows any user to view any other user's postsqueryAllMyPosts($userId:String!)@auth(level:USER){posts(where:{authorUid:{eq:$userId}}){id,text,createdAt}}Avoid using theUSER access level without any filters
As discussed several times in the guide, the core access levels likeUSER,USER_ANON,USER_EMAIL_VERIFIED are baselines and starting points forauthorization checks, to be enhanced with filters and expressions. Using theselevels without a corresponding filter or expression that checkswhich user isperforming the request is essentially equivalent to using thePUBLIC level.
# Antipattern!# This incorrectly allows any user to view all documentsqueryListDocuments@auth(level:USER){documents{idtitletext}}Avoid usingPUBLIC orUSER access level for prototyping
To speed up development, it can be tempting to set all operations to thePUBLIC access level or toUSER access level without further enhancements toauthorize all operations and let you quickly test your code.
NO_ACCESS access level. You'll be ableto execute these operations in the safe console and VS Code prototypingenvironments before they are deployed. Following this practice means that evenif you deploy such operations to production, they cannot be executed.When you've done very initial prototyping this way, begin to switch fromNO_ACCESS to production-ready authorization withPUBLIC andUSER levels.However, don't deploy them asPUBLIC orUSER without adding additionallogic as shown in this guide.
# Antipattern!# This incorrectly allows anyone to delete any postmutationDeletePost($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 grantaccess to email addresses that have been verified through FirebaseAuthentication.
# Antipattern!# Anyone can claim an email address during sign-inmutationCreatePost($text:String!,$visibility:String)@auth(expr:"auth.token.email.endsWith('@example.com')"){post_insert(data:{# set the author's uid to the current user uidauthorUid_expr:"auth.uid"text:$textvisibility:$visibility})}Also checkauth.token.email_verified
mutationCreatePost($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 uidauthorUid_expr:"auth.uid"text:$textvisibility:$visibility})}Audit authorization with the Firebase CLI
As indicated earlier, preset access levels such asPUBLIC andUSER are the starting point for robust authorization, andshould be used with additional filter and expression-based authorization checks.They shouldn't be used on their own without careful consideration of the usecase.
Data Connect helps you audit your authorization strategy by analyzingyour connector code when you deploy to the server usingfirebase deploy fromtheFirebase 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 youfor confirmation when you use certain access levels in your new operations, orwhen 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 theinsecureReason argument.Warnings and prompts always occur for:
PUBLIC
And, warnings and prompts occur on the following access levels when youdon't augment them with filters usingauth.uid:
USERUSER_ANONUSER_EMAIL_VERIFIED
Suppress insecure operation warnings with the@auth(insecureReason:) argument
In many cases, you will conclude that using thePUBLIC andUSER* accesslevels is perfectly appropriate.
When your connector contains many operations, you may want clearer, morerelevant security audit output that omits operations that would normally triggera warning, but you know have the correct access level.
You can suppress warnings for such operations with@auth(insecureReason:).For example:
querylistItem@auth(level:PUBLIC,insecureReason:"This operation is safe to expose to the public."){items{idname}}UseFirebase App Check for app attestation
Authentication and authorization are critical components ofData Connect security. Authentication and authorization combined withapp attestation makes for a very robust security solution.
With attestation throughFirebase App Check, devicesrunning your app will use an app or device attestation provider that atteststhatData Connect operations originate from your authentic app andrequests originate from an authentic, untampered device. This attestation isattached to every request your app makes toData Connect.
To learn how to enableApp Check forData Connect and include itsclient SDK in your app, have alook at theApp Checkoverview.
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 encompassesall users who match following levels.
Warning: These access levels are only the starting point for security, uponwhich you implement additional filter and expression-based authorization checkslike thosediscussed above.| Level | Definition |
|---|---|
PUBLIC | The operation can be executed by anyone with or without authentication. 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 thebest practice examples and alternatives. Equivalent to @auth(expr: "true")@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 withFirebase Authentication, is authorized to perform the query or mutation. Note: USER_ANON is a superset ofUSER.Considerations: Note that you must carefully design your queries and mutations for this level of authorization. This level allows user to be logged inanonymously (automatic sign-in tied only to a user device) withAuthentication, and does not on its own perform other checks on, for example, whether data belongs to the user. See thebest practice examples and alternatives. SinceAuthentication anonymous login flows issue a uid, theUSER_ANON level is equivalent to@auth(expr: "auth.uid != nil") |
USER | Any user who has logged in withFirebase Authentication is authorized to perform the query or mutation except anonymous sign-in users. 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 withAuthentication, and does not on its own perform other checks on, for example, whether data belongs to the user. See thebest practice examples and alternatives. Equivalent to @auth(expr: "auth.uid != nil && auth.token.firebase.sign_in_provider != 'anonymous'")" |
USER_EMAIL_VERIFIED | Any user who has logged in withFirebase Authentication with a verified email address is authorized to perform the query or mutation. Considerations: Since email verification is performed usingAuthentication, it's based on a more robustAuthentication method, thus this level provides additional security compared to USER orUSER_ANON. This level only checks that the user is logged in withAuthentication with a verified email, and does not on its own perform other checks on, for example, whether data belongs to the user. See thebest practice examples and alternatives.Equivalent to @auth(expr: "auth.uid != nil && auth.token.email_verified")" |
NO_ACCESS | This operation cannot be executed outside an Admin SDK context. Equivalent to @auth(expr: "false") |
CEL Reference for@auth(expr)
As shown in examples elsewhere in this guide, you can and should useexpressions defined in Common Expression Language (CEL) to control authorizationforData Connect using@auth(expr:) and@check directives.
This section covers CEL syntax relevant to creating expressions for thesedirectives.
Complete reference information for CEL is provided in theCEL specification.
Test variables passed in queries and mutations
@auth(expr) syntax allows you access and test variables from queries andmutations.
For example, you can include an operation variable, such as$status, usingvars.status.
mutationUpdate($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 thefollowing:
request.operationNamevars(alias forrequest.variables)auth(alias forrequest.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
Therequest.operarationName binding stores the type of operation, either queryor mutation.
Thevars binding (request.vars)
Thevars binding allows your expressions to access all variablespassed in your query or mutation.
You can usevars.<variablename> in an expression as an alias for thefully-qualifiedrequest.variables.<variablename>:
# The following are equivalentmutationStringType($v:String!)@auth(expr:"vars.v=='hello'")mutationStringType($v:String!)@auth(expr:"request.variables.v=='hello'")Theauth binding (request.auth)
Authentication identifies users requesting access to your data and providesthat information as a binding you can build on in your expressions.
In your filters and expressions, you can useauth as an alias forrequest.auth.
The auth binding contains the following information:
uid: A unique user ID, assigned to the requesting user.token: A map of values collected byAuthentication.
For more details on the contents ofauth.token seeData in auth tokens
Theresponse binding
Theresponse binding contains the data being assembled by the server inresponse to a query or mutationas that data is being assembled.
As the operation proceeds, as each step is completed successfully,response contains response data from successfully-completed steps.
Theresponse binding is structured according the shape of its associatedoperation, including (multiple) nested fields and (if applicable) embeddedqueries.
Note that when you accessembedded query response data, fields can containany data type, depending on the data requested in the embedded query; when youaccess data returned by mutation fields like_inserts and_deletes, they maycontain UUID keys, number of deletes, nulls (see themutations reference).
For example:
- In a mutation that contains an embedded query, the
responsebinding contains lookup data atresponse.query.<fieldName>.<fieldName>...., inthis case,response.query.todoListandresponse.query.todoList.priority.
mutationCheckTodoPriority($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
_insertfields, theresponsebinding contains partial data atresponse.<fieldName>.<fieldName>....,in this case,response.todoList_insert.id.
mutationCreateTodoListWithFirstItem($listName:String!,$itemContent:String!)@transaction{# Step 1todoList_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,})}Thethis binding
Note: Thethis binding is valid for@check expressions, not@authexpressions.The bindingthis evaluates to the field that the@check directiveis attached to. In a basic case, you might evaluate single-valued queryresults.
mutationUpdateMovieTitle($movieId:UUID!,$newTitle:String!)@auth(level:USER)@transaction{# Step 1: Query and checkquery@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: Actmovie_update(id:$movieId,data:{title:$newTitle})}If the returned field occurs multiple times because any ancestor is a list, eachoccurrence is tested withthis bound to each value.
For any given path, if an ancestor isnull or[], the field won't bereached and the CEL evaluation will be skipped for that path. In other words,evaluation only takes place whenthis isnull or non-null, but neverundefined.
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 thefollowing example.
mutationUpdateMovieTitle2($movieId:UUID!,$newTitle:String!)@auth(level:USER)@transaction{# Step 1: Query and checkquery{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: Actmovie_update(id:$movieId,data:{title:$newTitle})}Complex expression syntax
You can write more complex expressions by combining with the&& and||operators.
mutationUpsertUser($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 correspondingprecedence.
Given arbitrary expressionsa andb, a fieldf, and an indexi.
| 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, wheret 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
Theauth.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 theemail 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 followingauth.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 amaximum of 3600 seconds later than theiat.Note: this only controls the time when thecustom 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 throughauth.token (orrequest.auth.token) in expressions. For example, if you create a custom claimadminClaim, you can access it withauth.token.adminClaim. | |
What's next?
- Firebase Data Connect provides an Admin SDK to let you performqueries and mutations from privileged environments.
- Learn about IAM security in theguide for managing services and databases.
Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.
Last updated 2026-02-10 UTC.