All repos authenticate to GCP using Workload Identity Federation -- keyless, short-lived tokens with no service account JSON keys.
Architecture
flowchart TB
subgraph github["GitHub Actions (Stig-Johnny/*)"]
workflow["Workflow Job"]
end
subgraph gcp["Google Cloud Platform"]
subgraph wif["Workload Identity Pool: github-actions"]
provider["Provider: github<br/>Condition: repository_owner == 'Stig-Johnny'"]
end
sa["Service Account<br/>github-actions@invotek-github-infra"]
subgraph perms["Permissions"]
billing["Billing User"]
firebase["Firebase Admin"]
serviceusage["Service Usage Admin"]
end
end
workflow -->|"OIDC Token"| provider
provider -->|"Impersonate"| sa
sa --> perms
Resources
| Resource |
Value |
| GCP Project |
invotek-github-infra |
| Project Number |
1090472687120 |
| Billing Account |
015BBC-422A59-EB7AF4 |
| Service Account |
github-actions@invotek-github-infra.iam.gserviceaccount.com |
| WIF Provider |
projects/1090472687120/locations/global/workloadIdentityPools/github-actions/providers/github |
GitHub Secrets
Set on each repo that needs GCP access:
| Secret |
Value |
GCP_WORKLOAD_IDENTITY_PROVIDER |
WIF Provider path (see above) |
GCP_SERVICE_ACCOUNT |
Service account email |
GCP_BILLING_ACCOUNT |
Billing account ID |
Configured on: nutri-e, cutie, star-rewards
Service Account Roles
| Role |
Scope |
Purpose |
roles/billing.user |
Billing Account |
Link new projects to billing |
roles/firebase.admin |
invotek-github-infra |
Manage Firebase resources |
roles/firebase.admin |
cuti-e |
Cuti-E Firebase |
roles/firebase.admin |
claude-memory-mcp |
Memory MCP Firebase |
roles/firebase.admin |
reward-e-app |
Reward-E Firebase |
roles/serviceusage.serviceUsageAdmin |
invotek-github-infra |
Enable APIs |
Firebase Projects
| Project ID |
App |
Region |
Bundle ID |
reward-e-app |
Reward-E |
europe-west1 |
no.invotek.RewardE |
cuti-e |
Cuti-E |
us-central1 |
com.invotek.Cuti-E |
Usage in Workflows
jobs:
deploy:
runs-on: [self-hosted, Linux]
permissions:
id-token: write # Required for OIDC
contents: read
steps:
- uses: actions/checkout@v4
- uses: google-github-actions/auth@v2
with:
workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }}
service_account: ${{ secrets.GCP_SERVICE_ACCOUNT }}
- uses: google-github-actions/setup-gcloud@v2
- run: gcloud projects list
Adding GCP to a New Repo
gh secret set GCP_WORKLOAD_IDENTITY_PROVIDER \
--repo Stig-Johnny/NEW_REPO \
--body "projects/1090472687120/locations/global/workloadIdentityPools/github-actions/providers/github"
gh secret set GCP_SERVICE_ACCOUNT \
--repo Stig-Johnny/NEW_REPO \
--body "github-actions@invotek-github-infra.iam.gserviceaccount.com"
gh secret set GCP_BILLING_ACCOUNT \
--repo Stig-Johnny/NEW_REPO \
--body "015BBC-422A59-EB7AF4"
Security
- No long-lived secrets -- WIF uses short-lived tokens (1 hour)
- Scoped to owner -- Only repos under
Stig-Johnny can authenticate
- Immutable identifiers -- Uses
repository_id (survives renames)
- Audit logging -- All authentications logged in Cloud Audit Logs
Troubleshooting
| Error |
Solution |
| "Unable to generate access token" |
Verify repo is under Stig-Johnny; check secrets; ensure permissions: id-token: write |
| "Permission denied on billing" |
SA can only link projects, not manage billing settings |
| "Firebase project not found" |
Grant roles/firebase.admin on the target project |
| "Cannot create projects without parent" |
Create projects manually with user credentials, then grant SA Firebase admin |