Understanding AWS Credential Federation — How IAM, STS, and OIDC Actually Fit Together

Understanding AWS Credential Federation — How IAM, STS, and OIDC Actually Fit Together


Introduction

AWS Private EC2 Operations Guide Part 4 introduces OIDC federation as the way to permanently delete AWS access keys from GitHub Actions. The recipe works if you follow it — but surprisingly few engineers can draw what’s actually happening underneath.

The four most common questions:

  • What is OIDC federation, exactly, and why is the word “federation” attached?
  • Why does STS have no standalone console page? Where do you actually configure it?
  • Why is the sub condition in the trust policy emphasized so heavily?
  • Where else does federation show up beyond GitHub Actions? (SAML, IAM Identity Center, EKS IRSA, Cognito…)

This post answers all four in one place. If Part 4 was about “how to apply this pattern to our environment,” this one is “what parts fit together underneath, and how”. By the end you can diagnose Part 4, EKS, cross-account AssumeRole, and corporate SSO with the same mental model.

The target reader is someone who has used AWS for a while but never built a foundational understanding of IAM, STS, and OIDC. You don’t need to have read the AWS Private EC2 series, but pairing this post with Part 4’s OIDC section gives you the abstraction and the implementation at the same time.


TL;DR

  • Every AWS credential flow ultimately funnels into STS. STS = Security Token Service, a stateless API that issues temporary credentials. EC2 instance profiles, OIDC, SAML, IAM Identity Center — all of them end with an STS call.
  • Long-lived keys (AKIA…) → short-lived keys (ASIA…) + SessionToken is the major direction in AWS credential security. Temporary keys expire on their own, and incident blast radius decays naturally with time.
  • “Federation” means AWS trusts the identity verification done by an external provider and issues temporary credentials based on that trust. OIDC, SAML, and Cognito are all variants of this pattern.
  • STS itself has nothing to configure. The lack of a standalone console page is not a bug — it’s by design. What people call “STS configuration” is entirely IAM work: identity providers, roles, and trust policies.
  • The sub condition in the trust policy is the lock on federation. Drop it and any external identity trusted by the same provider can assume your role — the number-one cause of OIDC incidents.

1. The Evolution of Credentials — Why Federation Exists

1.1 The starting point — IAM User + long-lived access key

Early AWS credential models were straightforward. You created an IAM User, generated an access key pair, and embedded it in your application. Keys were valid forever, and one key equaled one identity.

# Legacy approach
resource "aws_iam_user" "ci"        { name = "github-ci" }
resource "aws_iam_access_key" "ci"  { user = aws_iam_user.ci.name }
# AccessKeyId / SecretAccessKey stored in GitHub Secrets

Familiar but accumulates four pain points in production:

ProblemWhat it actually looks like
Unbounded validityA key pushed to git or caught in a screenshot is a permanent breach until manually revoked
Sharing is hardOne person’s key embedded in many places must be rotated everywhere simultaneously
Limited traceabilityCloudTrail only tells you “which IAM User called” — not which workflow / instance / session
No time-bounded permissions”Read-only most of the time, write only during deploy” can’t be expressed at the key level

1.2 One step forward — IAM Role

To address these pain points, AWS introduced IAM Roles. A Role is a “persona that holds permissions” — no long-lived key attached, and a Trust Policy defines who (which Principal) can borrow it temporarily.

flowchart LR
    User[IAM User<br/>or another Role] -->|"AssumeRole"| STS[AWS STS]
    STS -->|"Temporary credentials (1 hour)"| User
    User -->|"Calls with temp keys"| API[AWS API]

Three pivotal changes:

  • You can borrow a Role without long-lived keys — EC2 instance profiles are the canonical example.
  • Permissions are issued in time-bounded grants — automatic expiry after one hour, automatic rotation.
  • Calling context becomes rich — CloudTrail records who assumed which Role under which session name.

But Roles still have a limit. Calling AssumeRole still requires that someone has credentials to start with. EC2 has the instance profile (IMDS); a local machine has IAM User keys. So Roles only cover “flows that originate inside AWS.” What about identities outside AWS (GitHub Actions workflows, corporate SSO users, mobile app users)?

