-
Notifications
You must be signed in to change notification settings - Fork 216
Open
Labels
Description
[REQUIRED] Environment info
firebase-functions: 6.4.0
firebase-tools: 14.16.0
Platform: macOS (Darwin 25.0.0)
[REQUIRED] Test case
import { onSchedule } from 'firebase-functions/v2/scheduler'
// Deploy this function
export const testScheduler = onSchedule(
{
schedule: 'every 2 minutes',
timeZone: 'America/Los_Angeles',
},
async () => {
console.log('Test scheduler executed at', new Date().toISOString())
}
)
[REQUIRED] Steps to reproduce
- Delete any existing scheduled functions:
firebase functions:delete FUNCTION_NAME --force
- Deploy a fresh v2 scheduled function:
firebase deploy --only functions
- Check Cloud Scheduler console - job is created with correct Cloud Run URL
- Wait for scheduled execution or manually trigger
- Cloud Scheduler reports
PERMISSION_DENIED
error - However, manually invoking the Cloud Run URL with proper auth token works:
TOKEN=$(gcloud auth print-identity-token --impersonate-service-account=SERVICE_ACCOUNT --audiences=CLOUD_RUN_URL)
curl -X POST CLOUD_RUN_URL -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{}'
# Returns 200 OK
[REQUIRED] Expected behavior
Cloud Scheduler should successfully invoke the v2 scheduled functions without permission errors.
[REQUIRED] Actual behavior
- Cloud Scheduler jobs are created with correct Cloud Run URLs (
.run.app
format) - Cloud Run services have correct IAM bindings (
roles/run.invoker
for the service account) - Scheduler jobs have correct OIDC token configuration
- BUT all scheduled invocations fail with
PERMISSION_DENIED
- Direct invocation with the same service account credentials works fine
Details and Analysis
After investigation, the issue appears to be with how Firebase sets up the authentication chain between Cloud Scheduler and Cloud Run for v2 functions:
- Cloud Scheduler Configuration (looks correct):
{
"httpTarget": {
"uri": "https://FUNCTION_NAME-HASH-uc.a.run.app/",
"httpMethod": "POST",
"headers": {
"User-Agent": "Google-Cloud-Scheduler"
},
"oidcToken": {
"serviceAccountEmail": "[email protected]",
"audience": "https://FUNCTION_NAME-HASH-uc.a.run.app"
}
}
}
- Cloud Run IAM Policy (looks correct):
bindings:
- members:
- serviceAccount:[email protected]
role: roles/run.invoker
- Yet the error persists:
{
"@type": "type.googleapis.com/google.cloud.scheduler.logging.AttemptFinished",
"jobName": "projects/PROJECT/locations/REGION/jobs/firebase-schedule-FUNCTION",
"status": "PERMISSION_DENIED",
"targetType": "HTTP",
"url": "https://FUNCTION_NAME-HASH-uc.a.run.app/"
}
Affected Functions State
- Functions are in
ACTIVE
state - Cloud Run services exist and are healthy
- Manual invocation with identity tokens works
- Only Cloud Scheduler invocation fails
Workaround Attempts That Don't Work
- Deleting and redeploying functions
- Using object syntax for
onSchedule()
(fixes URL issue from onSchedule() with string parameter creates Cloud Scheduler jobs with incorrect v1 URLs instead of v2 Cloud Run URLs #1734 but not auth) - Waiting for propagation (issue persists after hours)
Potential Root Causes
- The App Engine default service account may need additional permissions to create identity tokens for itself
- There may be a missing step in the Firebase deployment process for setting up the OAuth flow
- The OIDC audience field might need special formatting for v2 functions
Impact
This makes Firebase Functions v2 scheduled functions completely unusable in production, as they cannot be invoked by Cloud Scheduler despite appearing to be correctly configured.
Related Issues
- onSchedule() with string parameter creates Cloud Scheduler jobs with incorrect v1 URLs instead of v2 Cloud Run URLs #1734 (URL format issue - fixed by using object syntax but auth still fails)
- cloud function error: PERMISSION_DENIED: Missing or insufficient permissions #1425 (General PERMISSION_DENIED errors)
- Cloud Function V2 randomly not creating deterministic Function URL #1447 (Cloud Function V2 URL issues)
This is a critical issue as it prevents migration from v1 to v2 for any scheduled functions.