Handling sessions with Firestore

Many apps need session management for authentication and user preferences. ASP.NET core comes with middleware to store sessions in a distributed cache.

ASP.NET's default distributed cache is actually not distributed at all. It stores session data in the memory of the web server. When only one web server is serving a web site, this strategy is fine. But when many web servers are serving a web site, the web site's users can experience errors and lost data.

To avoid errors and lost data, an ASP.NET app must use a distributed cache that stores data in a persistent data store. This tutorial shows how to manage sessions on Cloud Run by storing them in Firestore and encrypting cookies with Cloud Key Management Service.

Examining the source code

The following diagram illustrates how Firestore handles sessions for the Cloud Run app.

Diagram of architecture: user, Cloud Run, Firestore.

The ConfigureServices method in the Startup.cs file sets up the app to use Cloud KMS for encryption and Cloud Storage to store encrypted keys.

  1. In Cloud Shell, click launch editor to launch the editor and examine the Startup.cs file.

    public void ConfigureServices(IServiceCollection services)
    {
        // Antiforgery tokens require data protection.
        services.AddDataProtection()
            // Store keys in Cloud Storage so that multiple instances
            // of the web application see the same keys.
            .PersistKeysToGoogleCloudStorage(
                Configuration["DataProtection:Bucket"],
                Configuration["DataProtection:Object"])
            // Protect the keys with Google KMS for encryption and fine-
            // grained access control.
            .ProtectKeysWithGoogleKms(
                Configuration["DataProtection:KmsKeyName"]);
        services.AddFirestoreDistributedCache(
                Configuration["FIRESTORE_PROJECT_ID"])
            .AddFirestoreDistributedCacheGarbageCollector();
        services.AddSession();
    }
    

Setting up the Google Cloud project

  1. In the Cloud Shell editor, edit the appsettings.json file and replace the two instances of YOUR-PROJECT-ID with your Google Cloud project ID. Save the file.

    {
      "Logging": {
        "LogLevel": {
          "Default": "Warning"
        }
      },
      "AllowedHosts": "*",
      "DataProtection": {
        "Bucket": "YOUR-PROJECT-ID-bucket",
        "Object": "DataProtectionProviderKeys.xml",
        "KmsKeyName": "projects/YOUR-PROJECT-ID/locations/global/keyRings/dataprotectionprovider/cryptoKeys/masterkey"
      }
    }
    
  2. Create a new Cloud Key Management Service key ring named dataprotectionprovider:

    gcloud kms keyrings create dataprotectionprovider --location global

  3. Create a new Cloud Key Management Service key named masterkey:

    gcloud kms keys create masterkey --location global --keyring dataprotectionprovider --purpose=encryption

  4. Create a Cloud Storage bucket to store the encrypted keys:

    gcloud storage buckets create gs://PROJECT_ID-bucket

Deploying and running on Cloud Run

You can use the Cloud Run to build and deploy an app that runs reliably under heavy load and with large amounts of data.

This tutorial uses the Cloud Run to deploy the server.

  1. In your Cloud Shell, publish your app:

    dotnet publish -c Release
    
  2. Use Cloud Build to build a Docker container and publish to Container Registry:

    gcloud builds submit --tag gcr.io/PROJECT_ID/sessions bin/Release/netcoreapp2.1/publish

  3. Run the container with Cloud Run:

    gcloud beta run deploy sessions --region us-central1 --platform managed --image gcr.io/PROJECT_ID/sessions --allow-unauthenticated

    Make a note of the URL in the output:

    Service [sessions] revision [sessions-00003-xiz] has been deployed and is serving
    100 percent of traffic at https://sessions-r3f3em7nuq-uc.a.run.app
  4. To view the live app, go to the URL that you copied from the previous step.

Deleting sessions

You can delete session data in the Google Cloud console or implement an automated deletion strategy. If you use storage solutions for sessions such as Memcache or Redis, expired sessions are automatically deleted.

Debugging the app

If you cannot connect to your Cloud Run app, check the following:

  1. Check that the gcloud deploy commands successfully completed and didn't output any errors. If there were errors (for example, message=Build failed), fix them, and try deploying the Cloud Run app again.
  2. See the Cloud Run guide to viewing logs.