Passwordless everywhere¶
The first deep page in the trust arc. Two sides — app → services and app → database — with the same load-bearing rule: fetch a fresh token on each connect; never cache a connection string. The surface intro named the pattern; this page proves the wire.
The one rule¶
Zero secrets in your app. Every outbound call is authenticated with a token minted just before use by the platform on behalf of the workload identity. If the words "database password" or "long-lived key" appear in your config, you've dropped out of passwordless mode.
The secrets anti-pattern¶
DATABASE_URL="postgresql://admin:my_password_never_rotates@…" # ← THIS
API_KEY="sk-a1b2c3d4e5f6…" # ← THIS
Every one of these:
- must be created, stored, rotated, revoked by someone — usually a person with a spreadsheet;
- outlives the workload that uses it (still valid after the pod dies, after the operator quits);
- fans out — every leak surface (build logs, CI vars,
.envin a git commit, exception traces) becomes a compromise vector; - can't be tied to who is running the code — only to what has the string.
The passwordless model replaces every one of those strings with a short-lived, workload-scoped, platform-minted token. The platform is the credential authority; your code never sees a static key.
The two halves¶
- App → services — how the workload asks the platform for an access token for a cloud service (Key Vault, S3, Vertex AI, whatever). IMDS / metadata server / IRSA. Token lifetimes, refresh, the "cache in the SDK, not in your code" rule.
- App → database — the same workload identity, one hop further: fetch a fresh DB token on every connect, hand it to
psycopgas the password, let the connection pool churn it. Azure Entra token for Postgres (audiencehttps://ossrdbms-aad.database.windows.net/.default), Cloud SQL IAM auth via the Python Connector, RDSgenerate_db_auth_token().
Docs verified 2026-08-08¶
- Azure IMDS + Managed Identity token acquisition: learn.microsoft.com/…/managed-identities-azure-resources/how-to-use-vm-token
- Azure Postgres Flexible Server Entra auth: learn.microsoft.com/…/postgresql/flexible-server/how-to-configure-sign-in-azure-ad-authentication
- GCP Cloud SQL IAM authentication (Python Connector): cloud.google.com/sql/docs/postgres/connect-connectors
- GCP metadata server: cloud.google.com/compute/docs/metadata/overview
- AWS IMDSv2 (EC2) / IRSA (EKS): docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html
- AWS RDS IAM auth (Python): docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.Connecting.Python.html
Token-refresh vs pooling — the load-bearing subtlety¶
A traditional connection pool keeps a bag of live TCP connections, each authenticated once at open time. If your DB token expires at 15 minutes but a pooled connection stays open for 12 hours, the connection still works — the token was consumed on the initial handshake; the DB doesn't re-check it on subsequent queries. Different clouds have different rules here:
| Provider | Token used for | Re-validation after connect? | Practical rule |
|---|---|---|---|
| Azure Entra → Postgres | Initial libpq authentication | No — connection lives as long as it's open | Cap pool_recycle at token lifetime (55 min for a 60-min token) so a pool never carries a session past the token's practical trust horizon. |
| Cloud SQL Connector | Connector refreshes the underlying IAM token in the background before the ephemeral cert expires | Managed by the connector | Use the connector as documented; do not hand-roll a token loop around it. |
RDS IAM generate_db_auth_token() |
Initial libpq authentication | No — 15-min token used once, connection lives on | Cap pool_recycle at 14 min (below the 15-min token TTL) so every new physical connection uses a fresh token. |
Two failure modes to design against:
- Cached connection strings. If your app stores
postgresql://user:<token>@host/dbanywhere (an env var, a config object, a wrapper class), it dies the first time that token expires. Store the ingredients (workload identity, hostname, user, DB name), regenerate the password on demand. SQLAlchemyengines with defaultpool_recycle=-1. Same problem — a physical connection lives forever, but its auth token doesn't. Setpool_recycleexplicitly, per the table above.
The examples show the "fetch fresh on each connect" pattern with psycopg using a password callable — a per-connection token acquisition function passed to the connector so every physical connect goes through a fresh mint.
What passwordless does NOT solve¶
- Authorization. Passwordless proves what identity is calling — it says nothing about whether that identity is allowed. Grant narrow IAM/DB roles alongside; passwordless is the credential story, not the policy one.
- Client-supplied secrets (API keys the end user brings). Passwordless is about the workload's outbound calls. What your users pass in over your API is a separate design problem — see Guardrails + Trust — attributed writes.
- Cross-account trust. Workload identity federation across accounts adds its own layer (Entra guest tenants, GCP Workload Identity Federation from AWS, AWS
sts:AssumeRolebetween accounts). Same pattern, more setup — a deeper page in this arc covers it.
Where the arc goes next¶
- App → services — how to get a token for a cloud service, per provider.
- App → database — the same identity, but landing as a DB password with the right refresh rule.
- Later in the arc — cross-account/tenant federation, and RBAC/RLS composed on top of these two halves.