1.3 Federation — accepting identities from outside AWS

The answer is to have AWS trust an external Identity Provider (IdP). The IdP issues a signed proof saying “this user/workflow has been verified by us,” AWS receives the proof, and STS issues temporary credentials in return. This model is federation.

The mental model flips once here. Federation isn’t “register users in AWS” — it’s “trust an identity system outside AWS.” If 1.1’s IAM User approach means creating 100 IAM Users for 100 employees, federation lets AWS accept identities that already live in the company SSO (Okta, Azure AD), keeping just one IdP registration and a handful of Roles in IAM. AWS doesn’t know those 100 people as IAM Users — it only holds rules like “if a token signed by Okta with sub = X arrives, lend out this Role for one hour.”

Note: IAM Identity Center (formerly AWS SSO) looks like console login on the surface — there’s a browser sign-in page. But what happens behind it is federation. Users are not registered as IAM Users; STS mints fresh temporary credentials on every sign-in.

Federation typeIdP examplesAWS API
OIDCGitHub Actions, GitLab, EKS, Auth0AssumeRoleWithWebIdentity
SAML 2.0Okta, Azure AD, ADFS, Google WorkspaceAssumeRoleWithSAML
Cognito Identity PoolCognito User Pool, social loginGetCredentialsForIdentity
IAM Identity CenterAWS-managed directory or external IdPConsole / CLI handles automatically

The names vary, but the essence is identical:

  1. An external IdP verifies the user / workflow’s identity.
  2. It issues a signed token (JWT, SAML assertion, etc.).
  3. AWS STS validates the signature and conditions on that token.
  4. STS issues temporary credentials.

This flow is the standard for AWS credential operations in 2026. Long-lived keys are increasingly an anti-pattern.

1.4 Aside: OAuth 2.0 / OIDC / SAML — where each one sits

Three protocols keep showing up in federation discussions. Mixing them up muddies later sections, so a quick orientation:

ProtocolOriginal purposeUsable for sign-in alone?Where it shows up in AWS
OAuth 2.0API call authorization — “let this app read the user’s Drive”❌ no standard for representing the user’s identityNot used directly
OIDCAn authentication layer built on top of OAuth 2.0. An id_token (JWT) proves who the user isGitHub Actions, EKS IRSA, Cognito — machine/app federation (§3)
SAML 2.0XML-based SSO standard. Predates OAuthOkta, Azure AD, Google Workspace → IAM Identity Center / console sign-in (§5.1, §5.2)

Two takeaways:

  • “OAuth2 vs SAML” is the wrong framing. OAuth 2.0 is an authorization framework — by itself it has no notion of “who the user is.” The correct comparison is “OIDC vs SAML”, and OAuth 2.0 is the substrate underneath OIDC.
  • AWS picks one protocol per scenario. Humans signing into the console go through SAML; CI / Pods / apps calling the API go through OIDC. Same federation idea, but different entry APIs (AssumeRoleWithSAML vs AssumeRoleWithWebIdentity).

2. STS — the Issuance Counter for Temporary Credentials

2.1 What STS is, and why you don’t see it

STS (Security Token Service) is AWS’s “API service dedicated to issuing temporary credentials”. Whenever any AWS service validates credentials, the issuance step at the end was STS.

A summary:

AspectDetail
RoleIssues temporary credentials
StateStateless — does not store the issued tokens
Endpointsts.amazonaws.com (global) or sts.<region>.amazonaws.com (regional)
Resources to createNone
Console pageNone (only a few settings live inside IAM)
CostCalls themselves are free

If other AWS services are buildings, STS is closer to “the badge issuance counter inside the building”. You stop by, get a pass, and leave — there’s nothing to build or store there. That’s why “STS” returns nothing useful in the console search bar.

2.2 Four issuance APIs

STS APIs split into four actions based essentially on “who’s collecting the token”.

