Authenticating users to Firestore with Identity Platform and Google identities


This document shows you how to set up user-based access control to a Firestore database using Identity Platform as your user identity and access management platform. Identity Platform lets you add an authentication layer to your application so that you can protect and manage customer credentials. Firestore is a flexible NoSQL, document-oriented database. It uses a rules language called Firestore Security Rules to control access to this data, so you don't need to write server-side authorization code.

This document is intended for developers and security professionals who want to use Firestore with Firestore Security Rules, and who want to authenticate their users with Identity Platform with an external sign-in provider such as Google. The code in this document demonstrates two ways to use Identity Platform and Firestore:

  • REST API calls, using JavaScript to call Identity Platform and Firestore APIs. This approach lets you have full control over how your web app creates requests for Identity Platform.
  • Identity Platform Client SDK, using the Identity Platform Client SDK and the Firestore SDK to manage the sign-in process to Identity Platform and querying Firestore. The SDK provides JavaScript wrapper functions over Identity Platform REST APIs, which lets you call Identity Platform by using JavaScript functions and objects, instead of manually creating HTTP requests.

The Identity Platform Client SDK and the Firebase Client SDK share the same SDK. The SDK supports all of the capabilities of Identity Platform. To preserve backward compatibility, the SDK retains the Firebase branding.

Architecture

The following diagram shows the logical architecture that's described in this document:

Logical architecture diagram.

In addition to Identity Platform and Firestore, the document uses and demonstrates the following components:

  • Web app: An app that lets users sign in to Identity Platform with Google identities. It then queries Firestore for information about your signed-in user.
  • Google Sign-In: The identity provider used in this example.
  • Authentication handler: A service endpoint that gets the response from Google, performs the sign-in with Identity Platform, and sends the result back to the web app to complete the sign-in process.

Objectives

  • Set up Identity Platform for your Google Cloud project.
  • Add Google as a sign-in provider for Identity Platform.
  • Use Firestore Security Rules to control access to a Firestore database.
  • Sign in users to a web app with the Identity Platform APIs and the Identity Platform Client SDK.
  • Securely access Firestore from a client-side web app using the Firestore REST API and the Firestore JavaScript client SDK.

Costs

In this document, you use the following billable components of Google Cloud:

To generate a cost estimate based on your projected usage, use the pricing calculator. New Google Cloud users might be eligible for a free trial.

Before you begin

  1. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

    If you select an existing project, you must select a project that meets the following conditions:

    • Datastore is not enabled.
    • App Engine is not enabled.
    • The project doesn't include a Firestore database with existing security rules. You overwrite any existing rules with the rules that are described in this document.
  2. Make sure that billing is enabled for your Google Cloud project.

  3. Enable Identity Platform:
    1. Go to the Identity Platform Marketplace page in the Google Cloud console.

      Go to the Identity Platform Marketplace page

    2. Click Enable Identity Platform, and wait for the operation to complete.
  4. Enable Firestore:
    1. In the Google Cloud console, open the menu on the left-hand side and select Firestore.
    2. Select a Firestore mode. Click Select Native Mode.
    3. Choose where to store your data. Select the region closest to your location. Click Create Database.

When you finish the tasks that are described in this document, you can avoid continued billing by deleting the resources that you created. For more information, see Clean up.

Configuring Identity Platform

To enable user authentication in Identity Platform, you must add identity providers.

Before you configure a Google provider in Identity Platform, you must configure the Google OAuth consent screen. Users see this consent screen the first time they sign in to your web app. In the OAuth consent screen configuration, you can set attributes such as the name of your app, your app logo, and a support email address.

  1. In the Google Cloud console, go to the Identity Providers page.

    Go to the Identity Providers page

  2. Click Add a provider.
  3. Select Google, and in the New identity provider page, click the APIs and Services link. The Credentials page opens in a new tab. Don't close the previous tab, because you need to copy from the Credentials page to the New identity provider page.
  4. On the Credentials page, click the credentials Web client (auto created by Google Service).
  5. Copy the Client ID value, go to the tab showing the New identity provider page, and paste the value in the Web Client ID field. Repeat this step to copy the Client secret value to the Web Client Secret field.
  6. On the New Identity Provider page, click Save.
  7. Return to the Credentials page and click OAuth consent screen.
  8. Select External, and then click Create.
  9. On the OAuth consent screen page, add the following information:
    • Application name: Identity Platform Tutorial
    • Support email: Select your email address from the drop-down list.
  10. Click Save.
  11. On the Edit app registration page, add the following information:
    • App name: Identity Platform Tutorial
    • User support email: Select your email address from the drop-down list.
    • Email addresses: Type your email address.
  12. Click Save and continue.
  13. On the Scopes and Optional info pages, click Save and continue.
  14. On the Summary page, click Back to dashboard.

