On demand credentials - Secretless AI assistant example on GCP
Riptides reimagines workload identity by embedding it directly in the Linux kernel, eliminating the need for sidecars, proxies, or application-level authentication logic. Built on SPIFFE standards, it enables per-process cryptographic identities that are portable, secure, and seamlessly federated across external systems without embedding secrets. Earlier posts detailed how SPIFFE Verifiable Identity Documents (SVIDs) are issued and enforced at the OS layer, providing strong runtime-scoped identities. Building on this, we introduced a credential injection demo, where AWS credentials were provisioned dynamically and just-in-time into outgoing requests—allowing workloads to securely access services like Amazon Bedrock without relying on static keys or tokens, addressing the limitations of cloud-native federation alone.
In this post, we take another step towards a world without stored credentials by exploring a demo application running on Google Cloud Platform that uses service account impersonation, credential files, and traditional access tokens to securely access GCP services. Through this example, we show how Riptides can provide the credential files and access tokens—two of Google Cloud’s standard authentication methods—and deliver them directly to managed workloads, all without storing a single secret.
Our demo app is a compact chat assistant that showcases how a conversational UI can translate between geographic coordinates and postal addresses using a tool-enabled AI model hosted by Google, paired with a Geocoding API integration for retrieval-augmented lookups. The code repository contains a minimal implementation on top of assistant-ui and can be found here.
Let's try to run the app!
Run the app the standard way
This demo requires two types of credentials: a credential file for the Vercel AI SDK for Google Vertex AI, which calls the LLM, and an access token for the tool that queries the Geocoding API. Both are referenced in app/api/chat/route.ts
at L41
, L78
and L126
and are sourced from environment variables via the .env
file. Let's generate these credentials first.
Setting up the GCP environment
To get our demo app running, we need a service account with the right permissions. Specifically:
roles/aiplatform.user
— required for calling Vertex AIroles/serviceusage.serviceUsageConsumer
— required for using the Geocoding API
1. Create a service account
gcloud iam service-accounts create gcp-demo-svca
2. Assign the roles
Grant the service account the necessary permissions:
gcloud projects add-iam-policy-binding <PROJECT_ID> \
--member="serviceAccount:gcp-demo-svca@<PROJECT_ID>.iam.gserviceaccount.com" \
--role="roles/aiplatform.user"
gcloud projects add-iam-policy-binding <PROJECT_ID> \
--member="serviceAccount:gcp-demo-svca@<PROJECT_ID>.iam.gserviceaccount.com" \
--role="roles/serviceusage.serviceUsageConsumer"
3. Authenticate with the service account
Next, download a JSON key for the service account and authenticate locally:
gcloud auth login --cred-file=<PATH_TO_KEY_FILE>
Then retrieve an access token for the app:
gcloud auth print-access-token
Running the demo app
With authentication in place, set the environment variables in the project’s .env
file:
GOOGLE_CREDENTIALS_PATH=<PATH_TO_KEY_FILE>
GOOGLE_MAPS_ACCESS_TOKEN=<ACCESS_TOKEN>
GOOGLE_PROJECT_ID=<GCP_PROJECT_ID>
GOOGLE_PROJECT_REGION=<GCP_PROJECT_REGION>
Start the application, and you’ll be able to query either coordinates for an address or an address for coordinates.

