firebase-tools:
Platform:
[REQUIRED] Test case
[REQUIRED] Steps to reproduce
[REQUIRED] Expected behavior
[REQUIRED] Actual behavior
Product: firebase-tools
Version affected: v15.x (confirmed v15.17.0; likely all v15 releases)
Environment: GitHub Actions / any CI using google-github-actions/auth with Workload Identity Federation
Problem statement
When firebase deploy is run in a GitHub Actions environment where authentication was established via google-github-actions/auth with Workload Identity Federation, it throws:
Error: Failed to authenticate, have you run firebase login?
This occurs even though:
GOOGLE_APPLICATION_CREDENTIALS is set to a valid credentials file
gcloud auth print-access-token succeeds and returns a valid token
The service account has full roles/firebase.admin and roles/cloudfunctions.developer IAM bindings
Every other GCP API called in the same CI job (Firestore REST API, Storage REST API, gcloud CLI) authenticates successfully with the same credentials
Root cause analysis
The auth resolution chain in requireAuth.js (paraphrased) is:
- --token CLI flag
- FIREBASE_TOKEN environment variable
- configstore (local login via
firebase login)
- autoAuth() via google-auth-library
Step 4 is where it fails silently. autoAuth() calls google-auth-library's GoogleAuth, which itself reads ADC correctly — but firebase-tools wraps this in a hardcoded timeout. If autoAuth() does not resolve within that window (observed in WIF environments where the credential exchange adds latency), firebase-tools catches the timeout and surfaces the generic "have you run firebase login?" error, discarding the actual ADC failure reason.
Critically, GOOGLE_OAUTH_ACCESS_TOKEN is never consulted anywhere in this chain — not as an env-var shortcut, not as a fallback. This is inconsistent with gcloud, which accepts a raw bearer token via that variable directly.
The result is that the only reliable CI workaround in firebase-tools v15 is the deprecated --token flag with a long-lived OAuth 2.0 refresh token (FIREBASE_TOKEN), which contradicts GCP's current security guidance to use short-lived WIF credentials.
Reproduction steps
Set up a GitHub Actions job using google-github-actions/auth@v3 with workload_identity_provider and service_account (standard WIF setup, create_credentials_file: true, export_environment_variables: true).
In the same job, confirm ADC works:
gcloud auth print-access-token # succeeds, returns valid token
curl -H "Authorization: Bearer $(gcloud auth print-access-token)"
"https://firebase.googleapis.com/v1beta1/projects/my-project" # 200 OK
Attempt a deploy:
pnpm exec firebase deploy --only functions:myFunction --project my-project
Observe:
Error: Failed to authenticate, have you run firebase login?
despite steps 2 confirming valid credentials.
Minimal reproduction config (deploy.yml excerpt):
- uses: google-github-actions/auth@v3
with:
workload_identity_provider: ${{ vars.GCP_WORKLOAD_IDENTITY_PROVIDER }}
service_account: ${{ vars.GCP_DEPLOY_SERVICE_ACCOUNT }}
create_credentials_file: true
export_environment_variables: true # sets GOOGLE_APPLICATION_CREDENTIALS
- run: |
gcloud auth print-access-token # ✅ works
pnpm exec firebase deploy --only functions # ❌ "Failed to authenticate"
Expected behavior
firebase deploy should respect GOOGLE_APPLICATION_CREDENTIALS / ADC as a valid authentication path, consistent with every other Google Cloud SDK and client library. Minimally, one of:
Option A: Honor a GOOGLE_OAUTH_ACCESS_TOKEN environment variable as a direct bearer-token override (identical to gcloud's behavior).
Option B: Remove or significantly increase the timeout on autoAuth() so ADC via WIF credential exchange is given sufficient time to complete.
Option C: Surface the actual ADC error in the thrown exception rather than discarding it and replacing it with the generic "have you run firebase login?" message — this would at minimum allow users to diagnose the real problem.
Workaround
Bypass firebase-tools for Cloud Functions deployment by calling the Cloud Functions v2 REST API directly, using a bearer token from gcloud auth print-access-token:
// generateUploadUrl → upload zip → PATCH each function → poll operations
const token = execSync('gcloud auth print-access-token', { encoding: 'utf8' }).trim();
// Upload source
const { uploadUrl, storageSource } = await fetch(
https://cloudfunctions.googleapis.com/v2/projects/${project}/locations/${region}/functions:generateUploadUrl,
{ method: 'POST', headers: { Authorization: Bearer ${token}, 'Content-Type': 'application/json' }, body: '{}' }
).then(r => r.json());
// PATCH each function to reference the new source
const op = await fetch(
https://cloudfunctions.googleapis.com/v2/projects/${project}/locations/${region}/functions/${name}?updateMask=buildConfig.source,
{ method: 'PATCH', headers: { Authorization: Bearer ${token}, 'Content-Type': 'application/json' },
body: JSON.stringify({ buildConfig: { source: { storageSource } } }) }
).then(r => r.json());
This API accepts any valid GCP bearer token. The google-github-actions/auth WIF credentials work perfectly via this path — only firebase-tools' own auth layer rejects them.
Additional context: This regression likely affects any user who followed GCP's guidance to migrate from long-lived service account keys to Workload Identity Federation in GitHub Actions CI. The symptom (a generic "have you run firebase login?" error with no indication ADC was attempted) makes it appear to be a login issue rather than a timeout/auth-chain-ordering bug, which significantly increases debugging time.
firebase-tools:
Platform:
[REQUIRED] Test case
[REQUIRED] Steps to reproduce
[REQUIRED] Expected behavior
[REQUIRED] Actual behavior
Product: firebase-tools
Version affected: v15.x (confirmed v15.17.0; likely all v15 releases)
Environment: GitHub Actions / any CI using google-github-actions/auth with Workload Identity Federation
Problem statement
When firebase deploy is run in a GitHub Actions environment where authentication was established via google-github-actions/auth with Workload Identity Federation, it throws:
Error: Failed to authenticate, have you run firebase login?
This occurs even though:
GOOGLE_APPLICATION_CREDENTIALS is set to a valid credentials file
gcloud auth print-access-token succeeds and returns a valid token
The service account has full roles/firebase.admin and roles/cloudfunctions.developer IAM bindings
Every other GCP API called in the same CI job (Firestore REST API, Storage REST API, gcloud CLI) authenticates successfully with the same credentials
Root cause analysis
The auth resolution chain in requireAuth.js (paraphrased) is:
firebase login)Step 4 is where it fails silently. autoAuth() calls google-auth-library's GoogleAuth, which itself reads ADC correctly — but firebase-tools wraps this in a hardcoded timeout. If autoAuth() does not resolve within that window (observed in WIF environments where the credential exchange adds latency), firebase-tools catches the timeout and surfaces the generic "have you run firebase login?" error, discarding the actual ADC failure reason.
Critically, GOOGLE_OAUTH_ACCESS_TOKEN is never consulted anywhere in this chain — not as an env-var shortcut, not as a fallback. This is inconsistent with gcloud, which accepts a raw bearer token via that variable directly.
The result is that the only reliable CI workaround in firebase-tools v15 is the deprecated --token flag with a long-lived OAuth 2.0 refresh token (FIREBASE_TOKEN), which contradicts GCP's current security guidance to use short-lived WIF credentials.
Reproduction steps
Set up a GitHub Actions job using google-github-actions/auth@v3 with workload_identity_provider and service_account (standard WIF setup, create_credentials_file: true, export_environment_variables: true).
In the same job, confirm ADC works:
gcloud auth print-access-token # succeeds, returns valid token
curl -H "Authorization: Bearer $(gcloud auth print-access-token)"
"https://firebase.googleapis.com/v1beta1/projects/my-project" # 200 OK
Attempt a deploy:
pnpm exec firebase deploy --only functions:myFunction --project my-project
Observe:
Error: Failed to authenticate, have you run firebase login?
despite steps 2 confirming valid credentials.
Minimal reproduction config (deploy.yml excerpt):
with:
workload_identity_provider: ${{ vars.GCP_WORKLOAD_IDENTITY_PROVIDER }}
service_account: ${{ vars.GCP_DEPLOY_SERVICE_ACCOUNT }}
create_credentials_file: true
export_environment_variables: true # sets GOOGLE_APPLICATION_CREDENTIALS
gcloud auth print-access-token # ✅ works
pnpm exec firebase deploy --only functions # ❌ "Failed to authenticate"
Expected behavior
firebase deploy should respect GOOGLE_APPLICATION_CREDENTIALS / ADC as a valid authentication path, consistent with every other Google Cloud SDK and client library. Minimally, one of:
Option A: Honor a GOOGLE_OAUTH_ACCESS_TOKEN environment variable as a direct bearer-token override (identical to gcloud's behavior).
Option B: Remove or significantly increase the timeout on autoAuth() so ADC via WIF credential exchange is given sufficient time to complete.
Option C: Surface the actual ADC error in the thrown exception rather than discarding it and replacing it with the generic "have you run firebase login?" message — this would at minimum allow users to diagnose the real problem.
Workaround
Bypass firebase-tools for Cloud Functions deployment by calling the Cloud Functions v2 REST API directly, using a bearer token from gcloud auth print-access-token:
// generateUploadUrl → upload zip → PATCH each function → poll operations
const token = execSync('gcloud auth print-access-token', { encoding: 'utf8' }).trim();
// Upload source
const { uploadUrl, storageSource } = await fetch(
https://cloudfunctions.googleapis.com/v2/projects/${project}/locations/${region}/functions:generateUploadUrl,{ method: 'POST', headers: { Authorization:
Bearer ${token}, 'Content-Type': 'application/json' }, body: '{}' }).then(r => r.json());
// PATCH each function to reference the new source
const op = await fetch(
https://cloudfunctions.googleapis.com/v2/projects/${project}/locations/${region}/functions/${name}?updateMask=buildConfig.source,{ method: 'PATCH', headers: { Authorization:
Bearer ${token}, 'Content-Type': 'application/json' },body: JSON.stringify({ buildConfig: { source: { storageSource } } }) }
).then(r => r.json());
This API accepts any valid GCP bearer token. The google-github-actions/auth WIF credentials work perfectly via this path — only firebase-tools' own auth layer rejects them.
Additional context: This regression likely affects any user who followed GCP's guidance to migrate from long-lived service account keys to Workload Identity Federation in GitHub Actions CI. The symptom (a generic "have you run firebase login?" error with no indication ADC was attempted) makes it appear to be a login issue rather than a timeout/auth-chain-ordering bug, which significantly increases debugging time.