Configuring Firestore

The new Firestore database you created is currently empty. The new Firestore database also has a default set of security rules that allow anyone to perform read operations on the database. These default rules prevent anyone from writing to the database. In the next steps, you populate the database with data and update the security rules to limit read (query) requests to authorized users.

Create test data in Firestore

In this procedure, you test the security rules by querying Firestore collections from a web app.

First, create several documents to help you test the Firestore security rules. Collection and field names are case-sensitive. Use lowercase names for the collection and fields to prevent the web app from failing when it sends a query to Firestore.

  1. Go to the Firestore page in the Google Cloud console.
  2. Click Start collection.
  3. In the Collection ID field, type customers.
  4. Create a document with the following information:

    Document ID: bob@example.com

    Field name Field type Field value
    name String Bob
    company String
    ExampleOrganization
  5. Click Save and add another.

  6. Click Clear field values.

  7. Enter the following information:

  8. Document ID: your email address

    Field name Field type Field value
    name String Your name
    company String Your company name
  9. Click Save.

Create Firestore security rules

Firestore security rules can be evaluated using user authentication metadata, data from incoming queries, and existing data in your database.

In this procedure, you create a security rule for Firestore that is based on the signed-in user's email address and provider name.

Use the Firebase console to manage the Firestore security rules.

  1. Open the Firebase console, and click your project.
  2. Click Firestore on the left-hand side of the screen.
  3. On the Firestore page, click the Rules tab.
  4. If you choose to use an existing project that has a Firestore database with security rules, and you are planning on cleaning up the project when you finish the procedures in this document, make a note of your existing security rules.
  5. Replace the existing rules with the following rule:

    rules_version = '2';
    service cloud.firestore {
     match /databases/{database}/documents {
      match /customers/{customerID} {
      allow read:
       if request.auth.uid != null
         && request.auth.token.firebase.sign_in_provider == "google.com"
         && request.auth.token.email == customerID
        }
      }
    }
    

    This rule grants read-only permission for the customers collection. The rule verifies that you have signed in through the Google sign-in provider that you configured in Identity Platform. It also makes sure that you can only retrieve documents with a customer ID that matches your email address.

  6. Click Publish.

Configuring the test environment

To test your Firestore security rules, you start by creating a web app that requires users to sign in. The web app is available on GitHub, and downloads into a Cloud Shell environment, where you can test the application. After the user signs in, the web app reads documents from Firestore and displays their content.

Configure the web app

  1. Go to the Identity Platform providers page.

    Go to the Identity Platform providers page

  2. On the right-hand side of the page, click Application setup details.
  3. Copy the values listed next to apiKey and authDomain to your clipboard and then click Close.
  4. Click Google Cloud at the top of the page, and copy the Project ID from the Project Info card.
  5. Click Open in Cloud Shell to open Cloud Shell, clone the GitHub repository, and open the config.js file. When the Open in Cloud Shell dialog appears, click Confirm.

    Open in Cloud Shell

  6. In the config.js file, replace the placeholders [API_KEY], [AUTH_DOMAIN], and [PROJECT_ID] with the values you copied in the previous step. Your code injects these values into the URL and body of the messages it creates when it sends requests to Identity Platform.

Register the custom authentication handler

When users access your web app, they are redirected to sign in using Google as an identity provider. After the user successfully signs in to Google, Google returns a redirect (302) response with the user's token to the authentication handler, as shown in the architecture diagram. In OAuth 2.0, you must register every redirect URL in the configuration for the provider in advance, to prevent the provider from sending your token to an unknown destination. The Redirect URL Registration page on the OAuth 2.0 website explains the reasons for this restriction.

In this step, you update the authorized redirect URLs list to trust the authentication handlers that are used in this document.

The URL list is part of Google OAuth 2.0 client configuration—the page where you copied the client ID and client secret from when you configured Identity Platform.

In this architecture, you use two different authentication handlers:

  • An authentication handler hosted by Identity Platform.
  • A custom authentication handler hosted by the web app.

The authentication handler hosted by Identity Platform is accessible through the following endpoint, which is managed by Google: https://[YOUR_PROJECT_ID].firebaseapp.com/__/auth/handler

To use this handler, you don't need to update the authorized URL list in the client settings for Google OAuth 2.0. When you enabled Identity Platform earlier in this document, it added the URL automatically to the list of authorized URLs.

If you use the Identity Platform Client SDK, the SDK uses this built-in authentication handler. The authentication handler for Identity Platform requires that your web app use this SDK because the SDK exchanges state objects with the handler. For example, the SDK informs the handler where to redirect users after they successfully sign in to Identity Platform.

For the custom authentication handler hosted by the web app, when you use the Identity Platform REST APIs directly from JavaScript, we recommend implementing and hosting your own authentication handler rather than the SDK.

This document describes a sample authentication handler that manages the sign-in process to Identity Platform when it receives the user token from Google. You must add the URL for the custom handler to the authorized URLs list in your Google OAuth 2.0 client settings.

In this document, you run the web app from Cloud Shell. After you start the web app, find the hostname of your Cloud Shell instance and update the provider's configuration accordingly.

  1. Run the web app from Cloud Shell:

    npm install
    node app.js
    
  2. Wait for the following output to show: Example app listening on port 8080!

  3. Click the Web Preview icon, and then click Preview on port 8080. Wait for the new tab to show the web page, and copy the value under Auth handler URL (for Google OAuth 2.0 Client).

  4. Go to the Credentials page.
    Go to the Credentials page

  5. On the Credentials page, click Web client (auto created by Google Service).

  6. On the Authorized redirect URIs page, click Add URI, and paste the URL you previously copied.

  7. Click Save, and leave the web app running.

Authorize the web app domain

When you use the Identity Platform authentication handler, the handler redirects you back to the web app, along with your user information and tokens. To prevent sending your information to an unauthorized domain, you need to authorize the domain where your web app is running.

  1. Go back to the default web page for the web app, and copy the value under Hostname (for Identity Platform Authorized Domains).
  2. Go to the Identity Platform Settings page.

    Go to the Identity Platform Settings page

  3. Click the Security tab, and then click Add domain.
  4. Paste the domain you copied, click Add, and then click Save.
  5. Keep the web app running in Cloud Shell. You need it for the next task.

Signing in to Identity Platform with your Google identity

The following diagram expands the high-level architecture diagram at the beginning of this guide. This diagram goes into the details of the authentication process that's described in this document to show the chronological flow of events. The events start with the user clicking a sign-in button, and end with the web app retrieving data from Firestore, using the user's identity:

High level architecture;

  1. A user of your web app clicks Sign in with Google in the web app.
  2. The web app queries Identity Platform for the sign-in URL of the chosen identity provider (Google in this case).
  3. The web app redirects the user to the sign-in page for your identity provider, along with a callback URL that points to the authentication handler.
  4. On the provider sign-in page, the user enters their credentials and consents to the authorization requested by the web app.
  5. After the user successfully signs in, the provider generates a token and sends a redirect to the previously provided callback URL.
  6. The authentication handler receives the Google-issued token and sends it to Identity Platform to sign the user in. Identity Platform validates the token, signs the user in, and returns an Identity Platform issued ID token and a refresh token with the user's information. When Identity Platform signs a user in for the first time, it creates a matching user profile in its database. The account information made available by Google is used to populate the user's profile.
  7. After you sign in to Identity Platform, the handler redirects you back to the web app, along with the new tokens it got from Identity Platform.
  8. To send requests to Firestore, the web app attaches the user's ID token to every Firestore request. The Firestore security rules stipulate that Firestore treats any request without an ID token as an anonymous request, and it is denied.
  9. ID tokens issued by Identity Platform expire after one hour. If the ID token expires, the web app uses the cached refresh token to retrieve a new ID token from Identity Platform.

