GCP Setup for Cloud Storage Buckets

This page is for the GCP admin setting up the Workload Identity method of a bucket connection. Run these commands once, then hand the result (a WIF audience string) to your teammates to paste into Mycorr.

You don't need any of this for the default Sign in with Google method, which reads a bucket with the user's own Google access.

Mycorr uses Workload Identity Federation (WIF): users sign in with Google, and Mycorr exchanges the user's Google id-token against your WIF provider for a short-lived federated token that reads the bucket directly. No service-account keys and no service-account impersonation. What the federated token can read comes entirely from the IAM bindings you set below.

The user's Google identity is the trust anchor; Mycorr never holds a long-lived customer secret.

What Mycorr signs as

Google is the OIDC issuer. The id-token Mycorr presents looks like:

{
  "iss": "https://accounts.google.com",
  "sub": "108234567890123456789",
  "email": "you@yourcompany.com",
  "email_verified": true,
  "hd": "yourcompany.com",
  "aud": "<Mycorr OAuth client_id>",
  "exp": ...
}

Your WIF provider validates the signature against Google's JWKS, checks the aud against --allowed-audiences, and evaluates your --attribute-condition against the mapped claims.

Setup

Prerequisites

  • A GCP project with Cloud Storage, IAM, and Security Token Service APIs enabled.
  • A GCS bucket.
  • gcloud authenticated with iam.workloadIdentityPoolAdmin and storage.admin.
  • Mycorr users sign in with Workspace Google accounts. Personal @gmail.com accounts have no hd claim and will fail the default org-domain condition (usually what you want).

Step 1 — Set variables

export PROJECT=your-gcp-project
export BUCKET=your-bucket-name
export MYCORR_HOST=https://space.mycorr.app
gcloud config set project $PROJECT
PROJECT_NUM=$(gcloud projects describe $PROJECT --format='value(projectNumber)')

# Mycorr's allowed-audience — the bare OAuth client_id Google sets as
# the `aud` claim on user-OAuth id-tokens. Stable per Mycorr deployment.
# (Mycorr also shows this value inline on the connection form.)
MYCORR_AUDIENCE=$(curl -s $MYCORR_HOST/server/api/public/federation/config | jq -r .allowed_audience)

# The user (or domain) you want to authorize.
USER_EMAIL=you@yourcompany.com
USER_DOMAIN=${USER_EMAIL#*@}

Step 2 — Create the Workload Identity Pool + Provider

gcloud iam workload-identity-pools create mycorr-pool \
  --location=global --project=$PROJECT

gcloud iam workload-identity-pools providers create-oidc mycorr-issuer \
  --workload-identity-pool=mycorr-pool --location=global --project=$PROJECT \
  --issuer-uri="https://accounts.google.com" \
  --allowed-audiences="$MYCORR_AUDIENCE" \
  --attribute-mapping="google.subject=assertion.sub,attribute.email=assertion.email,attribute.org=assertion.hd" \
  --attribute-condition="attribute.email == '$USER_EMAIL'"

# Or trust every user in your Workspace domain — swap the last flag:
# --attribute-condition="attribute.org == '$USER_DOMAIN'"

The three flags doing real work:

  • --issuer-uri=https://accounts.google.com — Google is the OIDC issuer; GCP fetches Google's JWKS to verify token signatures.
  • --allowed-audiences=$MYCORR_AUDIENCE — required. Without it, STS only accepts tokens whose aud equals the provider resource name.
  • --attribute-condition=... — the security gate. Email-pinned for one user, or attribute.org for whole-org trust.

Step 3 — Grant the federated principalSet read on your bucket

Bucket-scoped (recommended):

PREFIX="principalSet://iam.googleapis.com/projects/$PROJECT_NUM/locations/global/workloadIdentityPools/mycorr-pool"

# Anyone in your Workspace domain:
PRINCIPAL_SET="$PREFIX/attribute.org/$USER_DOMAIN"

# Or a specific user:
# PRINCIPAL_SET="$PREFIX/attribute.email/$USER_EMAIL"

gcloud storage buckets add-iam-policy-binding gs://$BUCKET \
  --member="$PRINCIPAL_SET" --role=roles/storage.objectViewer

Project-scoped: grant both bucketViewer (to enable buckets.list) and objectViewer at the project level:

gcloud projects add-iam-policy-binding $PROJECT \
  --member="$PRINCIPAL_SET" --role=roles/storage.bucketViewer

gcloud projects add-iam-policy-binding $PROJECT \
  --member="$PRINCIPAL_SET" --role=roles/storage.objectViewer

Step 4 — Hand the audience string to the Mycorr user

echo "//iam.googleapis.com/projects/$PROJECT_NUM/locations/global/workloadIdentityPools/mycorr-pool/providers/mycorr-issuer"

What the Mycorr user does in the app

  1. Connections → Add connection, pick the bucket type, and set Method to Workload Identity.
  2. Access scope: Single bucket (and the bucket name) or Entire project.
  3. Click Sign in with Google and approve.
  4. Paste the audience string from Step 4 into WIF audience. (Copy Mycorr client ID shows the value used for --allowed-audiences, for cross-checking.)
  5. Click Test & save.

Revoking access

# Remove the principalSet binding (other resources unaffected):
gcloud storage buckets remove-iam-policy-binding gs://$BUCKET \
  --member="$PRINCIPAL_SET" --role=roles/storage.objectViewer

# Tighten the condition (kicks off any user no longer matching):
gcloud iam workload-identity-pools providers update-oidc mycorr-issuer \
  --workload-identity-pool=mycorr-pool --location=global \
  --attribute-condition="attribute.email == 'specific-user@$USER_DOMAIN'"

# Nuclear — delete the provider (all Mycorr federation against it stops
# on next refresh):
gcloud iam workload-identity-pools providers delete mycorr-issuer \
  --workload-identity-pool=mycorr-pool --location=global

In-flight 1-hour federated tokens stay valid until expiry; deleting the provider cuts off new exchanges immediately.

Troubleshooting

What the user seesLikely causeFix
"linked google_saved_connection_id not found — sign in to Google again"The user hasn't signed in, or removed the Google connectionClick Sign in with Google again on the connection
A server error when testing the connectionThe STS exchange was refused: the audience doesn't match (--allowed-audiences must be the bare client ID), or the user's email / hd fails --attribute-conditionRe-check Steps 2 and 4, and the provider's condition
"Permission denied by cloud storage. Grant roles/storage.objectViewer…"The principalSet has no read on the bucket, or lacks bucketViewer for project scopeRe-check Step 3
"Bucket or path not found…"Wrong bucket name, or the federated identity can't see itCheck the name and the bucket binding
ALREADY_EXISTS creating the WIF pool / providerGCP soft-deletes for 30 daysUse undelete + update-oidc, or pick a new name

Audit logging

The federated principal encodes the user's stable sub in principalSubject. Cross-reference against your IdP / Workspace directory.

resource.type="gcs_bucket"
resource.labels.bucket_name="$BUCKET"
protoPayload.authenticationInfo.principalSubject=~"workloadIdentityPools/mycorr-pool"

Note on OAuth scopes

The Google consent screen asks for openid, email, profile and the read-only Cloud Storage scope (devstorage.read_only). One sign-in serves both connection methods, so switching between them never asks again. Workload Identity doesn't use the Cloud Storage scope: it exchanges the openid id-token for its own federated token.

With Workload Identity, what Mycorr can read is bounded by your IAM: the federated principalSet is granted only storage.objectViewer (+ bucketViewer for project scope), read-only on the bucket. The sign-in proves who the user is; your IAM bindings decide what they can read.