I've configured a cloud endpoint that executes a GCF. Everything works fine when the cloud run service is allowing allUsers
to call the API.
Once I remove the allUsers
and authenticate using the service account, I get 403 errors showing up in the Cloud run console:
The request was not authenticated. Either allow unauthenticated invocations or set the proper Authorization header. Read more at https://cloud.google.com/run/docs/securing/authenticating
Chrome JS console shows the following error message:
Access to fetch at 'https://.run.app/do-this&key=' from origin 'http://0.0.0.0:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
This is my JS code running in the browser:
let options: RequestInit = {
headers: {
'Authorization': `Bearer ${token}`,
},
}
const result = await fetch(fetchURL, options);
When running curl with the same token, I get the expected response
curl -H "Authorization: Bearer ${token}" 'https://<my-api>.run.app/do-this&key=<key>'
For completeness here is also the endpoints yaml
swagger: '2.0'
info:
title: My first widget
description: This is a great widget
version: 1.0.0
host: <my-api>.run.app
schemes:
- https
produces:
- application/json
paths:
/do-this:
get:
summary: Do-this
operationId: doit
x-google-backend:
address: https://<project-id>.cloudfunctions.net/do-that
responses:
'200':
description: A successful response.
schema:
type: string
'403':
description: An error occurred
schema:
type: string
security:
- api_key: []
securityDefinitions:
# This section configures basic authentication with an API key.
api_key:
type: "apiKey"
name: "key"
in: "query"
Command to update esp:
gcloud run services update <my-api> --set-env-vars="^|^ENDPOINTS_SERVICE_NAME=<my-api>.run.app|ESP_ARGS=--rollout_strategy=managed,--cors_preset=basic" --project=<project-id> --platform=managed --region=europe-west1
Update
Enabling cors browser side did not help.
The Google docs mention that it should be possible to call from outside GCP
If you're invoking a service from a compute instance that doesn't have access to compute metadata (e.g. your own server), you'll have to manually generate the proper token:
Self-sign a service account JWT with the target_audience claim set to the URL of the receiving service. Exchange the self-signed JWT for a Google-signed ID token, which should have the aud claim set to the above URL. Include the ID token in an Authorization: Bearer ID_TOKEN header in the request to the service. Although Identity-Aware Proxy is not yet supported for Cloud Run (fully managed), you can examine the Identity-Aware Proxy sample code for code examples of the steps above.
The end-users section: mentions CORS though
When you build a web app, you have to account for Cross-Origin Resource Sharing (CORS) issues. For example, CORS preflight requests are sent without an Authorization header, so they are rejected on a non-public service. Because the preflight requests fail, the main request will also fail.
To work around this, you can host your web app and service(s) on the same domain to avoid CORS preflight requests. You can achieve that by using Firebase Hosting.
I tried hosting the JS script and HTML on Firebase hosting, yet the issue persists.
Another question that comes to mind is: do I need to set OAuth alongside API key authentication in the open api specification?
Update 2
This discussion suggests it is not possible to use Cloud Run with Authentication supporting CORS. I'm yet wondering why it's possible in curl. I'm using a service account token for auth, not end user.