You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 41caf90
Browse filesBrowse the repository at this point in the historyBrowse files
feat(credentials): keyless Google Cloud credentials via Workload Identity Federation
A keyless mode of the existing Google service account credential
(google-service-account) for Google Cloud APIs. JSON-key credentials, their
connect flow, and their token minting are unchanged.
- Flow: Sim signs a short-lived token for the credential's own subject with
aud = the customer's provider resource name
(https://iam.googleapis.com/projects/<number>/locations/global/
workloadIdentityPools/<pool>/providers/<provider>), exchanges it at
sts.googleapis.com/v1/token (token-exchange, jwt subject token,
cloud-platform scope, audience //iam.googleapis.com/...), then calls
iamcredentials generateAccessToken for the service account with the tool's
scopes and a 1h lifetime. Impersonation rather than direct federated access:
some Cloud APIs reject federated tokens, and the service account keeps the
same identity and roles a JSON key uses.
- Inputs validated against Google's documented formats before any request:
numeric project number, pool/provider IDs (4-32 [a-z0-9-], no gcp-), and a
user-managed *.iam.gserviceaccount.com email, so nothing can steer the STS
audience or IAM URL. Requests never follow redirects.
- Scope: refuses domain-wide delegation (an impersonated user) and any service
but BigQuery and Vertex AI before reading further or contacting Google, with
a new shared unsupported_use code and copy pointing to a JSON key. Workspace
blocks never offer the mode.
- Save check: STS exchange, impersonation, and tokeninfo (no IAM role or
enabled API needed), plus a probe that a subject no credential holds cannot
impersonate the service account. Subject bound to the saving user with the
signed proof; kept across reconnects.
- STS/IAM failures map to actionable copy (provider settings, issuer
reachability, the exact workloadIdentityUser binding) without Google's
descriptions or any token; runtime keeps the invalid_credentials /
provider_unavailable split. Tokens cache per credential, secret version, and
scopes in an LRU.
- Per-credential audiences: audiences.ts gains google without a fixed
audience; the setup response omits audience for it and reports a keyless
reconnect. Connect dialog shows the issuer, audience, subject, and filled-in
gcloud commands (pool, OIDC provider with attribute condition on the
subject, IAM binding); docs section added.
Copy file name to clipboardExpand all lines: apps/docs/content/docs/integrations/google-service-account.mdx
+63Lines changed: 63 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -11,6 +11,8 @@ Google service accounts can access shared resources directly or impersonate a Go
11
11
12
12
For example, you could build a workflow that iterates through a list of employees, impersonates each one to read their Google Docs, and uploads the contents to a shared knowledge base — all without requiring any of those users to sign in.
13
13
14
+
For Google Cloud APIs such as BigQuery and Vertex AI, you can skip the JSON key entirely: see [Keyless Google Cloud access](#keyless-google-cloud-access).
15
+
14
16
## Prerequisites
15
17
16
18
Create the service account and enable its APIs in Google Cloud. Configure the Google Workspace Admin Console only when using domain-wide delegation.
@@ -172,3 +174,64 @@ Leave **Impersonated Account** blank to authenticate as the service account itse
172
174
Domain-wide delegation requires a Google Workspace super administrator. It authorizes impersonation within that Workspace domain, subject to the authorized scopes and the impersonated user’s access. Consumer Gmail accounts and users outside that domain cannot be impersonated. Access also depends on the license required by the target service. See [Google’s service-account authorization guide](https://developers.google.com/identity/protocols/oauth2/service-account).
173
175
174
176
A credential can serve multiple supported Google services when their APIs and scopes are configured. To rotate its key, create a replacement JSON key, update the credential in Sim, verify access, then delete the old key in Google Cloud.
177
+
178
+
## Keyless Google Cloud access [#keyless-google-cloud-access]
179
+
180
+
A Google service account credential can also be **keyless**: Sim stores no key. Instead, your Google Cloud project trusts Sim through [Workload Identity Federation](https://cloud.google.com/iam/docs/workload-identity-federation-with-other-providers). On each run, Sim signs a short-lived token for this credential's own subject, exchanges it at Google's Security Token Service for a federated token, and uses that to impersonate your service account. The service account's IAM roles decide what workflows can do, exactly as with a JSON key.
181
+
182
+
<Callouttype="info">
183
+
Keyless credentials work with Google Cloud APIs that the service account calls as itself: **BigQuery**, and **Vertex AI** wherever a Google service account credential is accepted. They can't use domain-wide delegation, so Gmail, Drive, Sheets, Calendar, and other Google Workspace blocks don't offer them, and a keyless credential refuses an **Impersonated Account**. Use a JSON key for those.
184
+
</Callout>
185
+
186
+
<Steps>
187
+
<Step>
188
+
In the [Google Cloud Console](https://console.cloud.google.com/), enable the IAM, Resource Manager, Service Account Credentials, and Security Token Service APIs in the project that will hold the workload identity pool, and note its **project number** (on the project's Dashboard; it's numeric, not the project ID).
189
+
</Step>
190
+
<Step>
191
+
Create or pick a service account, and grant it the roles your workflows need (for example, BigQuery Data Viewer and BigQuery Job User, or Vertex AI User). No key is needed.
192
+
</Step>
193
+
<Step>
194
+
In Sim, open the **Google BigQuery** integration, choose **Add service account**, and set **Authentication** to **Workload Identity Federation (keyless, via Sim OIDC)**. Enter the project number, a pool ID and provider ID (4 to 32 lowercase letters, digits, or hyphens, not starting with `gcp-`), and the service account email (`name@project-id.iam.gserviceaccount.com`).
195
+
</Step>
196
+
<Step>
197
+
The dialog shows the **Issuer**, **Audience**, and **Subject**, and three `gcloud` commands with your values filled in. Run them in that project. They create the pool, an OIDC provider for Sim's issuer with the attribute mapping `google.subject=assertion.sub` and an attribute condition that accepts only this credential's subject, and grant that subject `roles/iam.workloadIdentityUser` on the service account:
198
+
199
+
```bash
200
+
gcloud iam workload-identity-pools create POOL_ID \
201
+
--location="global" \
202
+
--display-name="Sim"
203
+
204
+
gcloud iam workload-identity-pools providers create-oidc PROVIDER_ID \
The provider sets no allowed audiences, so it accepts its own resource name, which is the **Audience** the dialog shows.
217
+
</Step>
218
+
<Step>
219
+
Save in Sim. Sim exchanges a token at STS, impersonates the service account, and confirms Google accepts the resulting token. It also checks that a subject no credential holds can't impersonate the service account, so only this credential can use it. New IAM bindings can take a few minutes to apply; if saving fails right after you run the commands, wait and try again.
220
+
</Step>
221
+
</Steps>
222
+
223
+
<Callouttype="warn">
224
+
Keep the attribute condition and bind the role to the exact `principal://…/subject/SUBJECT` member. Without the condition, the provider accepts a token for any Sim subject, and a role granted to a `principalSet` for the whole pool would let other Sim credentials act with it. Sim refuses to save a service account that another subject can impersonate, but it can't see roles you grant the pool directly on other resources.
225
+
</Callout>
226
+
227
+
Each keyless credential has its own subject; reconnecting keeps it by default so your provider and binding keep working. Use one provider per credential (a pool can hold many). Keyless credentials are available for workspace credentials, not organization credentials. Sim uses impersonation rather than direct federated access because some Google Cloud APIs don't accept federated tokens, and impersonation keeps the same identity a JSON key would use.
228
+
229
+
Self-hosted deployments need `WORKLOAD_IDENTITY_SIGNING_KEY` set and an `https` issuer Google can reach over the public internet. See [AWS credentials](/integrations/aws-credentials#self-hosted-deployments).
230
+
231
+
### Troubleshooting keyless access
232
+
233
+
-**"Google Cloud refused Sim's token for this workload identity provider":** the project number, pool ID, or provider ID doesn't match; the provider is disabled, has allowed audiences set, or uses a different issuer; or its attribute condition doesn't allow this credential's subject.
234
+
-**"Google Cloud could not fetch the signing keys from Sim's issuer":** the issuer isn't reachable over public HTTPS (self-hosted deployments).
235
+
-**"The service account … does not let this credential impersonate it":** run the `add-iam-policy-binding` command with the exact member shown, and enable the IAM Service Account Credentials API.
236
+
-**"This service account also trusts other Sim credentials":** remove any `principalSet` binding for the pool from the service account and keep the attribute condition.
237
+
-**A block fails with "This credential cannot be used here":** the block is a Google Workspace block or sets an **Impersonated Account**. Use a JSON key credential for it.
Copy file name to clipboardExpand all lines: apps/sim/app/workspace/[workspaceId]/integrations/components/connect-service-account-modal/aws-account-modal.tsx
Copy file name to clipboardExpand all lines: apps/sim/app/workspace/[workspaceId]/integrations/components/connect-service-account-modal/connect-service-account-modal.tsx
@@ -725,39 +780,84 @@ function GoogleServiceAccountModalForm({
725
780
Add {serviceName} service account
726
781
</ChipModalHeader>
727
782
<ChipModalBody>
728
-
<ChipModalField
729
-
type='textarea'
730
-
title='JSON key'
731
-
value={jsonInput}
732
-
onChange={(value)=>{
733
-
setJsonInput(value)
734
-
if(uploadedFileName)setUploadedFileName(null)
735
-
if(error)setError(null)
736
-
maybeFillDisplayNameFromJson(value)
737
-
}}
738
-
placeholder='Paste your service account JSON key here'
739
-
minHeight={120}
740
-
required
741
-
/>
783
+
{offerKeyless&&(
784
+
<ChipModalField
785
+
type='dropdown'
786
+
title='Authentication'
787
+
value={authType}
788
+
onChange={(value)=>{
789
+
setSelectedAuthType(valueasGoogleAuthType)
790
+
setError(null)
791
+
keylessSubmit.clearError()
792
+
}}
793
+
options={[
794
+
{value: 'json_key',label: 'JSON key'},
795
+
{
796
+
value: 'workload_identity',
797
+
label: 'Workload Identity Federation (keyless, via Sim OIDC)',
798
+
},
799
+
]}
800
+
required
801
+
hint={
802
+
authType==='workload_identity'
803
+
? 'No key is stored. Sim signs a short-lived token your workload identity provider trusts, then impersonates the service account. Google Cloud APIs only, without domain-wide delegation.'
804
+
: undefined
805
+
}
806
+
/>
807
+
)}
742
808
743
-
<ChipModalField
744
-
type='file'
745
-
title='Or upload a file'
746
-
accept='.json'
747
-
label={
748
-
uploadedFileName
749
-
? `Uploaded ${uploadedFileName} — click or drop to replace`
750
-
: 'Drag & drop a .json file, or click to browse'
751
-
}
752
-
onChange={handleFileUpload}
753
-
/>
809
+
{authType==='workload_identity'&&(
810
+
<GoogleKeylessFields
811
+
input={keylessInput}
812
+
onChange={(next)=>{
813
+
setKeylessInput(next)
814
+
keylessSubmit.clearError()
815
+
}}
816
+
setup={keyless}
817
+
/>
818
+
)}
819
+
820
+
{authType==='json_key'&&(
821
+
<>
822
+
<ChipModalField
823
+
type='textarea'
824
+
title='JSON key'
825
+
value={jsonInput}
826
+
onChange={(value)=>{
827
+
setJsonInput(value)
828
+
if(uploadedFileName)setUploadedFileName(null)
829
+
if(error)setError(null)
830
+
maybeFillDisplayNameFromJson(value)
831
+
}}
832
+
placeholder='Paste your service account JSON key here'
833
+
minHeight={120}
834
+
required
835
+
/>
836
+
837
+
<ChipModalField
838
+
type='file'
839
+
title='Or upload a file'
840
+
accept='.json'
841
+
label={
842
+
uploadedFileName
843
+
? `Uploaded ${uploadedFileName} — click or drop to replace`
844
+
: 'Drag & drop a .json file, or click to browse'
845
+
}
846
+
onChange={handleFileUpload}
847
+
/>
848
+
</>
849
+
)}
754
850
755
851
<ChipModalField
756
852
type='input'
757
853
title='Display name'
758
854
value={displayName}
759
855
onChange={setDisplayName}
760
-
placeholder='Auto-populated from client_email'
856
+
placeholder={
857
+
authType==='workload_identity'
858
+
? 'Defaults to the service account email'
859
+
: 'Auto-populated from client_email'
860
+
}
761
861
autoComplete='off'
762
862
/>
763
863
@@ -771,7 +871,7 @@ function GoogleServiceAccountModalForm({
771
871
minHeight={80}
772
872
/>
773
873
774
-
<ChipModalError>{error}</ChipModalError>
874
+
<ChipModalError>{submitError}</ChipModalError>
775
875
</ChipModalBody>
776
876
<ChipModalFooter
777
877
onCancel={()=>onOpenChange(false)}
@@ -782,8 +882,24 @@ function GoogleServiceAccountModalForm({
782
882
},
783
883
]}
784
884
primaryAction={{
785
-
label: isPending ? 'Adding...' : 'Add service account',
Copy file name to clipboardExpand all lines: apps/sim/app/workspace/[workspaceId]/integrations/components/connect-service-account-modal/databricks-account-modal.tsx
+1-1Lines changed: 1 addition & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -122,7 +122,7 @@ function DatabricksAccountForm({
0 commit comments