fix: add authentication for Cloud Run service calls

Cloud Run services are deployed with --no-allow-unauthenticated,
requiring an ID token in the Authorization header.

- Add _get_cloud_run_id_token() helper using google-auth library
- Update whisper_transcribe.py to include Bearer token in Cloud Run calls
- Update video_renderer.py to include Bearer token in FFmpeg Cloud Run calls

The ID token is fetched using the service account credentials
(GOOGLE_APPLICATION_CREDENTIALS) and targets the Cloud Run service URL.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
michael 2026-01-02 11:41:07 -06:00
parent 77dc58b124
commit 7d2366d0f4
2 changed files with 57 additions and 5 deletions

View file

@ -14,8 +14,11 @@ from pathlib import Path
from typing import Any
from uuid import uuid4
import google.auth.transport.requests
import httpx
from google.auth import default
from google.cloud import storage
from google.oauth2 import id_token
from ..core.config import settings
from ..core.logging import get_logger
@ -24,6 +27,19 @@ from ..schemas.accessible_video import AccessibleVideoMethod, GeminiAccessibleVi
logger = get_logger(__name__)
def _get_cloud_run_id_token(audience: str) -> str:
"""
Get an ID token for authenticating to Cloud Run services.
Uses the service account credentials to generate an ID token
that Cloud Run will accept for authentication.
"""
credentials, _ = default()
request = google.auth.transport.requests.Request()
token = id_token.fetch_id_token(request, audience)
return token
class FFmpegExecutionError(Exception):
"""Raised when an FFmpeg/FFprobe command fails."""
pass
@ -153,9 +169,13 @@ class VideoRendererService:
Probe result with duration and stream info
"""
client = self._get_http_client()
service_url = settings.ffmpeg_service_url.rstrip("/")
auth_token = _get_cloud_run_id_token(service_url)
response = client.post(
f"{settings.ffmpeg_service_url}/probe",
json={"source_gcs_uri": gcs_uri}
f"{service_url}/probe",
json={"source_gcs_uri": gcs_uri},
headers={"Authorization": f"Bearer {auth_token}"}
)
response.raise_for_status()
return response.json()
@ -178,10 +198,14 @@ class VideoRendererService:
Response JSON
"""
client = self._get_http_client()
service_url = settings.ffmpeg_service_url.rstrip("/")
auth_token = _get_cloud_run_id_token(service_url)
try:
response = client.post(
f"{settings.ffmpeg_service_url}{endpoint}",
json=payload
f"{service_url}{endpoint}",
json=payload,
headers={"Authorization": f"Bearer {auth_token}"}
)
response.raise_for_status()
return response.json()

View file

@ -3,8 +3,11 @@
import os
import uuid
import google.auth.transport.requests
import httpx
from google.auth import default
from google.cloud import storage
from google.oauth2 import id_token
from ..core.config import settings
from ..core.logging import get_logger
@ -14,6 +17,25 @@ from . import celery_app
logger = get_logger(__name__)
def _get_cloud_run_id_token(audience: str) -> str:
"""
Get an ID token for authenticating to Cloud Run services.
Uses the service account credentials to generate an ID token
that Cloud Run will accept for authentication.
"""
# Get credentials from the environment (GOOGLE_APPLICATION_CREDENTIALS)
credentials, _ = default()
# Create a request object for token refresh
request = google.auth.transport.requests.Request()
# Fetch an ID token for the target audience (the Cloud Run service URL)
token = id_token.fetch_id_token(request, audience)
return token
def _upload_audio_to_gcs_temp(audio_path: str, job_id: str) -> str:
"""Upload local audio file to GCS temporary location and return GCS URI."""
# Generate unique temp path
@ -70,11 +92,17 @@ def _transcribe_via_cloud_run(job_id: str, audio_path: str) -> dict:
logger.info(f"Calling Whisper Cloud Run service: {endpoint}")
# Get ID token for Cloud Run authentication
id_token = _get_cloud_run_id_token(service_url)
with httpx.Client(timeout=300.0) as client:
response = client.post(
endpoint,
json={"gcs_uri": gcs_uri},
headers={"Content-Type": "application/json"}
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {id_token}"
}
)
response.raise_for_status()