AWS Identity

How the Workers authenticate to FloatMe’s service APIs on AWS, and the change that replaces the static IAM user keys with a role assumed through Auth0. The AWS and Auth0 halves live in the infrastructure repo; its docs page "Cloudflare Workers identity" is the design record. This page is the Worker-side plan. Nothing here is implemented yet.

Today

The auth Worker signs requests to user-service, the transactions service, floatmetrics, and the QA service with SigV4 through aws4fetch, using FLOATME_AWS_ACCESS_KEY_ID and FLOATME_AWS_SECRET_ACCESS_KEY from Worker secrets. Two call sites build the client:

  • cloudflare/workers/auth/handlers/signup-v2.ts, createUserServiceUser

  • cloudflare/helpers/qa-helper.ts, the integration-user path, which hard-codes us-west-2

The keys belong to the test-cloudflare-workers and prod-cloudflare-workers IAM users, are minted by hand, arrive as GitHub secrets, and reach the Workers through each worker’s secrets.sh. They have never been rotated, and user-service’s user-creation handler accepts calls only from those user names.

Target

No AWS credential is stored anywhere. The Worker gets a client-credentials token from Auth0, exchanges it with STS for temporary credentials on a role, caches them, and signs as before.

Auth0 /oauth/token  (client_credentials, audience = AWS API identifier)
  -> access token, subject "<client_id>@clients"
STS AssumeRoleWithWebIdentity  (role ARN, that token, fixed session name)
  -> access key, secret, session token, ~1h
aws4fetch AwsClient with sessionToken

STS’s AssumeRoleWithWebIdentity is an unsigned Query API call, so no credential is needed to make it. The Auth0 client secret is the only secret, and rotating it is an Auth0 action.

Changes

New helper

cloudflare/helpers/aws-credentials.ts exporting one function that returns a ready AwsClient for a given region. Internally:

  1. Read cached credentials from KV under a key per environment. If they expire more than five minutes out, use them.

  2. Otherwise request an Auth0 token with grant_type=client_credentials against AUTH0_BASE_URL for AUTH0_AWS_AUDIENCE. Cache the token in KV for its lifetime, since Auth0 meters machine-to-machine tokens per month and every isolate starting cold would otherwise mint one.

  3. Call STS with RoleArn=FLOATME_AWS_ROLE_ARN, WebIdentityToken, and RoleSessionName=<worker name>. Parse the XML credentials.

  4. Write the credentials and expiry to KV, and return the client.

Both existing call sites switch to it. The qa-helper.ts region stays hard-coded until the QA service exists in more than one region.

Config

Per environment in wrangler.toml, non-secret:

  • FLOATME_AWS_ROLE_ARN, from the infrastructure root’s output

  • AUTH0_AWS_AUDIENCE, the API identifier created in the tenant

  • a kv_namespaces binding for the credential cache

Secrets, through secrets.sh and the deploy workflow:

  • AUTH0_AWS_CLIENT_ID and AUTH0_AWS_CLIENT_SECRET, the machine-to-machine application. Separate from the authentication and management clients so the token can do nothing but assume the role.

Env in cloudflare/helpers/env.ts gains those and, after cutover, loses FLOATME_AWS_ACCESS_KEY_ID and FLOATME_AWS_SECRET_ACCESS_KEY.

Staging environment

Staging is where this lands first, and the repo has no staging environment yet. It needs a [env.staging] stanza in each worker’s wrangler.toml with staging routes and the staging API hostnames, a staging block in deploy.yaml populating the same variables the test block does, and the secrets script run for it. Which event deploys staging follows the infrastructure repo’s release flow for staging.

Sequencing

  1. infrastructure: staging Auth0 tenant root creates the application and API; staging AWS root creates the OIDC provider and role.

  2. user-service: caller check becomes config-driven and matches on the role name.

  3. This repo: helper, config, staging environment, cutover of the two call sites.

  4. Verify a signup end to end in staging, then check CloudTrail shows the assume with the Auth0 subject and the API Gateway log shows the invoke.

  5. Prod follows the same steps once the prod tenant and root are ready. Then the static-key secrets and the IAM users are deleted.

Notes

  • Session credentials are one hour. The five-minute refresh margin means a Worker never presents credentials that expire mid-request.

  • If Auth0 is unavailable, cached credentials keep working until they expire. That is the new failure mode this design adds; the Workers already cannot log users in without Auth0, so it is not a new dependency, only a wider one.

  • The role session name is what shows in CloudTrail and in the ARN user-service sees. Keep it fixed per worker; user-service matches on the role name segment, not the session name.