<p>I'm trying to write a simple backend that will access my Google Cloud Firestore, it lives in the Google Kubernetes Engine. On my local I'm using the following code to authenticate to Firestore as detailed in the Google Documentation.</p>
<pre class="lang-js prettyprint-override"><code>if (process.env.NODE_ENV !== 'production') {
  const result = require('dotenv').config()
  //Additional error handling here
}
</code></pre>
<p>This pulls the GOOGLE_APPLICATION_CREDENTIALS environment variable and populates it with my <code>google-application-credentals.json</code> which I got from creating a service account with the <code>&quot;Cloud Datastore User&quot;</code> role.</p>
<p>So, locally, my code runs fine. I can reach my Firestore and do everything I need to. However, the problem arises once I deploy to GKE.</p>
<p>I followed this <a href="https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity#terminology" rel="noreferrer">Google Documentation</a> to set up a Workload Identity for my cluster, I've created a deployment and verified that the pods all are using the correct IAM Service Account by running:</p>
<pre><code>kubectl exec -it POD_NAME -c CONTAINER_NAME -n NAMESPACE sh
&gt; gcloud auth list
</code></pre>
<p>I was under the impression from the documentation that authentication would be handled for my service as long as the above held true. I'm really not sure why but my Firestore() instance is behaving as if it does not have the necessary credentials to access the Firestore.</p>
<p>In case it helps below is my declaration and implementation of the instance:</p>
<pre class="lang-js prettyprint-override"><code>const firestore = new Firestore()

const server = new ApolloServer({
  schema: schema,
  dataSources: () =&gt; {
    return {
      userDatasource: new UserDatasource(firestore)
    }
  }
})
</code></pre>
<p><strong>UPDATE:</strong></p>
<p>In a bout of desperation I decided to tear down everything and re-build it. Following everything over step by step I appear to have either encountered a bug or (more likely) I did something mildly wrong the first time. I'm now able to connect to my backend service. <strong>However</strong>, I'm now getting a different error. Upon sending any request (I'm using GraphQL, but in essence it's any REST call) I get back a 404.</p>
<p>Inspecting the logs yields the following:</p>
<p><code>'Getting metadata from plugin failed with error: Could not refresh access token: A Not Found error was returned while attempting to retrieve an accesstoken for the Compute Engine built-in service account. This may be because the Compute Engine instance does not have any permission scopes specified: Could not refresh access token: Unsuccessful response status code. Request failed with status code 404'</code></p>
<p>A cursory search for this issue doesn't seem to return anything related to what I'm trying to accomplish, and so I'm back to square one.</p>

<p>I think your initial assumption was correct! Workload Identity is not functioning properly if you still have to specify scopes. In the Workload article you have linked, scopes are not used.</p>
<p>I've been struggling with the same issue and have identified three ways to get authenticated credentials in the pod.</p>
<hr />
<p><strong>1. Workload Identity</strong> (basically the Workload Identity article above with some deployment details added)</p>
<p>This method is preferred because it allows each pod deployment in a cluster to be granted only the permissions it needs.</p>
<p>Create cluster (note: no scopes or service account defined)</p>
<pre><code>gcloud beta container clusters create {cluster-name} \
  --release-channel regular \
  --identity-namespace {projectID}.svc.id.goog

</code></pre>
<p>Then create the k8sServiceAccount, assign roles, and annotate.</p>
<pre><code>gcloud container clusters get-credentials {cluster-name}

kubectl create serviceaccount --namespace default {k8sServiceAccount}

gcloud iam service-accounts add-iam-policy-binding \
  --member serviceAccount:{projectID}.svc.id.goog[default/{k8sServiceAccount}] \
  --role roles/iam.workloadIdentityUser \
  {googleServiceAccount}

kubectl annotate serviceaccount \
  --namespace default \
  {k8sServiceAccount} \
  iam.gke.io/gcp-service-account={googleServiceAccount}
</code></pre>
<p>Then I create my deployment, and <strong>set the k8sServiceAccount</strong>.
(Setting the service account was the part that I was missing)</p>
<pre><code>kubectl create deployment {deployment-name} --image={containerImageURL}
kubectl set serviceaccount deployment {deployment-name} {k8sServiceAccount}
</code></pre>
<p>Then expose with a target of 8080</p>
<pre><code>kubectl expose deployment {deployment-name}  --name={service-name} --type=LoadBalancer --port 80 --target-port 8080
</code></pre>
<p>The googleServiceAccount needs to have the appropriate IAM roles assigned (see below).</p>
<hr />
<p><strong>2. Cluster Service Account</strong></p>
<p>This method is not preferred, because all VMs and pods in the cluster will have permissions based on the defined service account.</p>
<p>Create cluster with assigned service account</p>
<pre><code>gcloud beta container clusters create [cluster-name] \
 --release-channel regular \
 --service-account {googleServiceAccount}
</code></pre>
<p>The googleServiceAccount needs to have the appropriate IAM roles assigned (see below).</p>
<p>Then deploy and expose as above, but without setting the k8sServiceAccount</p>
<hr />
<p><strong>3. Scopes</strong></p>
<p>This method is not preferred, because all VMs and pods in the cluster will have permisions based on the scopes defined.</p>
<p>Create cluster with assigned scopes (firestore only requires &quot;cloud-platform&quot;, realtime database also requires &quot;userinfo.email&quot;)</p>
<pre><code>gcloud beta container clusters create $2 \
  --release-channel regular \
  --scopes https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/userinfo.email

</code></pre>
<p>Then deploy and expose as above, but without setting the k8sServiceAccount</p>
<hr />
<p>The first two methods require a Google Service Account with the appropriate IAM roles assigned. Here are the roles I assigned to get a few Firebase products working:</p>
<ul>
<li>FireStore: Cloud Datastore User (Datastore)</li>
<li>Realtime Database: Firebase Realtime Database Admin (Firebase Products)</li>
<li>Storage: Storage Object Admin (Cloud Storage)</li>
</ul>
