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: