API Security: Validating Identity Platform JWTs at the Gateway
Scenario
Your mobile app allows users to log in (using email or Google OAuth). This process leverages GCP Identity Platform (formerly Firebase Auth), which issues a JWT (JSON Web Token) to the user's phone. When the phone wants to read data from your API, it attaches the JWT. You will configure an API Gateway OpenAPI specification to automatically sniff the token, validate the cryptographic signature against Google's public keys, and reject unauthenticated requests before they ever reach your backend Cloud Run service.
- Update a openapi.yaml file to include the securityDefinitions block targeting Firebase/Identity Platform.
- Deploy a Cloud Run backend (unauthenticated, strictly relying on API Gateway to block abuse).
- Deploy the google_api_gateway_api and google_api_gateway_api_config containing the security configuration.
Additional Context
API Gateway Security Models: GCP API Gateway can perform several types of security validation before routing traffic: API Keys (`x-api-key`), Service Account signatures, or JWTs. Validating JWTs at the Gateway removes the burden of writing JWT parsing/verification libraries into your application code.
Firebase/Identity Provider URI: The issuer URL for Identity Platform JWTs is rigidly defined as `https://securetoken.google.com/[YOUR_PROJECT_ID]`.
openapi.yaml.tpl
swagger: "2.0"
info:
title: "Secure User Data API"
version: "1.0.0"
paths:
/profile:
get:
summary: "Get User Profile"
operationId: "getProfile"
# Route request to backend
x-google-backend:
address: "${backend_url}"
# Enforce the JWT security definition defined below!
security:
- firebase: []
responses:
200:
description: "Success"
# Define How the Gateway Validates the Token
securityDefinitions:
firebase:
# Must be of type oauth2 or apiKey in swagger, but x-google-issuer makes it JWT
authorizationUrl: ""
flow: "implicit"
type: "oauth2"
x-google-issuer: "https://securetoken.google.com/${project_id}"
x-google-jwks_uri: "https://www.googleapis.com/service_accounts/v1/metadata/x509/[email protected]"
x-google-audiences: "${project_id}"main.tf
provider "google" {
project = var.project_id
region = "us-central1"
}
provider "google-beta" {
project = var.project_id
region = "us-central1"
}
# 1. The Backend (Blindly trusts the Gateway for authentication)
resource "google_cloud_run_v2_service" "backend" {
name = "user-profile-api"
location = "us-central1"
template {
containers { image = "us-docker.pkg.dev/cloudrun/container/hello" }
}
}
# Allow external traffic (Gateway) to access the backend unauthenticated
resource "google_cloud_run_service_iam_member" "allUsers" {
location = google_cloud_run_v2_service.backend.location
service = google_cloud_run_v2_service.backend.name
role = "roles/run.invoker"
member = "allUsers"
}
# 2. Add API API Gateway
resource "google_api_gateway_api" "secure_api" {
provider = google-beta
api_id = "jwt-secured-api"
}
resource "google_api_gateway_api_config" "secure_config" {
provider = google-beta
api = google_api_gateway_api.secure_api.api_id
api_config_id = "jwt-cfg-v1"
openapi_documents {
document {
path = "openapi.yaml"
contents = base64encode(templatefile("openapi.yaml.tpl", {
backend_url = google_cloud_run_v2_service.backend.uri
project_id = var.project_id
}))
}
}
# Identity Platform validation sometimes breaks if the backend hasn't settled
lifecycle { create_before_destroy = true }
}
resource "google_api_gateway_gateway" "api_gw" {
provider = google-beta
api_config = google_api_gateway_api_config.secure_config.id
gateway_id = "jwt-gw"
region = "us-central1"
}Workflow Commands
terraform init
terraform apply -var="project_id=my-gcp-project-123"
# Attempt standard access (Will return 401 Unauthorized via Gateway)
curl https://$(terraform output -raw gateway_url)/profile
# Attempt authenticated access (Will return 200 OK from Cloud Run)
curl -H "Authorization: Bearer " \
https://$(terraform output -raw gateway_url)/profile
terraform destroy -var="project_id=my-gcp-project-123" 