APICallerScenario
AssumeRoleAnother IAM User / RoleCross-account, privilege escalation
AssumeRoleWithWebIdentityOIDC token holderGitHub Actions, EKS Pod
AssumeRoleWithSAMLSAML assertion holderConsole login from corporate SSO
GetSessionTokenThe IAM User itselfEnforce MFA, convert one’s own keys to temporary

The most common in this article is the second — AssumeRoleWithWebIdentity. The GitHub Actions OIDC flow in Part 4 is exactly that call. EKS IRSA (service account federation) uses the same API.

2.3 What temporary credentials look like

When issued, they look like this:

{
  "AccessKeyId": "ASIAXXXX...",
  "SecretAccessKey": "abc123...",
  "SessionToken": "FwoGZXIvYXdzE...",
  "Expiration": "2026-04-28T11:30:00Z"
}

Two decisive differences from long-lived keys:

  • The SessionToken field is added. You must include it on AWS API calls or validation fails. After expiry, it’s rejected.
  • The Access Key prefix is ASIA…. Long-lived keys start with AKIA…. The prefix alone tells you at a glance whether a credential is permanent or temporary.

Key point: from a security standpoint, the core value of temporary credentials is “blast radius that decays with time”. Long-lived keys accumulate damage until the breach is discovered; temporary keys simply die on their own. You’re outsourcing the operational burden to time itself.


3. How OIDC Federation Actually Works

3.1 What OIDC is

OIDC (OpenID Connect) is an authentication standard layered on top of OAuth 2.0. The relevant piece for us is its core: “a short-lived JWT signed by a trusted issuer”.

A JWT splits into three parts (delimited by .):

eyJhbGciOiJSUzI1NiIs...     ← Header (signing algorithm)
.eyJzdWIiOiJyZXBvOl...      ← Payload (sub, aud, exp claims)
.SflKxwRJSMeKKF2QT4f...     ← Signature (signed with IdP's private key)

AWS validates two things:

  • Does the signature verify against the IdP’s public key — confirming the IdP actually issued the token.
  • Do the sub and aud claims in the payload match the trust policy’s conditions — confirming “this is the workflow/repo I authorized.”

3.2 Decomposing the GitHub Actions → AWS flow

sequenceDiagram
    participant GH as GitHub Actions Runner
    participant OIDC as GitHub OIDC Provider<br/>token.actions.githubusercontent.com
    participant STS as AWS STS
    participant AWS as AWS API (S3, SSM, ...)

    GH->>OIDC: 1. Request workflow token<br/>(jobs.id-token: write required)
    OIDC-->>GH: 2. JWT issued<br/>(sub=repo:owner/repo:ref:refs/heads/main, ...)
    GH->>STS: 3. AssumeRoleWithWebIdentity<br/>(submit JWT + RoleArn)
    Note over STS: 4. Verify JWT signature<br/>(IdP public key cached)
    Note over STS: 5. Check trust policy sub/aud
    STS-->>GH: 6. Temporary credentials (1 hour)
    GH->>AWS: 7. Calls with ASIA… + SessionToken

The decisive branch is step 5. STS reads the IAM Role’s trust policy and checks the JWT’s claims against the conditions. A mismatch means rejection in step 6.

3.3 The trust policy — sub and aud

A typical trust policy:

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {
      "Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
    },
    "Action": "sts:AssumeRoleWithWebIdentity",
    "Condition": {
      "StringEquals": {
        "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
      },
      "StringLike": {
        "token.actions.githubusercontent.com:sub": "repo:rhcwlq89/myrepo:ref:refs/heads/main"
      }
    }
  }]
}

The two key condition keys:

  • aud (audience) — “Who is the intended recipient of this token?” When GitHub Actions mints a token for AWS STS, it stamps aud=sts.amazonaws.com. The first lock against someone bringing a token meant for another system and using it to assume an AWS role.
  • sub (subject) — “Who is allowed to claim this token?” GitHub Actions formats sub as repo:<owner>/<repo>:ref:<ref> or repo:<owner>/<repo>:environment:<env>. Without narrowing this, every GitHub repo trusted by the same OIDC provider can pull your token.

3.4 The most common incident — missing sub

The number-one incident pattern in production:

"StringLike": {
  "token.actions.githubusercontent.com:sub": "repo:*"
}

Or a trust policy with no sub condition at all. With this, any workflow run from any GitHub repository can assume the role through your OIDC provider. Someone could mint an aud=sts.amazonaws.com token from their own repo, target your role ARN, and walk into your account.

The correct pattern:

"StringLike": {
  "token.actions.githubusercontent.com:sub": [
    "repo:rhcwlq89/myrepo:ref:refs/heads/main",
    "repo:rhcwlq89/myrepo:environment:prod"
  ]
}

Tighten down to repo + branch + environment. Splitting PR-build roles from prod-deploy roles is part of the same hygiene.

Caution: Use StringLike (rather than StringEquals) only when environment matching genuinely requires wildcards (repo:owner/repo:environment:prod*). For exact matches, prefer StringEquals — it removes the wildcard footgun.

3.5 How signature verification actually works — RS256, JWKS, and why KMS isn’t involved

Step 4 of the §3.2 sequence diagram (“verify JWT signature”) was a single line, but what happens inside is the heart of the OIDC security model. Let’s go one level deeper.

① How the signature is created

A GitHub OIDC Provider holds an RSA key pair.

  • Private key — held only by GitHub, used to generate signatures when issuing tokens
  • Public key — anyone can fetch it, used only for verification

The algorithm is RS256 (RSA + SHA-256). The flow:

  1. SHA-256 hash of header.payload (the first two dot-separated segments)
  2. Encrypt the hash with GitHub’s private key — that’s the signature (the third segment)
  3. AWS hashes the same header.payload
  4. Decrypt the signature with GitHub’s public key
  5. If the two hashes match, verification passes

The asymmetry is the whole point. Generating a signature requires the private key, but verifying only needs the public key. AWS can verify with the public key but cannot forge new signatures with it.

② How AWS gets GitHub’s public key — JWKS

Standard OIDC discovery, in three lines:

https://token.actions.githubusercontent.com/.well-known/openid-configuration
   → jwks_uri field points to:
   → https://token.actions.githubusercontent.com/.well-known/jwks
   → { "keys": [ { "kty": "RSA", "kid": "...", "n": "...", "e": "AQAB" }, ... ] }

AWS STS periodically fetches and caches this JWKS. When a token arrives, STS reads the kid in its header to identify which key signed it, then verifies with the cached public key.

Note: humans don’t register the public key in IAM. STS derives the JWKS location from the OIDC Provider’s URL alone and fetches it automatically.

③ STS does not use KMS — a common point of confusion

After reading the above, a natural question arises — “isn’t signing/verifying = KMS?” The answer is a clear no.

OperationTool usedKMS?
GitHub signs the JWTRSA private key in GitHub’s internal HSMN/A (outside AWS)
AWS verifies the JWTCached GitHub public key + standard libraryNo
Client signs AWS API requestsSecretAccessKey + HMAC-SHA256 (SigV4)No
SessionToken integrityAWS internal infrastructure (opaque)No (not customer KMS)

KMS shows up when “the application layer encrypts / decrypts / signs data”: S3 SSE-KMS, RDS encryption, Secrets Manager secret decryption, KMS Sign API for digitally signing documents — that domain.

The signatures in the credential issuance flow use only public-key verification (JWT) or HMAC (SigV4). KMS doesn’t appear anywhere in that chain. STS is the badge issuance counter; KMS is the vault — both are security infrastructure, but they handle different jobs.

3.6 What signature verification doesn’t catch — the real role of exp, aud, and sub

Signature verification is powerful but only catches tampering. The JWT security model addresses other threats with separate mechanisms.

ThreatCaught by signature alone?Mitigation
TamperingRS256 itself
Token theft & replayShort exp (5–15 min)
Reusing tokens for other systemsaud validation
Reusing tokens from other repos / branchessub condition (trust policy)
alg: none bypassLibrary’s algorithm allowlist
JWKS endpoint forgeryHTTPS certificates + Provider thumbprint

Briefly on each.

① Tampering — fully covered by signature

If an attacker changes a single byte in the payload, the SHA-256 hash changes. The original signature won’t match the new hash, so verification fails. Forging a valid signature requires GitHub’s private key, which exists only inside GitHub’s infrastructure. Brute-forcing RSA-2048 takes universe-lifetime scales — effectively impossible.

② Theft and replay — exp shrinks the window

What if an attacker grabs a valid JWT from someone’s workflow logs? The signature is valid, so AWS will accept it. That’s why GitHub OIDC tokens carry a short exp of 5–15 minutes. The window for stolen-token use is narrow. AWS STS always checks exp as part of verification.

③ Tokens minted for other systems — aud blocks them

GitHub Actions can mint OIDC tokens for multiple destination systems in the same workflow — aud=sts.amazonaws.com for AWS, aud=api://AzureADTokenExchange for Azure, etc. AWS STS rejects tokens whose aud isn’t its own. An Azure-bound token cannot be reused to assume an AWS role.

④ Tokens from other repos — sub is the lock

All GitHub repos receive tokens through the same OIDC provider. The signature can be perfectly valid even when sub is from someone else’s repo. The IAM Role’s trust policy must reject those. The missing-sub incident from §3.4 is exactly this lock unlocked — a perfectly-signed token from another repo just walks through.

alg: none bypass — a historical pitfall

The JWT spec includes an alg: none option (no signature). Some older libraries treated this as “verification passed” and accepted unsigned tokens. An attacker would set the header to {"alg":"none"}, blank out the signature, and slip through. Every well-built verifier today (AWS included) uses an algorithm allowlist (e.g., RS256-only) and rejects none.

⑥ JWKS endpoint forgery — the certificate trust chain

The deepest layer. For signature verification to be meaningful, AWS must hold the genuine GitHub public key. If an attacker could intercept the connection and inject a fake public key, they could mint their own valid signatures freely. Two safeguards prevent this:

  • HTTPS / TLS certificates: AWS validates the standard TLS chain when fetching JWKS, blocking man-in-the-middle attacks.
  • OIDC Provider thumbprint: registering the provider in IAM pins GitHub’s certificate thumbprint (the thumbprint_list field in Part 4’s Terraform). If the cert changes unexpectedly, the call is rejected. AWS now auto-validates this so it’s largely vestigial, but the original intent was this layer of defense.

Summary — JWT security is a five-piece set

Signature verification is sufficient and powerful for tampering. But it doesn’t cover token theft, context misuse, alg: none bypass, or JWKS forgery on its own. So the OIDC security model relies on:

  • Signature verification (RS256) — prevents tampering
  • Short exp — narrows theft windows
  • aud validation — blocks cross-system token misuse
  • sub condition — blocks other identities sharing the same IdP
  • Algorithm allowlist + JWKS HTTPS trust — prevents bypass and forgery

All five together. Drop any one and the whole posture weakens — which is why Part 4 emphasized the missing-sub case so heavily.


4. Where Do You Configure STS — the Mental Model of an “Invisible” Service

4.1 STS itself has nothing to configure

Search “STS” in the console and no standalone page appears. That’s correct. STS is an always-on global API server with no resources for humans to touch.

What humans configureConsole location
Register an OIDC Identity ProviderIAM → Identity providers
Create the IAM Role to be assumedIAM → Roles → Create role → Web identity
Trust policy with sub conditionIAM → Roles → that Role → Trust relationships
Attach permission policiesIAM → Roles → that Role → Permissions
Issue temporary credentialsNo setting — done at runtime

In other words, everything you call “OIDC configuration” is IAM work. There is no STS menu anywhere.

Three places hold settings that actually affect STS behavior.

① IAM → Account settings → Security Token Service

Toggles to enable or disable the STS endpoint per region. Defaults are all enabled. Some teams disable STS in regions they don’t use as a hardening step. The same screen has a global endpoint token compatibility v1/v2 setting — new accounts default to v2 (region-bound tokens).

② Maximum session duration on an IAM Role

Console: IAM → Roles → that Role → Edit. Sets the maximum lifetime of temporary credentials issued by AssumeRole, between 1 and 12 hours. Default is 1 hour. CI/CD roles typically stay at 1 hour; manual operator roles often run 4–8 hours.

③ Organizations SCP — restricting STS calls themselves

In multi-account setups, this is where you place guardrails like “accounts in this OU may not AssumeRole into external OIDC providers.” Irrelevant for single-account setups.

4.3 The mental model — humans vs. runtime

flowchart LR
    subgraph Human["What humans configure (IAM)"]
        IDP[Identity Provider<br/>OIDC issuer / audience]
        ROLE[IAM Role<br/>Trust policy + Permissions]
        ACCT[Account settings<br/>Regional STS toggles, etc.]
    end
    subgraph Runtime["What runs automatically (STS)"]
        STS[STS API<br/>Verify token + issue temp creds]
    end
    Caller[GitHub Actions<br/>or EC2, etc.] -->|Submit token| STS
    STS -.reads.-> IDP
    STS -.reads.-> ROLE
    STS -->|"Temporary credentials"| Caller

The “What humans configure (IAM)” box is what we touch in the console or Terraform; the “What runs automatically (STS)” box runs on its own at request time. STS is just that single box — your intuition that it’s “invisible” was correct.


5. Federation Patterns Beyond OIDC

The same federation skeleton appears in many contexts as variants. Once you see where STS and IAM Role sit in each, you won’t get lost when you encounter the next one.

5.1 SAML 2.0 — corporate SSO into the AWS Console

Logging into the AWS Console via Okta, Azure AD, or Google Workspace.

flowchart LR
    User[Employee browser] -->|"1. Login"| IdP[Okta / Azure AD]
    IdP -->|"2. SAML assertion"| User
    User -->|"3. Submit assertion"| Console[AWS Sign-in]
    Console -->|"4. AssumeRoleWithSAML"| STS[AWS STS]
    STS -->|"5. Temporary credentials"| Console

Only the token format differs — XML-based SAML assertion instead of a JWT. The skeleton is identical to OIDC. The AWS API is AssumeRoleWithSAML.

5.2 IAM Identity Center (formerly AWS SSO)

AWS’s own SSO solution. Internally it’s a layer that maps IAM Roles across multiple accounts in one place.

  • Users log in once and pick an account + role from a portal
  • The backend ultimately calls STS AssumeRole
  • The aws sso login CLI rides on top of this

Effectively required once an organization grows past five or so accounts. Lighter to operate than vanilla SAML.

5.3 EKS IRSA / Pod Identity

How Kubernetes Pods call AWS services. The EKS cluster itself acts as an OIDC provider.

flowchart LR
    Pod[K8s Pod] -->|"1. Token request"| SA[ServiceAccount<br/>+ EKS OIDC token]
    SA -->|"2. AssumeRoleWithWebIdentity"| STS[AWS STS]
    STS -->|"3. Temporary credentials"| Pod
    Pod -->|"4. S3, DynamoDB calls"| AWS[AWS API]

If GitHub Actions receives temporary keys per workflow run, IRSA receives them per Pod. Map a ServiceAccount in the Pod spec and the SDK handles the rest. Permission isolation is far finer-grained than a node-wide IAM Role.

5.4 Cognito Identity Pool

The pattern for handing temporary AWS credentials to unauthenticated guest users or social-login users in mobile/web apps. Common for direct-to-S3 uploads.

The skeleton is the same, with Cognito acting as one extra hop in front of STS.

PatternIdPSTS API
OIDCGitHub, EKS, Auth0AssumeRoleWithWebIdentity
SAMLOkta, Azure ADAssumeRoleWithSAML
IAM Identity CenterAWS itself or externalInternally AssumeRole
EKS IRSAEKS clusterAssumeRoleWithWebIdentity
Cognito Identity PoolCognito + socialGetCredentialsForIdentity

Five different contexts, but the skeleton — “external identity verification → STS → temporary keys” — is identical. Internalize this one pattern and the same diagnostic flow applies in all five.


6. Field Notes — Common Failure Points

6.1 A 5-step debug ladder for credential flows

When OIDC or AssumeRole fails, walk through these in order.

StepCheckCommand / location
1Was a token issued?GitHub Actions: id-token: write permission, post actions/checkout
2Are the token claims what you expected?Decode the token in the workflow (jwt.io, etc. — beware secret leakage)
3Is the IAM Identity Provider registered?IAM → Identity providers, check the thumbprint
4Does the Role’s trust policy sub/aud match?IAM → Roles → Trust relationships
5Is the Role’s permission policy sufficient?IAM Policy Simulator against the action you’re calling

The biggest snag is step 4. GitHub Actions’s sub shape changes depending on the trigger — pull_request events become pull_request, while push becomes ref:refs/heads/<branch>. If you change the trigger, update the trust policy too.

6.2 Common errors and what they mean

ErrorMeaningFirst diagnostic
Not authorized to perform sts:AssumeRoleWithWebIdentityTrust policy sub/aud doesn’t matchDecode the sub claim and compare patterns
InvalidIdentityToken: ... incorrect token audienceaud isn’t sts.amazonaws.comCheck configure-aws-credentials audience option
ExpiredTokenTemporary credential exceeded 1 hourFor long workflow steps, re-issue per step
AccessDenied: ... is not authorized to perform: ...Role’s permission policy is insufficientInspect Permissions policy (not Trust policy)

6.3 Touching federation by hand

Calling the flow end-to-end yourself is the fastest way to internalize it.

# 1. Inspect current credentials — what ARN, temporary (ASIA) or permanent (AKIA)?
aws sts get-caller-identity

# 2. Assume a different role — receive temporary credentials
aws sts assume-role \
  --role-arn arn:aws:iam::123456789012:role/ReadOnlyRole \
  --role-session-name my-session

# 3. List registered OIDC providers in this account
aws iam list-open-id-connect-providers

# 4. Log in via SSO (IAM Identity Center)
aws sso login --profile my-profile

These four commands are the fastest hands-on path to seeing how STS and IAM cooperate. Step 1 in particular gives an instant answer when you’re unsure where your current credentials came from (instance profile? SSO? a long-lived key?).


Recap

What to take away:

  1. Every AWS credential flow funnels into STS. EC2 instance profiles, IAM Role AssumeRole, OIDC, SAML, Cognito — they all end with STS issuing a temporary key.
  2. The shift from long-lived to short-lived keys is the larger direction in credential security. Temporary keys die on their own; incident blast radius decays naturally with time.
  3. Federation is the model where AWS trusts an external identity verification. OIDC, SAML, and Cognito are variants — the IdP and token format change, but the skeleton doesn’t.
  4. STS has nothing to configure. The lack of a console page is by design — what you call “STS configuration” is IAM Identity Provider, Role, and trust-policy work.
  5. The trust policy’s sub / aud conditions are the lock on federation. Drop them and any external identity trusted by the same provider can take your role — the number-one cause of OIDC incidents.
  6. OIDC is one of five patterns sharing the same skeleton — alongside SAML, IAM Identity Center, EKS IRSA, and Cognito. Internalize the pattern once and reuse it in five places.

If AWS Private EC2 Operations Guide Part 4 was the code that applies this pattern in our environment, this post is the look at what parts fit together underneath. The next time you encounter EKS IRSA, corporate SSO, or cross-account AssumeRole, the same mental model applies.


Appendix

A. Glossary

TermDefinition
IAMIdentity and Access Management — AWS’s identity and permissions service
STSSecurity Token Service — temporary-credentials issuance API
IdPIdentity Provider — an external identity issuer (GitHub, Okta, …)
JWTJSON Web Token — the signed token format OIDC uses
OIDCOpenID Connect — authentication protocol layered on OAuth 2.0
SAMLSecurity Assertion Markup Language — XML-based SSO protocol
Trust PolicyThe JSON policy defining who can assume an IAM Role
sub claimThe token’s subject — “whose token is this?”
aud claimThe token’s audience — “what is this token meant for?”
IRSAIAM Roles for Service Accounts — per-Pod federation in EKS
AssumeRoleThe STS API that temporarily borrows another Role’s permissions

B. References

C. Sister post

Shop on Amazon

As an Amazon Associate, I earn from qualifying purchases.