Personalizing Data for Authenticated Users

Use authenticated user information to store and retrieve user-specific data and personalize each user's experience with your web service.

In a previous step, you updated the web service to display the last ten requests from all users. In this step, you use authenticated user information to update your web service so that the page displays only a list of the last ten requests made by the currently authenticated user.

Before you Begin

If you have completed all the previous steps in this guide, skip this section. Otherwise, complete one of the following:

  • Start from Building a Python 3.7 App and complete all the steps leading up to this one.

  • If you already have a GCP project, you can continue by downloading a copy of the web service and adding Firebase:

    1. Download the sample application repository using Git:

      git clone https://github.com/GoogleCloudPlatform/python-docs-samples
      

      Alternatively, you can download the sample as a zip file and then extract it.

    2. Navigate to the directory that contains a copy of the files from the previous step:

      cd python-docs-samples/appengine/standard_python37/building-an-app/building-an-app-3
      
    3. Add Firebase to your GCP project and web service.

Storing and retrieving user-specific data

You can indicate that data is connected to certain user by using Cloud Datastore ancestors, which allow you to organize your Cloud Datastore data hierarchically.

To do this, complete the following steps:

  1. Update your store_time and fetch_time methods to use Cloud Datastore ancestors for storing and retrieving visit entities:

    datastore_client = datastore.Client()
    
    def store_time(email, dt):
        entity = datastore.Entity(key=datastore_client.key('User', email, 'visit'))
        entity.update({
            'timestamp': dt
        })
    
        datastore_client.put(entity)
    
    
    def fetch_times(email, limit):
        ancestor = datastore_client.key('User', email)
        query = datastore_client.query(kind='visit', ancestor=ancestor)
        query.order = ['-timestamp']
    
        times = query.fetch(limit=limit)
    
        return times

    Each visit entity now has an ancestor that it is connected to. These ancestors are Cloud Datastore entities that represent individual authenticated users. Each ancestor's key includes the User kind and a custom ID, which is the authenticated user's email address. You use the ancestor key to query the database for only the times that are associated with a specific user.

  2. Update the store_times method call in your root method and move it inside the id_token conditional so that it only runs if the server has authenticated a user:

    @app.route('/')
    def root():
        # Verify Firebase auth.
        id_token = request.cookies.get("token")
        error_message = None
        claims = None
        times = None
    
        if id_token:
            try:
                # Verify the token against the Firebase Auth API. This example
                # verifies the token on each page load. For improved performance,
                # some applications may wish to cache results in an encrypted
                # session store (see for instance
                # http://flask.pocoo.org/docs/1.0/quickstart/#sessions).
                claims = google.oauth2.id_token.verify_firebase_token(
                    id_token, firebase_request_adapter)
    
                store_time(claims['email'], datetime.datetime.now())
                times = fetch_times(claims['email'], 10)
    
            except ValueError as exc:
                # This will be raised if the token is expired or any other
                # verification checks fail.
                error_message = str(exc)
    
        return render_template(
            'index.html',
            user_data=claims, error_message=error_message, times=times)

Configuring indexes

Cloud Datastore makes queries based on indexes. For simple entities, Cloud Datastore automatically generates these indexes. However, it cannot automatically generate indexes for more complicated entities, including those with ancestors. Because of this, you need to manually create an index for visit entities so that Cloud Datastore can perform queries involving visit entities.

To create an index for visit entities, complete the following steps:

  1. Create an index.yaml file in the root directory of your project, for example building-an-app, and add the following index:

    indexes:
    
    - kind: visit
      ancestor: yes
      properties:
      - name: timestamp
        direction: desc
    
  2. Deploy your index.yaml indexes in Cloud Datastore by running the following command and following the prompts:

    gcloud datastore indexes create index.yaml
    

It can take a while for Cloud Datastore to create indexes. Creating indexes before deploying your web service to App Engine both allows you to test locally using those indexes and prevents exceptions that might occur for queries that require an index that is still in the process of being built.

Tip: You can view the index you created in the Cloud Datastore console:

View your indexes

For more information on making Cloud Datastore indexes, see Configuring Datastore Indexes.

Testing your web service

Test your web service by running it locally, using virtualenv to handle dependencies:

  1. Run the following command in your project's main directory to run your web service. If you have not set up a virtualenv for local testing, see testing your web service.

    python main.py
    
  2. Enter the following address in your web browser to view your web service:

    http://localhost:8080
    

Deploying your web service

Now that you have Cloud Datastore working locally, you can re-deploy your web service to App Engine.

Run the following command from the root directory of your project, where your app.yaml file is located:

gcloud app deploy

All traffic is automatically routed to the new version you deployed.

For more information on managing versions, see Managing Services and Versions.

Viewing your service

To quickly launch your browser and access your web service at https://GCP_PROJECT_ID.appspot.com, run the following command:

gcloud app browse

Next Steps

Congratulations! You've successfully built a web service that uses Cloud Datastore data storage and Firebase authentication to provide authenticated users with a personalized web page.

Now you can clean up by shutting down, deactivating, or disabling billing for your project.

Was this page helpful? Let us know how we did:

Send feedback about...

App Engine standard environment for Python 3 docs