GCP Setup for Cloud Storage Buckets

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

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. The Google consent screen requests only non-sensitive scopes (openid email profile) — bucket access comes entirely from the IAM bindings you set below, not from an OAuth scope.

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://mycorr.app   # use your Mycorr deployment's URL
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/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. New ConnectionCloud Storage BucketGoogle Cloud Storage.
  2. Access scope: Single bucket (and the bucket name) or Entire project.
  3. Click Sign in with Google (consents to non-sensitive openid email profile only).
  4. Paste the audience string from Step 4 into WIF audience. (Mycorr shows the matching client ID inline for cross-checking.)
  5. Test connection and 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

Error in MycorrLikely causeFix
linked Google connection not foundUser hasn't signed in, or removed the Google connectionClick Sign in with Google again on the connection form
Google STS exchange failed (400): invalid_targetWIF audience string mismatchRe-copy the audience from Step 4
Google STS exchange failed (400): allowed audiences not configured--allowed-audiences missing or wrongRe-create the WIF provider with the flag
Google STS exchange failed (400): audience does not match--allowed-audiences includes a prefix — Google sets aud to the bare client_idUse MYCORR_AUDIENCE exactly as /api/public/federation/config returns it
Google STS exchange failed (403): attribute condition not satisfiedUser identity (email / hd) doesn't match the conditionCheck the condition expression; the user should sign out and back in if they signed in before the field was added
403 storage.buckets.list permission denied (project scope)principalSet has objectViewer but not bucketViewerAdd roles/storage.bucketViewer at project level
403 storage.objects.list permission deniedBucket IAM doesn't include the federated principalSetRe-check the bucket binding step
ALREADY_EXISTS creating 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 requests only non-sensitive scopes: openid, email, and profile. Mycorr needs the openid id-token to federate — it never asks for a Cloud Storage or cloud-platform scope, so there is no "View and manage your data across Google Cloud services" prompt and no Google sensitive-scope verification.

What Mycorr can do with your bucket is bounded entirely by your IAM: the federated principalSet is granted only storage.objectViewer (+ bucketViewer for project scope) — read-only on the bucket. The OAuth grant proves who the user is; your IAM bindings decide what they can read.