The sample web app demonstrates how to interact with Identity Platform and Firestore in two ways.

The first method is with the Identity Platform REST API:

  • This technique uses custom code that calls the Identity Platform REST APIs using JavaScript.
  • The API calls are implemented in the site/identity-platform-auth-helper.js file. The authentication handler is implemented in the views/auth-handler.ejs file.
  • The helper and the handler exchange state objects to enable redirecting you back to the web app after you successfully sign in.

The second method is with the Identity Platform Client SDK:

  • With this technique, the SDK handles the sign-in process.
  • The SDK implements all the required API calls and exposes a set of functions to the developer to control which sign-in flow to initiate.

Sign in using Identity Platform REST APIs

There are two main API calls that control the sign-in flow with Google as the identity provider.

The two main API calls that control sign-in flow with Google as the provider

  • Get provider URL and identifier. The accounts.createAuthUri method returns an authorization URL for the given identity provider. The web app then navigates to the returned authorization URL to start the sign-in process with the selected identity provider (for example, Google).

    The following code snippet shows how to call this API:

    IdentityPlatformAuthHelper.prototype.createAuthUri = function(providerId, tenantId) {
      // https://cloud.google.com/identity-platform/docs/reference/rest/v1/accounts/createAuthUri
      const createAuthUriUrl = `${this.identityPlatformBaseUrl}/accounts:createAuthUri?key=${config.apiKey}`;
      const request = {
        'providerId' : providerId,
        'tenantId' : tenantId,
        'continueUri' : this.authHandlerUrl,
      };
    
      return fetch(
          createAuthUriUrl,
          {
            contentType: 'application/json',
            method: 'POST',
            body: JSON.stringify(request)
          }
        )
      .then(response => response.json())
      .then(data => {
        return {
          "authUri" : data.authUri,
          "sessionId" : data.sessionId
        };
      })
      .catch(error => {
        console.error(error);
      });
    };
  • Sign in to Identity Platform using a Google-issued token. The accounts.signInWithIdp method signs the user in to Identity Platform, using the authorization response from the identity provider. The API responds to this request with a new token, issued by Identity Platform. The web app calls this API after it receives a successful authorization response from the identity provider. The following code snippet shows how to call this API:

    IdentityPlatformAuthHelper.prototype.signInWithIdp = function(data) {
      authState = this.getAuthState();
      this.authHandlerUrl = authState.authHandlerUrl;
    
      // https://cloud.google.com/identity-platform/docs/reference/rest/v1/accounts/signInWithIdp
      const signInWithIdpUrl = `${this.identityPlatformBaseUrl}/accounts:signInWithIdp?key=${config.apiKey}`;
    
      const request = {
          'requestUri' : this.authHandlerUrl,
          'sessionId' : authState.sessionId,
          'returnRefreshToken' : true,
          'returnSecureToken' : true,
          'tenantId' : authState.tenantId
        };
    
      if (authState.providerId == 'google.com' || authState.providerId.startsWith('saml.')) {
        request.postBody = `${data}&providerId=${authState.providerId}`;
      } else {
        throw new Error('This sample script only supports the google.com and SAML providers for Identity Platform');
      }
    
      fetch(
          signInWithIdpUrl,
          {
            contentType: 'application/json',
            method: 'POST',
            body: JSON.stringify(request)
          }
        )
      .then(response => response.json())
      .then(data => {
        this.user = data;
        this.signedInHandler(this.user);
      })
      .catch(error => {
        console.error(error);
      });
    }

    The postBody field value has different formats, depending on the selected identity provider and the authorization protocol it uses. The code handles the Google identity provider with its OpenID Connect (OIDC) token, and SAML-based identity providers with their provided SAML responses. If you are using other types of authorization tokens, such as OAuth 2.0 or OAuth 1.0 access tokens, refer to the API documentation of your provider.

After a user signs in to your web app, the web app can send queries to Firestore.

Flow of events when the web app sends a query to Firestore

Before the code can trigger a request to the Firestore REST APIs, it adds the ID token that was issued and signed by Identity Platform to the request. The following code snippet shows how to create the request:

