Skip to content

feat(aws/sts): extract STS into a dedicated route and harden AssumeRole - #169

Open
andthezhang wants to merge 1 commit into
vercel-labs:mainfrom
andthezhang:feat/sts-dedicated-route
Open

feat(aws/sts): extract STS into a dedicated route and harden AssumeRole#169
andthezhang wants to merge 1 commit into
vercel-labs:mainfrom
andthezhang:feat/sts-dedicated-route

Conversation

@andthezhang

Copy link
Copy Markdown

Summary

Promotes the inline STS stub (previously buried in routes/iam.ts) into its own routes/sts.ts, and hardens it so the official AWS SDK / boto3 work against it.

What changed

  • AWS SDK wire-format dispatch. The SDK sends STS calls as POST / with a form-encoded body (not /sts/). This now dispatches STS only when the request is a urlencoded STS form (Action=AssumeRole|GetCallerIdentity), leaving S3's wildcard routes untouched. The legacy POST /sts/ path still works.
  • DurationSeconds support + validation. AssumeRole honors DurationSeconds (default 3600) and validates it against STS limits (900..43200), returning a ValidationError for out-of-range or non-integer values instead of silently clamping — matching real STS.
  • Stable identities. Mints ASIA-prefixed credentials and reuses the IAM store's role_id / user_id when the principal already exists.
  • Removes the now-duplicated STS code from routes/iam.ts.

Known limitations (intentional)

Consistent with LocalStack / Moto defaults:

  • Credentials are synthetic and not persisted — credentials returned by AssumeRole are not recognized on subsequent requests, and GetCallerIdentity reports the authenticated bearer user rather than an assumed-role principal.
  • SigV4 signatures are accepted but not verified (an emulator has no real secret to validate against).

These are noted so the auth model is explicit; closing them would require persisting issued credentials and parsing the SigV4 access key — out of scope here.

Tests

New sts.test.ts covers AssumeRole / GetCallerIdentity over both /sts/ and the SDK POST / path, DurationSeconds validation (too low / too high / non-integer / boundary), unknown-action handling, and that a non-STS POST / is not hijacked. pnpm --filter @emulators/aws test → all green; type-check clean. No new dependencies.

Moves the inline STS stub out of iam.ts into its own routes/sts.ts and
expands it for AWS SDK compatibility:

- Dispatches the AWS SDK wire format: STS targets `POST /` with a
  form-encoded body. We claim it only when the body is a urlencoded STS
  form (Action=AssumeRole|GetCallerIdentity), leaving S3 wildcards
  untouched. Legacy `POST /sts/` is still supported.
- AssumeRole honors DurationSeconds (default 3600) and validates it
  against STS limits (900..43200), returning a ValidationError for
  out-of-range or non-integer values rather than silently clamping.
- Mints ASIA-prefixed credentials and reuses the IAM store's role_id /
  user_id when the principal exists, for stable responses.

Known limitations (intentional, matching LocalStack/Moto): credentials
are synthetic and not persisted, so issued AssumeRole credentials are
not recognized on later requests, and GetCallerIdentity always reports
the authenticated bearer user. SigV4 signatures are accepted but not
verified.

Tests: dedicated sts.test.ts covering AssumeRole/GetCallerIdentity over
both `/sts/` and the SDK `POST /` path, DurationSeconds validation,
unknown-action handling, and that non-STS `POST /` is not hijacked.
@vercel

vercel Bot commented May 29, 2026

Copy link
Copy Markdown
Contributor

@andthezhang is attempting to deploy a commit to the Vercel Labs Team on Vercel.

A member of the Team first needs to authorize it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant