We’re implementing financial data synchronization between our billing system and Google Cloud IoT Core using REST API calls. The integration works fine in our test environment, but production keeps returning 401 Unauthorized errors when posting journal entries.
The OAuth2 token generation succeeds, but the actual POST request fails:
POST /v1/projects/fin-prod/locations/us-central1/registries/billing/devices/journal-sync
Authorization: Bearer eyJhbGc...truncated
HTTP/1.1 401 Unauthorized
{"error": "invalid_token", "error_description": "Token validation failed"}
We’re using service account credentials with the cloudiot.devices.create scope. Our API gateway configuration points to the IoT Core endpoint with multi-tenant validation enabled. The token works for reading device states but fails when attempting to write journal entry data. Has anyone encountered OAuth2 scope issues or multi-tenant token validation problems with Cloud IoT API gateway setup? We need this working for our month-end close process.
We had this exact problem last quarter during our Cloud IoT migration. The issue was our API gateway setup wasn’t properly configured for multi-tenant token validation. Here’s what we discovered: the gateway needs explicit tenant-to-registry mapping in its configuration, and the OAuth2 token must include custom claims for tenant identification. Without these, every request hits the default validation path which rejects cross-tenant operations even with correct scopes.
I’ve seen this exact issue before. The problem is likely that your OAuth2 scope is too narrow. For posting data to Cloud IoT devices, you need both cloudiot.devices.create AND cloudiot.devices.updateConfig. The error message is misleading because it says invalid_token when it’s actually a permissions issue. Check your service account IAM roles - you probably need Cloud IoT Device Controller role, not just Cloud IoT Viewer.
Thanks Sarah. I added the Device Controller role and expanded the OAuth2 scopes to include both permissions you mentioned. Still getting 401 errors though. Our multi-tenant setup has three different billing entities, and I’m wondering if the token validation is checking tenant context. The service account is configured at the project level - should it be at the registry level instead for multi-tenant scenarios?
Alex, your multi-tenant suspicion is correct. When you have multiple billing entities, each needs its own device registry with separate authentication contexts. The token validation fails because Cloud IoT Core checks the registry-level permissions, not just project-level. You need to create service accounts scoped to each registry and use tenant-specific tokens. Also verify your API gateway configuration has the correct audience claim in the JWT token - it should match your registry path exactly. This is a common gotcha with multi-tenant IoT deployments where financial data segregation is critical.
I spent two weeks solving this for a financial services client. The root cause is insufficient OAuth2 scope configuration combined with improper API gateway routing for multi-tenant scenarios. Your current token likely lacks the required custom claims and audience specificity.
First, fix your OAuth2 scope configuration. You need these specific scopes:
scopes:
- https://www.googleapis.com/auth/cloudiot
- https://www.googleapis.com/auth/cloud-platform
custom_claims:
tenant_id: "billing_entity_01"
registry_path: "projects/fin-prod/locations/us-central1/registries/billing"
Second, configure multi-tenant token validation in your API gateway. The gateway must extract the tenant_id claim and route to the correct registry. Without this, all tokens validate against the default registry, causing cross-tenant rejections.
Third, update your API gateway backend configuration:
backends:
- name: iot-billing
address: https://cloudiot.googleapis.com
jwt_audience: https://cloudiot.googleapis.com/projects/fin-prod/locations/us-central1/registries/billing
path_translation: CONSTANT_ADDRESS
The critical piece is the jwt_audience must match your exact registry path. This ensures token validation happens in the correct tenant context. Also verify your service account has roles/cloudiot.editor at the registry level, not just project level.
For your journal entry POST requests, include the tenant_id in both the token claims and as a custom header for additional validation. This dual-check approach prevents cross-tenant data leakage. After implementing these changes, test with curl to verify the token validation succeeds before integrating with your billing system. The month-end close process should work reliably once the OAuth2 scopes, multi-tenant validation, and API gateway routing are all properly aligned.
Adding to Mike’s point - I’ve debugged similar authentication chains and found that the API gateway middleware sometimes caches token validation results incorrectly in multi-tenant setups. If you’re using Cloud Endpoints or Apigee in front of IoT Core, check the token validation policy. The cache TTL might be too long, causing stale tenant context. Also, make sure your OAuth2 token includes the sub claim with the correct tenant identifier. Without it, the gateway can’t route to the right registry for validation.