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.
gcloudauthenticated withiam.workloadIdentityPoolAdminandstorage.admin.- Mycorr users sign in with Workspace Google accounts. Personal
@gmail.comaccounts have nohdclaim 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 whoseaudequals the provider resource name.--attribute-condition=...— the security gate. Email-pinned for one user, orattribute.orgfor 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
- New Connection → Cloud Storage Bucket → Google Cloud Storage.
- Access scope: Single bucket (and the bucket name) or Entire project.
- Click Sign in with Google (consents to non-sensitive
openid email profileonly). - Paste the audience string from Step 4 into WIF audience. (Mycorr shows the matching client ID inline for cross-checking.)
- 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 Mycorr | Likely cause | Fix |
|---|---|---|
linked Google connection not found | User hasn't signed in, or removed the Google connection | Click Sign in with Google again on the connection form |
Google STS exchange failed (400): invalid_target | WIF audience string mismatch | Re-copy the audience from Step 4 |
Google STS exchange failed (400): allowed audiences not configured | --allowed-audiences missing or wrong | Re-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_id | Use MYCORR_AUDIENCE exactly as /api/public/federation/config returns it |
Google STS exchange failed (403): attribute condition not satisfied | User identity (email / hd) doesn't match the condition | Check 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 bucketViewer | Add roles/storage.bucketViewer at project level |
403 storage.objects.list permission denied | Bucket IAM doesn't include the federated principalSet | Re-check the bucket binding step |
ALREADY_EXISTS creating WIF pool / provider | GCP soft-deletes for 30 days | Use 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.