Skip to content

feat(sts): implement AssumeRoot, GetDelegatedAccessToken, GetWebIdentityToken#698

Merged
vieiralucas merged 4 commits intomainfrom
worktree-worktree-sts-3ops
Apr 23, 2026
Merged

feat(sts): implement AssumeRoot, GetDelegatedAccessToken, GetWebIdentityToken#698
vieiralucas merged 4 commits intomainfrom
worktree-worktree-sts-3ops

Conversation

@vieiralucas
Copy link
Copy Markdown
Member

@vieiralucas vieiralucas commented Apr 23, 2026

Summary

  • AssumeRoot mints short-lived (<=900s) member-account-root credentials, persists them in the IAM state so subsequent SigV4 lookups resolve to the new principal.
  • GetDelegatedAccessToken trades any non-empty TradeInToken for fresh STS credentials tied to the caller (or account root when unauthenticated).
  • GetWebIdentityToken returns an unsigned JWT (iss/sub/aud/iat/exp claims) so client decoders see standard structure; SigningAlgorithm validated against RS256/ES384.
  • STS conformance: 8/11 -> 11/11 (100%); workspace baseline +94 passing variants.

Test plan

  • cargo test -p fakecloud-conformance --test sts -> 11 pass
  • cargo run -p fakecloud-conformance -- run --services sts -> 11/11 OK
  • cargo run -p fakecloud-conformance -- audit / check -> PASS
  • cargo clippy --workspace --all-targets -- -D warnings
  • cargo fmt

Summary by cubic

Adds three STS actions—AssumeRoot, GetDelegatedAccessToken, and GetWebIdentityToken—to mint short‑lived credentials and issue a web identity token. Completes STS conformance (11/11, 438/438), updates the conformance baseline, and adds unit tests for happy paths and validation errors.

  • New Features

    • AssumeRoot: mints ≤900s credentials for a member account’s root; persists in IAM; validates TaskPolicyArn and DurationSeconds.
    • GetDelegatedAccessToken: trades a non-empty TradeInToken for temporary credentials bound to the caller (or account root if unauthenticated).
    • GetWebIdentityToken: returns an unsigned JWT with iss/sub/aud/iat/exp; validates SigningAlgorithm (RS256/ES384) and DurationSeconds (60–3600).
  • Bug Fixes

    • Treat GetWebIdentityToken as read-only to avoid unnecessary snapshot writes.
    • Parse DurationSeconds strictly and return ValidationError on non-numeric input.

Written for commit b59a43c. Summary will update on new commits.

…ityToken

- AssumeRoot mints short-lived (max 900s) credentials targeting a member
  account root; persists them so subsequent calls under the new key
  resolve to the target account's root identity.
- GetDelegatedAccessToken trades any non-empty token for a fresh STS
  credential bound to the caller's principal (or account root when
  unauthenticated).
- GetWebIdentityToken issues an unsigned JWT with iss/sub/aud/iat/exp
  claims so client decoders see standard structure; alg defaults to
  RS256/ES384 per the model.

Closes the STS conformance gap to 11/11 (100%).
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 23, 2026

Codecov Report

❌ Patch coverage is 91.06145% with 32 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
crates/fakecloud-iam/src/sts_service.rs 90.24% 32 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 4 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="crates/fakecloud-iam/src/sts_service.rs">

<violation number="1" location="crates/fakecloud-iam/src/sts_service.rs:100">
P2: `GetWebIdentityToken` is read-only (it acquires `self.state.read()` and never inserts credentials) but is listed in `is_mutating_action`, causing a needless `save_iam_snapshot` on every successful call. Remove it from the mutating list.</violation>

<violation number="2" location="crates/fakecloud-iam/src/sts_service.rs:1141">
P2: Non-numeric `DurationSeconds` is silently swallowed (`.ok()` + `unwrap_or(300)`) instead of returning a `ValidationError`. The other STS methods (`assume_root`, `get_session_token`) reject invalid integers explicitly. Parse and validate before defaulting.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread crates/fakecloud-iam/src/sts_service.rs Outdated
Comment thread crates/fakecloud-iam/src/sts_service.rs Outdated
Cubic flagged two issues on PR #698:
- GetWebIdentityToken is read-only: it reads state via .read() and never
  inserts credentials. Listing it under is_mutating_action triggered a
  needless snapshot save on every call. Removed.
- DurationSeconds was silently swallowed when non-numeric (.ok() +
  unwrap_or(300)). Match the pattern used by assume_root /
  get_session_token: parse explicitly and return ValidationError on
  bad input.
…ntity_token

Adds unit tests around: AssumeRoot via account-id and ARN forms +
SourceIdentity rendering, missing TaskPolicyArn, malformed principal,
duration > 900; GetDelegatedAccessToken happy + missing token;
GetWebIdentityToken happy path, missing audience / signing, unknown
algorithm, non-numeric and out-of-range duration. Lifts patch coverage
on the new STS handlers.
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="crates/fakecloud-iam/src/sts_service.rs">

<violation number="1" location="crates/fakecloud-iam/src/sts_service.rs:1793">
P3: The JWT structure assertion is ineffective: checking for any `.` in the full XML body can pass even when `WebIdentityToken` is not a valid JWT.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

let body = String::from_utf8(resp.body.expect_bytes().to_vec()).unwrap();
assert!(body.contains("WebIdentityToken"), "{body}");
// JWT structure: header.payload.signature (signature blank).
assert!(body.contains("."), "{body}");
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The JWT structure assertion is ineffective: checking for any . in the full XML body can pass even when WebIdentityToken is not a valid JWT.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At crates/fakecloud-iam/src/sts_service.rs, line 1793:

<comment>The JWT structure assertion is ineffective: checking for any `.` in the full XML body can pass even when `WebIdentityToken` is not a valid JWT.</comment>

<file context>
@@ -1669,4 +1669,185 @@ mod tests {
+        let body = String::from_utf8(resp.body.expect_bytes().to_vec()).unwrap();
+        assert!(body.contains("WebIdentityToken"), "{body}");
+        // JWT structure: header.payload.signature (signature blank).
+        assert!(body.contains("."), "{body}");
+    }
+
</file context>
Suggested change
assert!(body.contains("."), "{body}");
let token = body
.split("<WebIdentityToken>")
.nth(1)
.and_then(|s| s.split("</WebIdentityToken>").next())
.unwrap_or("");
assert_eq!(token.matches('.').count(), 2, "{body}");
Fix with Cubic

…-3ops

# Conflicts:
#	conformance-baseline.json
@vieiralucas vieiralucas merged commit 3220724 into main Apr 23, 2026
48 checks passed
@vieiralucas vieiralucas deleted the worktree-worktree-sts-3ops branch April 23, 2026 04:39
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