function showCustomerInformation(userEmail) {
  $('#customer-information').show();
  $('#output').empty();

  const idTokenPromise = authHelper.getIdToken();
  const firestoreEndpoint = 'https://firestore.googleapis.com/v1';
  const defaultDbPath = `projects/${config.projectId}/databases/(default)/documents`;
  const collectionId = 'customers';

  // Call Firestore via its REST API and authenticate with the user's ID token
  idTokenPromise
  .then(idToken => {
    console.log(`JWT Token: ${idToken}`);
    return fetch(
      `${firestoreEndpoint}/${defaultDbPath}/${collectionId}/${userEmail}`,
      {
        headers: {
          'Authorization': 'Bearer ' + idToken
        },
        contentType: 'application/json',
        method: 'GET'
      })
  })
  .then(response => response.json())
  .then(data => {
      if (data.error) {
        throw data.error.message;
      }
      var fields = data.fields;
      $('#output').append($('<p>').text(`Id: ${userEmail}`));
      $('#output').append($('<p>').text(`Name: ${fields.name.stringValue}`));
      $('#output').append($('<p>').text(`Company: ${fields.company.stringValue}`));
      $('#output').append($('<p>').text(`Doc path: ${data.name}`));
      $('#output').append($('<p>').text(`Doc URL: ${firestoreEndpoint}/${data.name}`));
  })
  .catch(error => {
    console.error(error);
    $('#output').text("Error: " + JSON.stringify(error));
  });
}

The IdentityPlatformAuthHelper.getIdToken() function returns a valid ID token in the form of a JSON Web Token (JWT), by retrieving a browser-cached token. If the token has already expired, the function renews it by calling the Identity Platform API to exchange a refresh token for a new ID token.

The following snippet shows how to check whether the existing ID token is still valid or expired, and how to refresh it as needed by calling Identity Platform:

IdentityPlatformAuthHelper.prototype.getIdToken = function() {
  const token = this.jwtDecode(this.user.idToken);

  // If exp has passed, refresh the token
  if (Date.now() > token.payload.exp * 1000) {
    return this.refreshToken(this.user.refreshToken);
  }
  return Promise.resolve(this.user.idToken);
}

IdentityPlatformAuthHelper.prototype.jwtDecode = function(t) {
  const token = {};
  token.raw = t;
  token.header = JSON.parse(window.atob(t.split('.')[0]));
  token.payload = JSON.parse(window.atob(t.split('.')[1]));
  return token;
}

IdentityPlatformAuthHelper.prototype.refreshToken = function(refreshToken) {
  // https://cloud.google.com/identity-platform/docs/reference/rest/client#section-refresh-token
  const tokenUrl = `https://securetoken.googleapis.com/v1/token?key=${config.apiKey}`;
  const requestBody = new URLSearchParams(`grant_type=refresh_token&refresh_token=${refreshToken}`);

  return fetch(
      tokenUrl,
      {
        contentType: 'application/x-www-form-urlencoded',
        method: 'POST',
        body: requestBody
      }
    )
  .then(response => response.json())
  .then(data => {
    this.user.idToken = data.id_token;
    this.user.refreshToken = data.refresh_token;
    return this.user.idToken;
  })
  .catch(error => {
    console.error(error);
  });
}

Perform the following steps to sign in to Identity Platform with your Google identity:

  1. Return to the tab showing the default page for the web app. If you already closed that tab, go back to the Cloud Shell page, click Web preview, and then Preview on port 8080. Wait for the new tab to show the web page.
  2. Change the address in the browser to show the customer-info-with-api.html page. The new URL has the following form: https://random_prefix-devshell.appspot.com/customer-info-with-api.html
  3. Click Sign in with Google and sign in with your credentials. After you sign in, a text box appears with your email address.

    If you want to decode the JWT to see the user information provided by Identity Platform and Google, do the following. Alternatively, read ahead to the next step.

    The user's information is in the payload (second) part of the JWT, and is base64-encoded.To decode the second part of the JWT and print the information in a JSON file using jq, run the following command in Cloud Shell:

    token=[PASTE_JWT_STRING_HERE]
    echo $token | awk '{split($0, a, "."); print a[2]; }' | base64 -d | jq
    

    Continue with querying Firestore for other documents.

  4. Click Get customer info. Your name and company name appear as you entered them in the Firestore database.

  5. Change the email address to bob@example.com, and then click Sign in with Google. The response this time is the following error message:

    Error: "Missing or insufficient permissions." The security rule you added to Firestore limits your access to documents with an ID that matches your email address, as it appears in the token that Identity Platform created.

  6. Don't close the web page. You will use it in the next procedure.

Sign in using the Identity Platform Client SDK

Instead of manually crafting the requests to Identity Platform, you can use the Identity Platform Client SDK. The SDK manages the sign-in process, and provides functions to control the sign-in flow, such as which provider to use, or whether to use a redirect or a popup.

To use the Identity Platform client SDK, you need to include several script files in your HTML page. The following snippet shows which scripts you need:

<script src="https://www.gstatic.com/firebasejs/7.14.4/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/7.14.4/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/7.14.4/firebase-firestore.js"></script>

The following snippet shows how to use the SDK to sign in with the Google Provider.

$('#sign-in').click((event) => {
  provider = new firebase.auth.GoogleAuthProvider();
  //firebase.auth().signInWithPopup(provider)
  firebase.auth().signInWithRedirect(provider)
  .then((result) => {
    console.log(result);
  })
  .catch((error) => {
    console.error(error);
  });
});

firebase.auth().onAuthStateChanged(function(user) {
  if (user) {
    $('#logged-out').hide();
    /* If the provider gives a display name, use the name for the
    personal welcome message. Otherwise, use the user's email. */
    const welcomeName = user.displayName ? user.displayName : user.email;
    console.log(firebase.auth().currentUser);
    $('#user').text(welcomeName);
    $('#logged-in').show();
    $('#email').val(firebase.auth().currentUser.email);
  } else {
    $('#logged-in').hide();
    $('#logged-out').show();
    $('#email').val('');
  }
  $('#customer-information').hide();
});

$('#sign-out').click(function(event) {
  firebase.auth().signOut().then(function() {
    console.log('Sign out successful');
  }, function(error) {
    console.error(error);
  });
});

The firebase.auth().signInWithRedirect() function starts the sign-in process in the same browser window, by redirecting the user to the provider's sign-in page. Using the GoogleAuthProvider instructs the function to initiate the Google sign-in flow.

You can replace the redirect behavior with a popup behavior, by calling the signInWithPopup function instead.

To use other authentication providers, add any type that implements the firebase.auth.AuthProvider interface. To make sure you include all the required parameters, follow your selected provider's documentation.

The firebase.auth().onAuthStateChanged function is an observer that triggers on sign-in and sign-out. On sign-in, the web app code populates the web page with information taken from the User object and hides the sign-in button. On sign-out, the code clears the web page and shows the sign-in button again.

The Identity Platform Client SDK integrates with the Firestore SDK. On every query, the Firestore SDK attaches a valid ID token, by fetching it from the Identity Platform Client SDK. The Identity Platform Client SDK is responsible for refreshing the ID token when it expires.

The following code snippet shows how to query Firestore using the Firestore SDK:

function showCustomerInformation(userEmail) {
  $('#customer-information').show();
  $('#output').empty();

  const db = firebase.firestore();
  const collectionId = 'customers';

  query = db.collection(collectionId).doc(userEmail).get();
  query.then((doc) => {
    var fields = doc.data();
    $('#output').append($('<p>').text(`Id: ${doc.id}`));
    $('#output').append($('<p>').text(`Name: ${fields.name}`));
    $('#output').append($('<p>').text(`Company: ${fields.company}`));
  }).catch((error) => {
    console.error(error);
    $('#output').text("Error: " + error.toString());
  });
}

Notice that you don't have to write code to add the ID token to the query. The Firestore SDK and the Identity Platform Client SDK handle the authentication process.

Perform the following steps to sign in to Identity Platform with your Google identity, and then query Firestore:

  1. If you already closed the relevant web app tab, go back to the Cloud Shell page, click Web Preview and then Preview on port 8080. Wait for the new tab to show the web page.
  2. Change the address in the browser to show the customer-info-with-sdk.html page. The new URL has the following form: https://random_prefix-devshell.appspot.com/customer-info-with-sdk.html
  3. Click Sign in with Google and sign in with your credentials. After signing in, a text box appears with your email address.
  4. Click Get customer info. Your name and company name appear, as you entered them in the Firestore database.
  5. Change the email address to bob@example.com, and then click Sign in with Google. The response this time is an error message:

    Error: FirebaseError: [code=permission-denied]: Missing or insufficient Permissions.

    For more information on using JavaScript to query Firestore, see the Firestore documentation.

Troubleshooting issues in the web app

Learn about troubleshooting steps that you might find helpful if you run into the following issues when you run the web app.

Errors browsing the web app

Error Notes
You see either of the following error messages:

Error:Could not connect to Cloud Shell on port 8080

Error:No active Cloud Shell
Make sure Cloud Shell is open and that the web app is running, as explained in Register the custom authentication handler. If you open a new Cloud Shell session, change the working directory to the directory of the web app, before running the web app:

cd "$HOME"/cloudshell_open/securing-cloud-firestore-with-identity-platform

After you run the web app, check that the following output displays:

Example app listening on port 8080!

Errors signing in to Google

Error Notes
Nothing happens when you click Sign in with Google. You see the following error code:

Cannot GET /undefined
Make sure you have set the apiKey and authDomain variables in the config.js file, as explained in Configure the web application
On the Google sign-in page, you see the following error message:

Authorization Error - Error 400: redirect_uri_mismatch
The redirect URL sent to Google doesn't match the list of URLs authorized for the OAuth client. Make sure you have configured the authorized redirect URIs, as explained in Register the custom authentication handler.
When you sign in to Google with the Identity Platform SDK, you see the following error message:

This domain (***) is not authorized to run this operation. Add it to the OAuth redirect domains list in the Firebase console -> Auth section -> Sign-in method tab
This error might appear if the domain that Cloud Shell uses does not exist in the permitted domains list for Identity Platform. To verify that the domain name has been added to the domains list, follow the steps in Authorize the web app domain.

Errors fetching customer information

Error Notes
You see either of the following error messages:

Error: FirebaseError: [code=permission-denied]: Missing or insufficient permissions

Error: Missing or insufficient permissions
It's possible the Firestore security rules have failed to validate the token sent to Firestore. Make sure the Firestore security rules are configured properly, as explained in Create Firestore security rules.
You see any of the following error messages:

Error: FirebaseError: [code=unavailable]: Failed to get document because the client is offline

Error: "The project *** does not exist or it does not contain an active Datastore or Firestore database

Error: "Project id [PROJECT_ID] is malformed: it either contains invalid characters or is too long
Make sure that you set the projectId property in the config.js file, as explained in Configure the web application.

Clean up

To avoid incurring charges to your Google Cloud account for the resources used in this tutorial, either delete the project that contains the resources, or keep the project and delete the individual resources.

  1. In the Google Cloud console, go to the Manage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then click Delete.
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

Delete individual resources

  1. In Cloud Shell, stop the web app by pressing Ctrl+C.
  2. Delete the cloned repository directory:

    rm -rf "$HOME"/cloudshell_open/securing-cloud-firestore-with-identity-platform
    
  3. Go to the Identity Platform Settings page.

    Go to the Identity Platform Settings page

  4. Click the Security tab, delete the domain you added before, and then click Save.

  5. Go to the Identity Providers page.

    Go to the Identity Providers page

  6. Delete the Google provider you added before.

  7. Go to APIs & Services, and then the Credentials page.

    Go to the Credentials page

  8. On the Credentials page, click the Web client (auto created by Google Service).

  9. On the Authorized redirect URIs page, delete the URL you previously pasted, and then click Save.

  10. Open the Firebase console, and in the console navigation pane, click your project.

  11. Select the Database option from the Develop menu on the left.

  12. On the Database page, click the Rules tab.

  13. Overwrite the current security rules with the ones you had before you started the document, and then click Publish.

  14. Go to the Firestore page in the Google Cloud console.

  15. Click the menu to the left of the customers collection, and then click Delete collection.

  16. In the delete confirmation popup, type customers in the Collection ID field, and then click Delete.

What's next

Explore reference architectures, diagrams, and best practices about Google Cloud. Take a look at our Cloud Architecture Center.