At this point, the application is running successfully with the credentials we configured. But this setup raises an important question: is storing and injecting service account keys this is really the best approach??
The dark side of static credentials
Static credentials and hardcoded service account keys may seem convenient, but they create brittle architectures, widen the blast radius of incidents, and work directly against zero-trust principles in modern cloud environments. Their key drawbacks are:
- Credential sprawl and copy-paste risk: Keys proliferate across repos, laptops, and pipelines, making it difficult to inventory and revoke all copies after an incident or team change.
- Long-lived secrets increase blast radius: A single leaked key enables persistent access until rotation, which is often slow and error-prone in multi-environment deployments.
- Weak auditability and provenance: Access is attributed to a shared secret rather than a verifiable workload identity, hindering forensics and fine-grained policy enforcement.
- Operational drag from rotation: Rotating static keys requires coordinated updates across services and environments, inviting downtime and configuration drift.
- Violates least-privilege over time: Static keys tend to accumulate permissions and linger beyond their original purpose, especially when reused across pipelines or services.
How does Riptides solve these problems?
- Riptides issues per‑workload identities and enforces them at the kernel layer, eliminating the need to copy API keys into code, repos, or pipelines, which directly curbs proliferation across laptops and CI/CD systems.
- Workloads authenticate with short‑lived, automatically rotated identities rather than static keys, sharply limiting the usable window of any intercepted material and aligning with zero‑trust design.
- Identities are auto‑issued and rotated without coordinated app changes, avoiding mass redeployments or secret sync work across environments and pipelines.
- Fine‑grained, SPIFFE‑native workload identities and policies prevent the “one key used everywhere” pattern; privileges are scoped per workload and enforced continuously rather than accumulating over time.
OK. But how can SPIFFE-native identities, issued at the kernel level, be used on platforms that don’t yet support them natively, such as Google Cloud? This is where the Riptides Identity Federation feature comes in. With this, it can translate SPIFFE identities into an IDP token with its OIDC provider and exchange this ID token for a credential that cloud providers understand, enabling secure workload authentication without relying on static credentials.
Preparing GCP for Identity Federation
To integrate SPIFFE identities into GCP, we need to set up Workload Identity Federation. This involves three steps:
1. Create a Workload Identity Pool
gcloud iam workload-identity-pools create demo-pool \
--project=<GCP_PROJECT_ID> \
--location="global" \
--display-name="Demo Pool"
2. Define a Workload Identity Provider
gcloud iam workload-identity-pools providers create-oidc demo-provider \
--project=<PROJECT_ID> \
--location="global" \
--workload-identity-pool="demo-pool" \
--display-name="Demo Provider" \
--issuer-uri=https://lilia-consolidative-zayn.ngrok-free.app/oidc \ # Riptides IDP provider
--attribute-mapping="google.subject=assertion.sub"
3. Grant Service Account Impersonation Rights
gcloud iam service-accounts add-iam-policy-binding gcp-demo-svca@<PROJECT_ID>.iam.gserviceaccount.com \
--project=<PROJECT_ID> \
--role="roles/iam.workloadIdentityUser" \
--member="principal://iam.googleapis.com/projects/<PROJECT_NUMBER>/locations/global/workloadIdentityPools/demo-pool/subject/spiffe://acme.corp/gcp-demo/reader"
In this step, we define which ID tokens, based on their subject, are authorized to assume the specified service account role. For example, tokens with the subject spiffe://acme.corp/gcp-demo/reader
may be exchanged for credentials that impersonate the gcp-demo-svca
service account.
How does it work?
The Riptides Controlplane can act as an identity provider, issuing OIDC tokens for workloads under its management. Thanks to the configuration above, GCP will trust these tokens for a specific workload identity (spiffe://acme.corp/gcp-demo/reader
). Once trusted, GCP exchanges the Riptides-issued ID token for an access token that impersonates a chosen service account.
Instead of dealing with downloaded key files and their lifecycle risks, you now get a credential file tied to workload identity federation—making authentication more secure, dynamic, and aligned with zero-trust principles.
Define Riptides resources
Riptides requires four resources to enable a fully secretless outbound call path to Google APIs.
Define a CredentialSource
first
apiVersion: core.riptides.io/v1alpha1
kind: CredentialSource
metadata:
name: gcp-demo-acc-cs
namespace: riptides-systemspec:
gcp:
serviceAccount: "gcp-demo@<PROJECT_ID>.gserviceaccount.com" # the service account to impersonate
oidcProviderId: "//iam.googleapis.com/projects/<PROJECT_NUMBER>/locations/global/workloadIdentityPools/demo-pool/providers/demo-provider" # the provider id that was defined earlier on GCP
lifetime: "3600s"
The CredentialSource specifies how Riptides acquires short‑lived credentials from Google Cloud using Workload Identity Federation and service account impersonation, including the target OIDC provider and the service account to impersonate after token exchange.
Note: Instead of service account impersonation, GCP provides the option to use direct resource access. In this case serviceAccount
field should not be set and access should be granted forprincipal://iam.googleapis.com/projects/<PROJECT_NUMBER>/locations/global
in the service we would like to access.
/workloadIdentityPools/demo-pool/subject/spiffe://acme.corp/gcp-demo/reader
Define a Service
for the Geocoding API
apiVersion: core.riptides.io/v1alpha1
kind: Service
metadata:
name: gcp-geocode-svc
namespace: riptides-system
spec:
addresses:
- address: geocode.googleapis.com # hostname that Riptides take care of
port: 443
tags: ["geocode", "gcp"]
labels:
api: geocode # selector that can be used later to point out this service
external: true # external says this service is outside Riptides-managed environment
The Service resource instructs Riptides to observe and, when configured, interpose on TLS connections targeting geocode.googleapis.com
so that policy-driven credential injection can occur on egress.
Define aWorkloadIdentity
for the demo app
apiVersion: core.riptides.io/v1alpha1
kind: WorkloadIdentity
metadata:
name: gcp-demo-reader
namespace: riptides-system
spec:
selectors:
- process:cmdline: "next-server (v15.5.2)"
workloadID: gcp-demo/reader # this name appears in SPIFFE id
scope:
agent:
id: <AGENT_WORKLOAD_ID> # on which nodes this identity is defined and applied
connection:
tls:
mode: PERMISSIVE # needed to let http connections to connect to the UI interface
egress:
- selectors:
- api: geocode # it defines that sercices with this selector should be intercepted
credentialName: gcp-demo-acc-cs-gcp-demo-reader-wc # this credential should be injected on the wire
connection:
tls:
intercept: true # it tells injection can happen even if the connection to interrupt is TLS
WorkloadIdentity declares which running processes are recognized as the gcp-demo/reader workload and binds them to a SPIFFE-style identity (for example, spiffe://acme.corp/gcp-demo/reader) to be used as the subject in issued tokens and policies, aligning with Google’s impersonation subject expectations. The egress rule directs Riptides to inject the designated GCP credential on connections matching the geocode selector, enabling transparent, per-connection token delivery to geocode.googleapis.com
.
Because the Service above carries the same selector, every outbound connection to geocode.googleapis.com
will receive a short‑lived GCP access token automatically at connection time.
Define a WorkloadCredential
apiVersion: core.riptides.io/v1alpha1
kind: WorkloadCredential
metadata:
name: gcp-demo-acc-cs-gcp-demo-reader-wc
namespace: riptides-system
spec:
workloadID: gcp-demo/reader # name of the workload identity we would like to be granted to reach the credential source
credentialSource: gcp-demo-acc-cs # name of the credential source
WorkloadCredential binds the CredentialSource to the WorkloadIdentity and exposes ephemeral credential artifacts via sysfs for that workload, including a JSON file path that client libraries can reference for on-demand authentication. The path is built from a unique hash of the WorkloadIdentity's ID and the WorkloadCredential's name.
...
status:
paths:
- /sys/kernel/riptides/credentials/efa8626c-472c-5ac0-86d9-04a445c56c2b/gcp-demo-acc-cs-gcp-demo-reader-wc/gcp_credentials.json
...
Only processes authenticated as gcp-demo/reader
can read the sysfs-backed credential file, enforcing least privilege at the identity boundary rather than via static secrets.
Run the app secretless
In practice, the demo app obtains the gcp-demo/reader identity, which authorizes secure read access to the sysfs-projected credential file and enables Riptides to inject short‑lived access tokens (via impersonation) on egress to geocode.googleapis.com
without ever persisting credentials in the app or environment.
Set environment variables as follows:
GOOGLE_CREDENTIALS_PATH=/sys/kernel/riptides/credentials/efa8626c-472c-5ac0-86d9-04a445c56c2b/gcp-demo-acc-cs-gcp-demo-reader-wc/gcp_credentials.json
GOOGLE_MAPS_ACCESS_TOKEN=none
A placeholder value for GOOGLE_MAPS_ACCESS_TOKEN
is acceptable because the real token is issued just-in-time and injected at the connection layer, rotated automatically per policy and lifetime settings defined in the CredentialSource.
With this setup, the application can call the Geocoding API successfully, end-to-end, without storing or managing long‑lived secrets, relying instead on federated identity and short‑lived, policy-scoped tokens delivered at runtime.

Final thoughts
In this demo, Riptides delivered secretless authentication on GCP by enforcing SPIFFE identities at the kernel, federating them into Workload Identity Federation, and supplying two runtime paths: a federated credential file for Vertex AI and on‑the‑wire token injection to geocode.googleapis.com
— no downloaded keys, no app changes, no secret sprawl. The result is concrete risk reduction: static key elimination, automatic short‑lived impersonated tokens per connection, least‑privilege tied to a verifiable workload subject, and full auditability of who accessed what, when. This isn’t just an implementation detail, it’s a security philosophy — a mantra of no static secrets, no blind trust, only SPIFFE based verifiable identities at runtime.
Ready to replace secrets
with trusted identities?
Build with trust at the core.