Question: Under Google Cloud Run, you can select which service account your container is running. Using the default compute service account fails to generate a signed url. The work around listed here works on Google Cloud Compute -- if you allow all the scopes for the service account. There does not seem to be away to do that in Cloud Run (not that I can find). https://github.com/googleapis/google-auth-library-python/issues/50 Things I have tried:  Assigned the service account the role: roles/iam.serviceAccountTokenCreator Verified the workaround in the same GCP project in a Virtual Machine (vs Cloud Run) Verified the code works locally in the container with the service account loaded from private key (via json file).  from google.cloud import storage client = storage.Client() bucket = client.get_bucket('EXAMPLE_BUCKET') blob = bucket.get_blob('libraries/image_1.png') expires = datetime.now() + timedelta(seconds=86400) blob.generate_signed_url(expiration=expires)  Fails with: you need a private key to sign credentials.the credentials you are currently using <class 'google.auth.compute_engine.credentials.Credentials'> just contains a token. see https://googleapis.dev/python/google-api-core/latest/auth.html#setting-up-a-service-account for more details. /usr/local/lib/python3.8/site-packages/google/cloud/storage/_signing.py, line 51, in ensure_signed_credentials  Trying to add the workaround, Error calling the IAM signBytes API: { "error": { "code": 400, "message": "Request contains an invalid argument.", "status": "INVALID_ARGUMENT" } } Exception Location: /usr/local/lib/python3.8/site-packages/google/auth/iam.py, line 81, in _make_signing_request  Workaround code as mention in Github issue: from google.cloud import storage from google.auth.transport import requests from google.auth import compute_engine from datetime import datetime, timedelta def get_signing_creds(credentials): auth_request = requests.Request() print(credentials.service_account_email) signing_credentials = compute_engine.IDTokenCredentials(auth_request, "", service_account_email=credentials.ser vice_account_email) return signing_credentials client = storage.Client() bucket = client.get_bucket('EXAMPLE_BUCKET') blob = bucket.get_blob('libraries/image_1.png') expires = datetime.now() + timedelta(seconds=86400) signing_creds = get_signing_creds(client._credentials) url = blob.generate_signed_url(expiration=expires, credentials=signing_creds) print(url)  How do I generate a signed url under Google Cloud Run? At this point, it seems like I may have to mount the service account key which I wanted to avoid. EDIT: To try and clarify, the service account has the correct permissions - it works in GCE and locally with the JSON private key.

Answer: Yes you can, but I had to deep dive to find how (jump to the end if you don't care about the details) If you go in the _signing.py file, line 623, you can see this if access_token and service_account_email: signature = _sign_message(string_to_sign, access_token, service_account_email) ...  If you provide the access_token and the service_account_email, you can use the _sign_message method. This method uses the IAM service SignBlob API at this line It's important because you can now sign blob without having locally the private key!! So, that solves the problem, and the following code works on Cloud Run (and I'm sure on Cloud Function) def sign_url(): from google.cloud import storage from datetime import datetime, timedelta import google.auth credentials, project_id = google.auth.default() # Perform a refresh request to get the access token of the current credentials (Else, it's None) from google.auth.transport import requests r = requests.Request() credentials.refresh(r) client = storage.Client() bucket = client.get_bucket('EXAMPLE_BUCKET') blob = bucket.get_blob('libraries/image_1.png') expires = datetime.now() + timedelta(seconds=86400) # In case of user credential use, define manually the service account to use (for development purpose only) service_account_email = "YOUR DEV SERVICE ACCOUNT" # If you use a service account credential, you can use the embedded email if hasattr(credentials, "service_account_email"): service_account_email = credentials.service_account_email url = blob.generate_signed_url(expiration=expires,service_account_email=service_account_email, access_token=credentials.token) return url, 200  Let me know if it's not clear