AWS IAM Outbound Identity Federation - Issuing Short-Lived Tokens from AWS to External Services

First Published:
Last Updated:

Somewhere in almost every AWS estate there is a secret that does not belong to AWS. An API key for an observability vendor, a service account credential for another cloud provider, a token for a data platform, a password for an application running in a rack you still own. It sits in a secret store, a Lambda function reads it at invocation, and it stays valid until a human remembers to rotate it. The blast radius of that credential leaking is not bounded by time. It is bounded by how quickly somebody notices.

AWS IAM outbound identity federation removes that credential from the picture entirely. An IAM principal calls AWS STS, receives a short-lived JSON Web Token signed by AWS, and presents it to the external service. The external service fetches AWS's public verification keys and checks the signature. Nothing long-lived is stored anywhere, and the thing being asserted is not a secret at all — it is an identity claim that anyone can verify and nobody can forge.

This is the direction of federation that almost no one writes about. The overwhelming majority of federation material describes the opposite direction, where an external identity provider issues a token and AWS consumes it. This article is about AWS as the issuer. The distinction is not cosmetic: the trust configuration lives in a different place, the secret disappears from a different side, and the failure modes are entirely different. Section 2 fixes that distinction before anything else, because every design decision downstream depends on which direction you are actually in.

Every fact below was verified against AWS official documentation on 2026-08-09, with the specific page linked at the point of use. This article reports no measurements, and no token was minted, no IAM policy was created, and no external service was configured in the course of writing it. Command syntax and code shapes are reproduced from AWS reference documentation and are labeled as such rather than presented as tested output. Where two AWS pages disagree, both are shown and the authoritative one is named. No pricing appears anywhere in this article.

Table of Contents

  1. 1. Introduction: The Long-Lived Key You Handed Out
  2. 2. Two Directions of Federation
  3. 3. The Issuer Side - What AWS Publishes
  4. 4. What Is Inside the Token
  5. 5. Controlling Who Can Mint a Token
  6. 6. The Verifier Side - Implementing Validation Correctly
  7. 7. ABAC Across the Boundary
  8. 8. Auditing with CloudTrail
  9. 9. The Revocation Problem
  10. 10. Migration from Long-Lived Keys
  11. 11. Failure Modes and Anti-Patterns
  12. 12. Frequently Asked Questions
  13. 13. Summary
  14. 14. References

1. Introduction: The Long-Lived Key You Handed Out

The decision this article supports is narrow and recurring: you have an AWS workload that must authenticate to something outside AWS, and you want to stop shipping a static credential to do it. That is a different problem from choosing where to store secrets, and a different problem again from letting an outside system into your AWS account. It is the problem of proving, to a party that has no visibility into your account, that a request originated from a specific IAM principal in a specific AWS account, at a specific moment.

Before this capability existed, the available answers were all variations on the same compromise. Mint a credential in the external service, store it in AWS, rotate it on a schedule, and accept that between rotations the credential is a bearer token with an unbounded lifetime. Rotation automation reduces the window but does not change the shape of the risk, because the credential is still a secret that must be transmitted, stored, read into process memory, and occasionally logged by accident.

Outbound identity federation changes the shape rather than the window. AWS Identity and Access Management outbound identity federation lets IAM principals request short-lived JSON Web Tokens from AWS Security Token Service, and AWS states plainly that this "eliminates the need to store long-term credentials such as API keys or passwords in your application code or environment variables" (Federating AWS Identities to external services). The token is signed, publicly verifiable, and carries a rich description of which AWS identity asked for it.

The capability was announced in November 2025 and is available, per AWS, "in all AWS commercial, GovCloud (US), and China Regions" (AWS IAM enables identity federation to external services using JSON Web Tokens). It is not a preview and it is not Region-limited, which means the design questions below are live questions for any account, today.

1.1 Scope and delegation

In scope: the two directions of federation and why they do not compose; what AWS publishes on the issuer side and what it does not; the contents of the token and the claims that are conditionally present; the IAM controls that decide who may mint a token and with what properties; how to implement verification correctly and what each skipped check costs; carrying attributes across the boundary for attribute-based authorization; what CloudTrail gives you and what it does not; the revocation problem and its consequences for lifetime design; a migration order from long-lived keys; and the resulting failure modes.

Out of scope, with delegation:

  • IAM policy evaluation order. How explicit denies, SCPs, RCPs, permission boundaries, and session policies combine into a single decision is a subject in its own right and is fully delegated to IAM Policy Evaluation Logic Step-by-Step. Section 5 names which condition keys exist and where they apply, and deliberately does not restate the evaluation algorithm.
  • Human identity federation and single sign-on. Workforce identity, permission sets, and Identity Center configuration belong to AWS IAM Identity Center Complete Setup Guide. Two token claims expose Identity Center context, and section 4 notes their existence without describing how to configure the service that produces them.
  • Inbound workload identity setup. Configuring an external OIDC provider so that a workload outside AWS can assume an AWS role is covered by Amazon EKS Pod Identity and IRSA Decision Guide for the Kubernetes case and Amazon Cognito Federation Complete Implementation Guide for the application case. Section 2 uses inbound only as a contrast and gives no configuration steps for it.
  • Choosing a secret store. Whether a given secret belongs in Secrets Manager or Parameter Store, and how rotation is wired, is the subject of AWS Secrets Manager and Parameter Store Decision Guide. Section 10 inventories the secrets you are trying to delete and stops there.
  • Designing organization guardrails. The data perimeter patterns that SCPs and RCPs implement are covered in AWS Organization Guardrails. Section 5.4 states only which policy types apply to this API.

1.2 How the facts were established

Every behavior below comes from an AWS primary source: the IAM User Guide chapters on outbound identity federation, the AWS STS and IAM API references, the IAM policy condition key reference, AWS CLI reference pages, the launch announcement, and the AWS service documentation of two first-party integrations that consume this feature. Secondary commentary was not used.

Two kinds of statement are deliberately absent. The first is any claim about behavior AWS does not document, most importantly the schedule on which AWS rotates its signing keys — section 3.3 says what AWS publishes and then treats the rest as a design constraint rather than inventing a value. The second is any claim of measurement. Where a limit appears below, it is a documented limit, not an observed one.

2. Two Directions of Federation

Federation is a word that hides an arrow. Two systems agree that one of them will vouch for identities and the other will accept that vouching, and everything about the design depends on which end AWS occupies. Getting the arrow backwards is the single most common way to misread this feature, because the mental model most engineers already have points the other way.

Inbound and outbound federation point in opposite directions
Inbound and outbound federation point in opposite directions

2.1 Inbound: an external identity becomes an AWS identity

In the inbound direction, an external identity provider issues a token, AWS is configured to trust that issuer, and a principal from outside ends up holding AWS credentials. A GitHub Actions workflow presents a workflow token and receives a role session. A Kubernetes service account presents a projected token and receives a role session. A workforce user authenticates against a corporate directory and receives a role session. The API at the center is AssumeRoleWithWebIdentity, and the trust configuration — which issuer, which audience, which subject patterns — lives inside the AWS account, in an OIDC identity provider object and a role trust policy.

This direction is thoroughly documented, thoroughly blogged, and thoroughly tooled. It is what most people mean when they say federation on AWS, and the IAM condition key reference reflects that: it enumerates provider-specific claim keys for GitHub, GitLab, Google, CircleCI, Buildkite, Oracle Cloud Infrastructure, Facebook, Amazon Cognito, and Login with Amazon (IAM and AWS STS condition context keys). Every one of those entries exists so that an AWS role trust policy can inspect a token that somebody else issued.

2.2 Outbound: an AWS identity is asserted to an external service

In the outbound direction everything moves to the other side. AWS is the issuer. An IAM principal that already holds AWS credentials calls GetWebIdentityToken, and AWS STS returns a signed JWT asserting who that principal is. The token is then presented to something outside AWS. The trust configuration — which issuer, which audience, which subject — lives in the external service, not in your AWS account. Your account contributes exactly two things to that trust relationship: an issuer URL, and IAM policies deciding who is allowed to obtain a token in the first place.

The AWS launch post describes the resulting flow in six steps, and the notable feature is what happens at the end: after verification, "the external service exchanges the JWT for its own credentials" (Simplify access to external services using AWS IAM Outbound Identity Federation). The AWS token is not a bearer credential for the external system's resources. It is a proof of identity that the external system converts into whatever it uses natively.

2.3 What actually reverses

Three things swap ends, and it is worth naming them individually because engineers commonly transfer an intuition from one direction that is false in the other.

* You can sort the table by clicking on the column name.

AspectInbound (external identity into AWS)Outbound (AWS identity out)
Who issues the tokenThe external identity providerAWS STS
Where the trust configuration livesIn your AWS account, as an OIDC provider and a role trust policyIn the external service, as a trusted issuer entry
Who verifies the signatureAWSThe external service
Which long-lived secret disappearsThe IAM user access key you would otherwise have given the external systemThe API key the external system would otherwise have given you
What you control with IAMWhich external identities may assume which rolesWhich of your principals may mint a token, and with what audience, lifetime, and algorithm
What you cannot control after issuanceNothing, because sessions can be constrained and roles can be changedThe token, which remains valid until it expires (section 9)

The last row is the one that surprises people. In the inbound direction, an AWS role session is subject to AWS policy at the moment of every request it makes, so tightening a policy takes effect immediately for sessions already in flight. In the outbound direction, once the token is signed and handed over, AWS is no longer in the request path at all. Nothing you change in IAM reaches a token that already exists.

2.4 The two directions do not compose

It is natural to ask whether the token AWS issues could be fed back into AWS - for example, to move an identity between accounts without configuring a role trust relationship. AWS closes that door explicitly. The getting-started documentation states that the JSON Web Tokens generated by the GetWebIdentityToken API "cannot be used for OpenID Connect (OIDC) federation into AWS (via the AssumeRoleWithWebIdentity API)" (Getting started with outbound identity federation).

That restriction is worth reading as a design statement rather than a limitation. An outbound token is a claim about an identity, produced for consumption by parties that cannot evaluate IAM policy. Inside AWS, identity is not asserted by tokens of this kind; it is established by request signing and evaluated by policy. Allowing the outbound token to re-enter would create a second, weaker path to the same authority, and would make the token itself a credential — exactly the property the feature exists to remove.

The related boundary question of what a network path does and does not guarantee, and how that interacts with authorization, is treated separately in The Boundaries of the AWS Global Network. The two articles meet at section 5.4 below, where the network layer becomes one of the policy types that can constrain token issuance.

3. The Issuer Side - What AWS Publishes

Being an OIDC issuer means publishing two things at a stable location: metadata describing the issuer, and the public keys that verify its signatures. AWS does both, per account, once you turn the feature on.

3.1 Enabling the feature and obtaining the issuer URL

Outbound identity federation is off by default and is enabled at the account level. AWS documents three IAM API operations for managing it, all of them account-scoped and none of them taking a resource argument.

OperationWhat it doesDocumented error
EnableOutboundWebIdentityFederationEnables the feature and generates a unique issuer URL for the accountFeatureEnabled (409) if already enabled
DisableOutboundWebIdentityFederationStops principals in the account from obtaining new tokensFeatureDisabled (404) if already disabled
GetOutboundWebIdentityFederationInfoReturns the issuer URL and the current enabled stateFeatureDisabled (404)

EnableOutboundWebIdentityFederation returns a single element, IssuerIdentifier, described as "a unique issuer URL for your AWS account that hosts the OpenID Connect (OIDC) discovery endpoints" (EnableOutboundWebIdentityFederation). Because the enable call fails with a 409 once the feature is on, the operation you actually want in automation is the read: GetOutboundWebIdentityFederationInfo returns both IssuerIdentifier and a boolean JwtVendingEnabled (GetOutboundWebIdentityFederationInfo).

aws iam enable-outbound-web-identity-federation

aws iam get-outbound-web-identity-federation-info

The AWS Systems Manager documentation, which uses this feature for its own Azure integration, describes the console path as IAM, then Account settings, then Outbound web identity federation, and shows the issuer URL taking the form https://UNIQUE_ID.tokens.sts.global.api.aws (AWS prerequisites for a Cloud Connector). The IAM API reference shows the same shape in its sample response, with a UUID in the leading label.

A note on documentation drift. The getting-started page prints response['IssuerUrl'] and response['Status'] in its Python sample, while the API reference, the CLI reference, and the SDK reference all name the fields IssuerIdentifier and JwtVendingEnabled. Treat the API reference as authoritative. The same drift appears again in section 4 for the token field itself, and it is a useful reminder that on a capability this new, the reference pages and the tutorial pages have not fully converged.

3.2 Discovery and JWKS

The issuer URL hosts the two well-known endpoints that any OIDC-aware verifier expects.

EndpointPurpose
{issuer_url}/.well-known/openid-configurationIssuer metadata, described by AWS as "metadata some providers use to verify tokens"
{issuer_url}/.well-known/jwks.jsonThe JSON Web Key Set containing the public keys that verify token signatures

AWS documents the JWKS response shape with one EC key and one RSA key present simultaneously, which matches the two signing algorithms the API offers.

{
  "keys": [
    {
      "kty": "EC",
      "use": "sig",
      "kid": "key-id-1",
      "alg": "ES384",
      "crv": "P-384",
      "x": "base64-encoded-x-coordinate",
      "y": "base64-encoded-y-coordinate"
    },
    {
      "kty": "RSA",
      "use": "sig",
      "kid": "key-id-2",
      "n": "base64-encoded-modulus",
      "e": "AQAB"
    }
  ]
}

Note what AWS says about the discovery document and, more importantly, what it does not say. The documentation characterizes it as metadata without enumerating its fields, so a verifier that depends on a specific member of that document is depending on something AWS has not committed to in writing. The JWKS endpoint, by contrast, is described concretely and is the one your verification code genuinely needs.

3.3 Keys, key identifiers, and the rotation question

Both signing algorithms are named precisely in the API reference: RS256 is "RSA with SHA-256" and ES384 is "ECDSA using P-384 curve with SHA-384" (GetWebIdentityToken). The getting-started page adds guidance on choosing between them: "Use ES384 for optimal security and performance, or RS256 for broader compatibility with systems that do not support ECDSA."

The token header carries a key identifier. AWS's launch post shows a decoded header of {"kid": "EC384_0", "typ": "JWT", "alg": "ES384"}, which tells the verifier which JWKS entry to use.

AWS does not publish a key rotation schedule for this issuer, and does not document the lifecycle of the kid values. That absence is a fact worth stating rather than filling in. It has one direct implication for verifier design, and section 6.2 turns it into a concrete rule: a verifier must be able to encounter an unknown kid and recover by refetching the key set, because it cannot know in advance when a new key will appear. Any implementation that fetches the JWKS once at startup and caches it for the process lifetime is betting on an interval AWS has never promised.

3.4 Do not construct the issuer URL

The documented format is a UUID-like label under tokens.sts.global.api.aws, and every AWS page that shows one shows that form. It is tempting to treat that as a template and derive the issuer URL from an account ID or a stored UUID.

Resist it, for two reasons. First, the label is not derived from anything you already know - AWS generates it when you enable the feature, which is why the API returns it rather than expecting you to compute it. Second, AWS has not published the issuer URL format for the GovCloud (US) or China partitions, even though the feature is available in both. An estate that hardcodes the commercial format and later extends into another partition inherits a silent configuration error, and the failure surfaces on the verifier side as an issuer mismatch that looks like a trust problem rather than a string problem.

The correct pattern is the boring one: call GetOutboundWebIdentityFederationInfo, treat the returned string as opaque, and propagate it to wherever the external trust configuration lives.

4. What Is Inside the Token

The token is the interface. Everything the external service can decide about your workload, it decides from these claims, so the set of claims is effectively the vocabulary available for cross-boundary authorization.

Outbound token flow, from issuance to verification
Outbound token flow, from issuance to verification
AWS states that the tokens are "compliant with RFC 7519" and that the AWS-specific claims are "nested under the https://sts.amazonaws.com/ namespace in the token" (Understanding token claims). The namespacing matters: standard JWT libraries will validate the top-level claims for you, and everything AWS-specific arrives as a nested object that your own code must reach into.

4.1 Standard OIDC claims

ClaimMeaningExample from AWS documentation
issYour account-specific issuer URLhttps://abc123-def456-ghi789-jkl012.tokens.sts.global.api.aws
audThe intended recipient, taken from the Audience request parameterhttps://api.example.com
subThe ARN of the IAM principal that requested the tokenarn:aws:iam::123456789012:role/DataProcessingRole
iatIssued-at time as a NumericDate1700000000
expExpiration time as a NumericDate1700000900
jtiUnique identifier for this token instancexyz123-def456-ghi789-jkl012

The sub claim deserves attention because it is the claim external services actually match on. AWS documents it as the ARN of the requesting IAM principal, and two first-party AWS integrations confirm the exact form in practice by using a role ARN as the subject in their trust configuration — including the IAM path. AWS Systems Manager uses arn:aws:iam::ACCOUNT_ID:role/service-role/SSM-AzureRole-CONNECTOR_NAME-ID8 (Azure prerequisites), and Security Hub CSPM uses arn:aws:iam::account-id:role/aws-service-role/thirdparty.config.amazonaws.com/AWSServiceRoleForConfigThirdParty (Configuring Microsoft Azure to integrate with Security Hub CSPM).

The path is part of the subject. A verifier that matches subjects by role name, or that strips paths when normalizing ARNs, will fail against service-linked roles and against any role you create under a path. This is the single most likely place for an integration to break in a way that looks mysterious from both ends.

4.2 AWS identity claims

These describe the account and the principal, and AWS helpfully documents which IAM condition key each one corresponds to — which is the bridge that makes section 7 possible.

ClaimDescriptionCorresponding condition key
aws_accountYour AWS account IDaws:PrincipalAccount
source_regionThe AWS Region where the token was requestedaws:RequestedRegion
org_idYour AWS Organizations ID, if the account is in an organizationaws:PrincipalOrgID
ou_pathThe organizational unit path, if applicableaws:PrincipalOrgPaths
principal_tagsTags on the IAM principal or the assumed role sessionaws:PrincipalTag/tag-key

org_id is the most operationally valuable of these for a verifier that serves more than one AWS account. Rather than enumerating every issuer URL of every account, a verifier can accept a set of issuers and then require a specific organization ID, which survives account creation and deletion.

4.3 Session context claims

AWS describes these as claims about "the compute environment and session where the token request originated," included "when applicable based on the requesting principal's session context."

ClaimDescriptionCorresponding condition key
original_session_expWhen the original role session credentials expire, for assumed rolesNot applicable
federated_providerThe identity provider name for federated sessionsaws:FederatedProvider
identity_store_user_idIAM Identity Center user IDidentitystore:UserId
identity_store_arnARN of the Identity Center identity storeidentitystore:IdentityStoreArn
ec2_source_instance_arnARN of the requesting EC2 instanceec2:SourceInstanceArn
ec2_instance_source_vpcVPC ID where EC2 role credentials were deliveredaws:Ec2InstanceSourceVpc
ec2_instance_source_private_ipv4Private IPv4 address of the EC2 instanceaws:Ec2InstanceSourcePrivateIPv4
ec2_role_deliveryInstance metadata service versionec2:RoleDelivery
source_identitySource identity set by the principalaws:SourceIdentity
lambda_source_function_arnARN of the calling Lambda functionlambda:SourceFunctionArn
glue_credential_issuing_serviceAWS Glue service identifier for Glue jobsglue:CredentialIssuingService

Two of these change what is possible on the verifier side in a way nothing else does. lambda_source_function_arn narrows the assertion from "some principal using this role" to "this specific function," which matters because a role is routinely shared across many functions. ec2_instance_source_vpc and ec2_source_instance_arn do the same for instances. A verifier that cares about which compute produced the request can enforce that without any coordination beyond reading a claim.

original_session_exp is the quietest and most interesting entry in the table. It tells the verifier when the underlying AWS role session was going to expire anyway, which is context no other mechanism provides — and it connects directly to a documented error condition covered in section 5.5.

4.4 Request tags become custom claims

The caller may attach tags to the request, and they arrive in the token under request_tags. AWS documents a maximum of 50 tags per request, and a corresponding error, JWTPayloadSizeExceeded, whose remediation is to "reduce the number of request tags included in the GetWebIdentityToken API call to reduce the token payload size."

aws sts get-web-identity-token \
    --audience "https://api.example.com" \
    --signing-algorithm ES384 \
    --duration-seconds 300 \
    --tags Key=team,Value=data-engineering \
           Key=environment,Value=production \
           Key=cost-center,Value=analytics

The resulting token places those values under request_tags, separately from principal_tags. That separation is not decorative and section 7.3 depends on it entirely: one of the two is set by the caller at request time, and the other is set by the identity configuration. Any verifier that merges them has thrown away the only signal distinguishing an assertion about the workload from an assertion by the workload.

Here is the sample token AWS publishes, which shows the namespacing and the coexistence of both tag objects.

{
  "iss": "https://abc123-def456-ghi789-jkl012.tokens.sts.global.api.aws",
  "aud": "https://api.example.com",
  "sub": "arn:aws:iam::123456789012:role/DataProcessingRole",
  "iat": 1700000000,
  "exp": 1700000900,
  "jti": "xyz123-def456-ghi789-jkl012",
  "https://sts.amazonaws.com/": {
    "aws_account": "123456789012",
    "source_region": "us-east-1",
    "org_id": "o-abc1234567",
    "ou_path": "o-a1b2c3d4e5/r-ab12/ou-ab12-11111111/ou-ab12-22222222/",
    "principal_tags": {
      "environment": "production",
      "team": "data-engineering",
      "cost-center": "engineering"
    },
    "lambda_source_function_arn": "arn:aws:lambda:us-east-1:123456789012:function:process-data",
    "request_tags": {
        "job-id": "job-2024-001",
        "priority": "high",
        "data-classification": "sensitive"
    }
  }
}

4.5 What the token does not assert

AWS attaches an explicit caveat to the claim list: "Please note that all these claims may not be present in a token at the same time." Nothing in the documentation tells you which combinations are guaranteed, so the presence of a claim is a fact about one token, not a property of the issuer.

The launch post makes the point sharper than the caveat does. Its sample decoded payload contains a claim named principal_id that does not appear anywhere in the reference table of claims. Whether that field is stable, renamed, or an artifact of a pre-launch build is not something an external observer can determine — and that uncertainty is exactly the point. A verifier written against the sample rather than the reference could be depending on a claim that has no documented contract.

Three rules follow, and they are the difference between an integration that survives and one that breaks on a Tuesday:

  1. Treat every AWS-specific claim as optional. Absence must produce a deny, never an exception and never a silent default.
  2. Never let absence widen access. If your rule is that production workloads may write and everything else may read, the missing-tag case must fall on the read side, not on the write side.
  3. Ignore unknown claims rather than rejecting them. The claim set has already grown once; a verifier that rejects tokens containing fields it does not recognize will break the next time it grows.

5. Controlling Who Can Mint a Token

Everything on the AWS side of this feature reduces to one question: which principals may call GetWebIdentityToken, and with what parameters. IAM answers it with two actions, three purpose-built condition keys, two tag condition keys, and the usual policy types.

5.1 Two actions, not one

sts:GetWebIdentityToken grants the ability to request a token. AWS documents a second, separate action for tags: "To allow Tags (key, value pairs) to be passed to the GetWebIdentityToken call, the IAM principal must have the sts:TagGetWebIdentityToken permission" (Controlling access with IAM policies).

Splitting these is a genuinely useful design. A principal can be allowed to prove its identity while being prevented from decorating that proof with self-asserted attributes. If an external service makes authorization decisions from request_tags, then withholding sts:TagGetWebIdentityToken from principals that have no business influencing those decisions is a one-line control that closes the whole category.

5.2 The three condition keys

Condition keyOperator familyWhat it constrains
sts:IdentityTokenAudienceStringWhich audiences the principal may request, and therefore which services can be handed a token
sts:DurationSecondsNumericThe maximum lifetime the principal may request
sts:SigningAlgorithmStringWhich of ES384 and RS256 the principal may request

AWS publishes this combined example, which is the shape most identity policies for this feature should start from.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowTokenGenerationWithRestrictions",
            "Effect": "Allow",
            "Action": "sts:GetWebIdentityToken",
            "Resource": "*",
            "Condition": {
                "ForAnyValue:StringEquals": {
                    "sts:IdentityTokenAudience": [
                        "https://api1.example.com",
                        "https://api2.example.com"
                    ]
                },
                "NumericLessThanEquals": {
                    "sts:DurationSeconds": 300
                },
                "StringEquals": {
                    "sts:SigningAlgorithm": "ES384"
                }
            }
        }
    ]
}

Resource is * in every example AWS publishes, and that is correct rather than lazy: there is no resource to scope, because the operation acts on the calling identity itself. The scoping is done entirely by the conditions.

5.3 ForAnyValue and ForAllValues are not interchangeable here

Audience is an array parameter. AWS documents it as accepting a minimum of 1 and a maximum of 10 items, each up to 1000 characters, and states that the value "populates the aud claim in the JWT." That makes sts:IdentityTokenAudience a multivalued context key, which per AWS "require a condition set operator" (Single-valued vs. multivalued context keys).

Both set operators appear in AWS's own examples for this feature. The getting-started page uses ForAllValues:StringEquals with a single audience; the policy page and the condition key reference both use ForAnyValue:StringEquals with two. They do not mean the same thing, and the difference is consequential when the request can carry up to ten values.

AWS defines them precisely. ForAllValues "returns true if every context key value in the request matches a context key value in the policy," and also "returns true if there are no context keys in the request." ForAnyValue "returns true if any one of the context key values in the request matches any one of the context key values in the policy."

Read that against a request carrying ten audiences. Under ForAnyValue, a request whose audience list contains one permitted value and nine others still matches the condition, and the resulting token carries all ten in its aud claim — meaning it is a valid token for nine audiences the policy never intended to authorize. Under ForAllValues, that request does not match, because not every value is in the allowed set.

For bounding which external services may be handed a token, ForAllValues is the operator that expresses the intent. AWS pairs one caution with it: ForAllValues with an Allow effect "can be overly permissive if the presence of missing context keys in the request context is unexpected," and the recommended remedy is a Null check with a value of false. For this particular key the absent case cannot arise, because Audience is a required API parameter and the key is documented as present in every GetWebIdentityToken request. Adding the Null guard anyway costs one line and removes the need for a future reader to reason about that at all.

The same reasoning applies to aws:RequestTag/tag-key and aws:TagKeys, which AWS lists as additionally available for this call and which section 7.4 uses to bound what a caller may assert about itself.

5.4 Where else the decision can be made

AWS names four policy types for this API: identity policies, service control policies, resource control policies, and VPC endpoint policies. SCPs "enable you to enforce organization-wide restrictions on token generation across all accounts," and VPC endpoint policies "restrict which principals can access the AWS STS GetWebIdentityToken API through your VPC endpoints, adding network-level controls to your security posture."

The VPC endpoint angle has a wrinkle that matters. The STS VPC endpoint policy documentation distinguishes two caller types: authenticated AWS principals that sign with SigV4, for whom keys such as aws:PrincipalOrgID and aws:PrincipalArn are available, and federated callers arriving via AssumeRoleWithSAML or AssumeRoleWithWebIdentity, for whom those keys are absent because "they have no AWS identity at the time of the request" (Control access to AWS STS with VPC endpoint policies).

GetWebIdentityToken falls squarely in the first category. AWS describes the caller as using "its existing AWS credentials obtained from the underlying platform," so principal-based condition keys are available and an endpoint policy written around aws:PrincipalOrgID will behave as expected for it. That is the opposite of the situation for the inbound APIs on the same endpoint, and it is why an endpoint policy that works for one direction cannot be assumed to work for the other. The broader question of what an endpoint changes and what it leaves alone is treated in The Boundaries of the AWS Global Network; the interaction of SCPs and RCPs as a perimeter is treated in AWS Organization Guardrails.

One further constraint shapes where the call can happen at all: AWS states that GetWebIdentityToken is not available on the STS global endpoint. Every call is a Regional call, which has consequences for endpoint configuration, for VPC endpoint placement, and for where the audit trail lands (section 8.1).

5.5 Principal types and the session ceiling

AWS describes the eligible callers as "IAM principals - such as IAM roles and users," and the documented compute sources are the ordinary ones: EC2 instance profiles, Lambda execution roles, and other AWS compute. In an estate that has already eliminated IAM users, nothing here reintroduces them.

Assumed-role sessions carry a constraint that is easy to miss until it fires in production. AWS documents a SessionDurationEscalation error, returned with HTTP 403: "The requested token duration would extend the session beyond its original expiration time. You cannot use this operation to extend the lifetime of a session beyond what was granted when the session was originally created."

The practical shape of that is a token request that succeeds all day and fails near the end of a session. A workload holding a role session with four minutes left cannot obtain a one-hour token. The failure is correct — a token outliving the session that authorized it would be an escalation — but it is time-dependent, which makes it the kind of thing that first appears in a long-running job rather than in a test. Two mitigations follow directly: request the shortest lifetime that works, and refresh the underlying role session rather than treating a token request failure as a permanent error. The original_session_exp claim from section 4.3 is the same information, exposed to the far side.

Finally, on rate: the IAM and STS quota page lists a default of 600 requests per second per account per Region shared among a named set of operations, and GetWebIdentityToken is not among the operations listed as sharing that quota (IAM and AWS STS quotas). AWS does not publish a separate figure for it either. The honest reading is that the rate limit for this call is unspecified, which argues for caching a token for its lifetime rather than minting one per outbound request.

6. The Verifier Side - Implementing Validation Correctly

The AWS side of this feature is small. The verifier side is where security is actually won or lost, because the verifier is the only component in the entire flow that can say no once a token exists.

6.1 Pin the issuer before anything else

AWS's guidance is to "verify the issuer matches the AWS account(s) you trust" and to "maintain a list of trusted issuer URLs." The launch post's sample code makes this the very first check, before any network call, rejecting the token outright if the iss value is not in an allowlist.

The ordering is the point. Because the verifier discovers the JWKS location from the issuer URL, a verifier that fetches keys before checking the issuer is fetching keys from a location the token told it to use. Checking membership in a static allowlist first means the only key sets you ever retrieve are ones you chose in advance.

For a verifier serving many AWS accounts, maintaining that allowlist by hand becomes the weak point. The org_id claim (section 4.2) is the documented alternative: accept issuers belonging to your organization and require a specific organization ID, so that account lifecycle events do not require an allowlist edit.

6.2 Fetch the key set, and cache it correctly

AWS's instruction is brief and unambiguous: "We recommend caching these keys to avoid fetching them for every token verification."

import requests

# Fetch Openid Configuration
open_id_config_response = requests.get("https://{issuer_url}/.well-known/openid-configuration")
open_id_config = open_id_config_response.json()

# Fetch JWKS
jwks_response = requests.get("https://{issuer_url}/.well-known/jwks.json")
jwks = jwks_response.json()

That advice bounds one failure mode and leaves the opposite one open. Fetching per verification makes the issuer endpoint a hard dependency of your request path and turns a transient network problem into an authentication outage. Caching indefinitely makes you unable to verify tokens signed with a key that appeared after your process started — and since AWS does not publish a rotation schedule (section 3.3), you cannot know when that will be.

The rule that resolves both, and the one thing this section would keep if it kept only one thing: cache the key set with a bounded lifetime, and additionally refetch on encountering an unknown kid, with a rate limit on that refetch. Unknown-kid refetching is what makes rotation a non-event. The rate limit is what stops a stream of tokens carrying fabricated kid values from turning your verifier into a load generator against the issuer.

6.3 Verify the signature against the key set, not against a key

The kid in the header selects the key. Standard libraries do this for you, and AWS's published outline uses PyJWKClient to resolve the signing key from the token before decoding.

Two details are worth stating explicitly because they are the classic JWT mistakes and neither is hypothetical here. First, the algorithm list must be supplied by the verifier, not read from the token. AWS's sample passes algorithms=["ES384", "RS256"] explicitly. Second, restrict that list to the algorithms you actually expect. AWS offers exactly two, and if your issuing policy pins sts:SigningAlgorithm to ES384 (section 5.2), then your verifier can and should accept only ES384 - the two controls together mean an unexpected algorithm is a signal rather than a shrug.

6.4 Then validate the claims

AWS lists four essential validations, and each one answers a distinct question.

CheckQuestion it answersAWS guidance
issDid a party I trust issue this?Verify the issuer matches the AWS accounts you trust, from a maintained list
subWhich principal is this?Verify the subject contains the expected IAM principal ARN pattern
audWas this token meant for me?Verify the audience matches your expected value
expIs it still valid?Ensure the token has not expired

Beyond those, AWS recommends validating the AWS-specific claims "whenever possible," naming org_id to restrict access to principals in your organization, principal_tags for attribute-based control, and session context claims such as lambda_source_function_arn or ec2_instance_source_vpc to restrict by compute resource.

6.5 What each skipped check costs

This table is written from the defensive side. Each row names a check, then names what the check is the only thing standing between you and.

Check omittedWhat stops being true
iss not pinned to an allowlistAny issuer the token names is trusted, including one whose keys you fetch on the token's instruction. Issuer pinning is the root of the entire trust chain
Signature not verifiedThe claims are attacker-controlled input. Every other check becomes decorative
Algorithm list read from the tokenThe token chooses how it will be checked. This is the oldest JWT weakness and it survives because the default in some libraries is permissive
aud not validatedA token minted for a different service is accepted by yours. Because one request may carry up to ten audiences, this is the check that makes audience scoping mean anything
exp not validatedThe token never expires from your point of view - and since expiry is the only revocation mechanism that exists (section 9), you have removed the sole limit on a leaked token
sub not matched to expected principalsEvery principal in a trusted account is equivalent to every other. Account-level trust replaces role-level trust silently
Unknown kid treated as fatal without refetchThe integration works until AWS introduces a new key, then stops, at a moment nobody scheduled
principal_tags and request_tags mergedA caller-supplied attribute is treated as an identity attribute. Section 7.3

6.6 What a real verifier configuration looks like

AWS documents two of its own services as consumers of this feature, and they are the most useful worked examples available because they show which claims a production verifier actually keys on.

AWS Systems Manager Cloud Connector uses it to reach Microsoft Azure. The AWS-side prerequisite is enabling outbound web identity federation and noting the issuer URL; the Azure-side configuration registers a federated identity credential naming an issuer, a subject, and an audience. Security Hub CSPM does the same thing for its Azure integration, and publishes the command shape:

$ az ad app federated-credential create \
  --id application-client-id \
  --parameters '{
    "name": "AWSConfigFederation",
    "issuer": "token-issuer-url",
    "subject": "arn:aws:iam::account-id:role/aws-service-role/thirdparty.config.amazonaws.com/AWSServiceRoleForConfigThirdParty",
    "audiences": ["api://AzureADTokenExchange"],
    "description": "Federation for AWS Config third-party cloud resource discovery"
  }'

Three things are visible here that no amount of reading the IAM documentation would tell you. The trust is scoped to one issuer, one subject, and one audience — not to an account. The subject is a full role ARN including its path, confirming section 4.1. And the audience is a value the external service defines, not one you choose, which is why sts:IdentityTokenAudience in your issuing policy must be written against the value the far side actually expects.

The Security Hub documentation adds one more structural lesson: its integration registers a separate federated credential per capability, each with a different service-linked role as the subject. One role per integration, one federated credential per role. That is the shape that lets you revoke a single integration by editing one trust entry, and it is the shape section 9.4 depends on.

7. ABAC Across the Boundary

Attribute-based access control inside AWS works because policies can read tags from the request context. Outbound federation extends the same idea across an administrative boundary, with one crucial difference: the far side cannot evaluate IAM policy, so the attributes have to travel inside the token.

7.1 The claim-to-condition-key mapping is the contract

The mapping table in section 4.2 is more than documentation convenience. It means an attribute you already use for authorization inside AWS - the organization ID, the OU path, a principal tag — arrives on the far side with a documented correspondence to the IAM concept it came from. A rule expressed as aws:PrincipalTag/environment inside AWS and a rule expressed as principal_tags.environment in an external policy engine are reading the same underlying attribute, which is what makes a single tagging scheme usable on both sides.

That is the case for treating tags as an interface rather than as metadata. Naming conventions, allowed values, and ownership are the subject of AWS Tagging Strategy; what this article adds is that once tags cross this boundary, a tag rename is a breaking change to an external integration, not an internal cleanup.

7.2 Session tags take precedence, and that is a feature

AWS documents the interaction explicitly: "When a token is requested where the requesting IAM principal has both principal tags and session tags, the session tags will be present in the JWT."

That resolution is the useful one. Session tags are set when a role is assumed, which means they can carry per-invocation context that a tag on the role itself cannot. A single role used by many tenants can produce tokens carrying the correct tenant attribute, because the tag is attached at assume time rather than to the role.

It also means the value a verifier sees may not be the value visible on the role in the console. When an external authorization decision does not match expectations, the assume-role path is where to look, not the role's tag list. Session tag mechanics are documented in Pass session tags in AWS STS.

7.3 Request tags are an assertion by the caller, not about the caller

This is the distinction that decides whether cross-boundary ABAC is sound.

principal_tags originate from identity configuration. Setting them requires permission to tag a role, or permission to pass session tags when assuming one. request_tags originate from the API call itself — the caller chooses the keys and values at the moment of the request, subject only to holding sts:TagGetWebIdentityToken.

A caller that can set a request tag can set any value it likes for that tag. If an external service grants elevated access when it sees environment: production and reads that from request_tags without further constraint, then every principal permitted to pass tags can claim to be production. The claim is authentic in the sense that AWS really did sign it; it simply does not mean what the verifier thinks it means.

The rule that follows is short. Use principal_tags for identity attributes that drive authorization. Use request_tags for per-request context that does not. A job identifier, a priority, a correlation value, a data classification label used for routing — these are legitimate uses. An environment name that unlocks write access is not, unless it is constrained as described next.

7.4 Constraining what a caller may assert

If a request tag must drive authorization, the assertion has to be bounded at issuance time, because nothing downstream can bound it. AWS lists two global condition keys as available for this call: aws:RequestTag/tag-key, which compares "the tag key-value pair that was passed in the request with the tag pair that you specify in the policy," and aws:TagKeys, which compares "the tag keys in a request with the keys that you specify in the policy."

Three controls compose into a defensible position:

  1. Withhold sts:TagGetWebIdentityToken from every principal that has no legitimate need to attach tags. This is the cheapest control and it eliminates the entire question for most principals.
  2. Bound the key set with aws:TagKeys so a principal cannot introduce keys the verifier was not designed for. Because aws:TagKeys is multivalued, this requires a set operator, and the ForAllValues reasoning from section 5.3 applies unchanged.
  3. Pin values with aws:RequestTag/tag-key for any tag that carries authorization weight, so the only value a given principal can assert is the correct one.

With those in place a request tag becomes as trustworthy as a principal tag, because the policy has removed the caller's freedom to choose. Without them, the verifier is trusting an unconstrained input that happens to arrive inside a signed envelope.

8. Auditing with CloudTrail

Two audit trails exist for every outbound token, and they are kept by different organizations. Making them join is a design decision that has to be made at build time.

8.1 Where the AWS-side record lands

AWS states that token requests are logged, and describes the resulting capability as "complete audit trails for security monitoring and compliance reporting." The product page phrases the same point as monitoring token usage using CloudTrail logs.

Where those events appear follows from two documented facts taken together. First, GetWebIdentityToken is not available on the STS global endpoint, so every call is made against a Regional endpoint. Second, for STS, "calls to regional endpoints, such as us-east-2.amazonaws.com, are logged in CloudTrail to their appropriate region" (AWS STS Regions and endpoints). The consequence is that token issuance events are distributed across the Regions where your workloads run, with no global endpoint catch-all.

That has a direct operational implication for anyone whose trail configuration grew up around the older STS behavior. The endpointType and awsServingRegion fields that AWS adds for global endpoint requests (Logging IAM and AWS STS API calls with AWS CloudTrail) do not help here, because there is no global endpoint path to distinguish. An organization trail, or per-Region coverage that genuinely spans every Region a workload might call from, is the requirement. The aggregation patterns for that are covered in Centralized Logging and Audit Architecture on AWS.

8.2 Correlating the two sides

The token carries jti, documented as a "unique identifier for this token instance." The external service can log it. On the AWS side you have a CloudTrail event with its own request identifier.

AWS does not document a field that ties a CloudTrail event to a specific jti. That is worth stating rather than assuming, because the assumption is natural and the consequence of it being wrong is an investigation that cannot answer the question it was convened to answer.

If a hard join between the two trails is a requirement, build it rather than hoping for it. The mechanism AWS provides is request_tags: a correlation value passed as a request tag appears in the token, so the same value can be written to your own application log at the moment of the call, and the external service can be asked to record it alongside its own decisions. This is a textbook legitimate use of request tags under the section 7.3 rule, because a correlation identifier carries no authorization weight.

8.3 What to watch for

Three signals are worth alerting on, and each one is cheap once the trail is in place:

  • Token requests with an audience that no policy intended. If issuing policies are written with ForAllValues as section 5.3 recommends, these appear as denials rather than as successful calls, which is the point.
  • Requests at the documented maximum lifetime of 3600 seconds from principals whose policies do not cap sts:DurationSeconds. This is less an attack signal than a finding: it identifies the principals where the cap was never applied.
  • SessionDurationEscalation errors. These indicate workloads requesting lifetimes their session cannot support, which is a design problem worth fixing before it becomes an outage.

Reviewing which principals actually hold sts:GetWebIdentityToken, and which of them have used it, is the kind of question IAM Access Analyzer Deep Dive exists to answer.

9. The Revocation Problem

This is the section that should change a design decision. Everything above describes controls that work. This one describes the control that does not exist, and what has to be true because of it.

Where each control acts on the life of an issued token
Where each control acts on the life of an issued token

9.1 Expiry is the only expiry

AWS documents the behavior of disabling the feature in one sentence, and it is the most consequential sentence in the entire feature's documentation: DisableOutboundWebIdentityFederation "does not affect tokens that were issued before the feature was disabled" (DisableOutboundWebIdentityFederation).

That is the account-level kill switch, and it does not reach a token that already exists. Nothing else in the documented surface does either. There is no revocation list, no introspection endpoint, no token invalidation API. The token is a signed assertion that stands on its own, and the only property that ever stops it being accepted is the exp claim inside it.

The reason is structural rather than an omission. A signed, publicly verifiable token is verifiable precisely because verification requires nothing from the issuer at verification time. That is what allows an on-premises application with no AWS connectivity to authenticate an AWS workload. The same property means the issuer has no channel through which to reach a token it has already signed.

9.2 What does not shorten an issued token

Being concrete about this is worthwhile, because each of these actions feels like revocation:

  • Removing sts:GetWebIdentityToken from the principal's policy. Stops the next token. Does nothing to the current one.
  • Detaching the policy, or deleting the role entirely. Same. The token asserts an identity that existed when it was signed; deleting the role does not unsign it.
  • Calling DisableOutboundWebIdentityFederation. Documented not to affect already-issued tokens.
  • Tightening sts:DurationSeconds or sts:IdentityTokenAudience. These are issuance-time conditions and are not evaluated again.
  • Rotating anything on the AWS side. AWS does not document a key rotation you can trigger, and a verifier caching the old key would continue to accept tokens signed with it until its cache expired anyway.

The mental model that survives contact with this list is: an AWS credential is checked continuously, and an outbound token is checked once. Every intuition carried over from IAM about tightening a policy and having it take effect immediately is false here.

9.3 Designing the lifetime

Given that the lifetime is the security control, the parameters matter. AWS documents a range of 60 to 3600 seconds with a default of 300, and states plainly: "We recommend shorter token lifetimes for increased security." The API reference adds the intended usage pattern — the token "is designed to be short-lived and should be used for proof of identity, then exchanged for credentials or short-lived tokens in the external service."

That last sentence is the design guidance most worth internalizing, because it tells you what the token is for. The token's lifetime does not need to cover the work. It needs to cover the exchange. If the external service issues its own session on successful verification, as AWS's own flow description says it does, then the AWS token exists only for the handshake and a 60-second lifetime is entirely reasonable. A one-hour token is appropriate only when the far side has no session concept and expects the token itself on every call.

Three rules make this concrete:

  1. Set the maximum in policy, not in code. sts:DurationSeconds with NumericLessThanEquals is the enforcement point. Code that requests 300 seconds today can request 3600 tomorrow with a one-line change and no review.
  2. Choose the lifetime from the exchange, not from the workload. A batch job that runs for six hours does not need a six-hour token; it needs a token per exchange, or one exchange and a long-lived session on the far side.
  3. Treat the lifetime as the incident window. Whatever number you choose is your answer to "how long is a leaked token useful," and it is the only answer you will have.

9.4 Cutting the trust instead

Because the token cannot be reached, the only control that operates inside the validity window lives on the verifier. That is not a workaround; it is the correct place for it, and it is why section 6.6's structural lesson matters.

Two mechanisms exist on the far side. The first is the trust configuration itself: removing an issuer from the allowlist, or removing a subject from a federated credential entry, causes every token matching it to fail verification immediately, including ones already in flight. The second is whatever session the external service issued after a successful verification — which, being the external service's own credential, is subject to the external service's own revocation.

The consequences for design are direct, and they are the reason to run this exercise before the first integration rather than after the fifth:

  • One role per integration. A shared role means revoking one integration revokes all of them, because the subject is what the far side matches on.
  • Know where the trust entry lives, per integration, before you need it. The revocation path runs through a system you may not administer, and discovering that during an incident is discovering it too late.
  • Write down the worst case. For every integration, the exposure of a leaked token is the token lifetime plus however long it takes a human to edit a trust entry in the external service. If that number is unacceptable, the lifetime is the parameter you can change.

10. Migration from Long-Lived Keys

The technical work is small. The sequencing is what determines whether the migration finishes or stalls with both mechanisms in place forever.

10.1 Inventory the keys, then sort by what is possible

Start from the credentials, not from the integrations. Every long-lived credential your AWS workloads present to an outside party is a candidate, and the first sort is on a question you cannot influence: does the external service support OIDC federation with a custom issuer? If it does not, no amount of AWS-side work helps, and the credential stays where it is under whatever rotation regime it has. Where the secrets live and how they rotate in the meantime is the subject of AWS Secrets Manager and Parameter Store Decision Guide.

For the ones that qualify, order by the product of blast radius and change cost: a broadly permissioned credential in a service whose trust configuration is a single API call goes first, and a narrowly scoped credential in a service that requires a support ticket goes last.

10.2 Establish the AWS side once

Enabling the feature is account-level and idempotent in effect if not in API behavior. Do it once, record the issuer URL from GetOutboundWebIdentityFederationInfo in whatever inventory your organization actually reads, and treat the value as opaque per section 3.4.

The reusable per-integration work is the role and the policy. One role per integration, with an identity policy granting sts:GetWebIdentityToken conditioned on the audience that specific service expects, a sts:DurationSeconds ceiling chosen per section 9.3, and a pinned sts:SigningAlgorithm. Grant sts:TagGetWebIdentityToken only where tags are actually needed.

10.3 Run both, then cut

The safe order has the old credential remaining valid throughout:

  1. Configure the trust on the external side, naming the issuer, the role ARN as subject including its path, and the audience the service requires.
  2. Deploy code that attempts token-based authentication and falls back to the stored credential on failure. Log which path was taken on every call.
  3. Watch the logs until the fallback path is unused across a full operational cycle — including whatever periodic job or seasonal peak exercises paths a quiet week does not.
  4. Remove the fallback code.
  5. Revoke the old credential at the external service, then delete it from the secret store. In that order. Deleting your copy first leaves a valid credential in existence that you can no longer identify.

Step 3 is where migrations actually fail, and the failure is always the same: the fallback path is never exercised in normal operation, so nobody notices it is load-bearing for one code path until the fallback is removed. Logging which path was taken is what converts that from a guess into an observation.

10.4 Plan the rollback before step 5

Between steps 1 and 5 rollback is trivial, because the old credential still works. After step 5 it is a new credential issuance in a system you may not control.

The failure mode worth rehearsing is the one this article has already named twice: a subject mismatch caused by an IAM path. If a role is recreated under a different path, or if an automation normalizes ARNs, the subject stops matching and every token is rejected — with an error on the far side that says nothing about paths. Rehearse the recovery once, on a low-value integration, so the first time you see that error it is not during an incident.

11. Failure Modes and Anti-Patterns

Each of these has appeared in this article as a consequence of something documented. Collected here, they are the review checklist.

Assuming the arrow points inward. Reading outbound federation as a variant of inbound leads to looking for an OIDC provider object in your account and a trust policy on a role, neither of which exists in this direction. The trust configuration is on the far side. This is the error that section 2 exists to prevent, and it costs an afternoon.

Not validating aud. The most consequential omission on the verifier side. Because a single request may name up to ten audiences, and because those all land in the aud claim, skipping this check means accepting tokens minted for entirely different services.

Using ForAnyValue where ForAllValues was meant. The condition matches if any one requested audience is permitted, which allows a token valid for audiences the policy never authorized. Both operators appear in AWS's own examples for this feature; the one that expresses "only these services" is ForAllValues.

Fetching the JWKS on every verification, or caching it forever. The first turns the issuer endpoint into a hard dependency of your request path, against AWS's explicit recommendation to cache. The second is a bet on a rotation interval AWS has never published. Bounded TTL plus rate-limited refetch on an unknown kid closes both.

Reading the algorithm from the token. The classic JWT weakness, and unnecessary here: AWS offers exactly two algorithms and lets you pin one with sts:SigningAlgorithm.

Treating request_tags as identity. They are set by the caller at request time. Any authorization rule reading them without aws:RequestTag or aws:TagKeys constraints at issuance is trusting an unconstrained input inside a signed envelope.

Requesting the maximum lifetime by default. 3600 seconds is the ceiling, not the recommendation, and since expiry is the only revocation that exists, the lifetime is the incident window. The intended pattern is a short token exchanged immediately for a session on the far side.

Believing that removing the permission revokes the token. It does not, and neither does deleting the role or disabling the feature. The only controls that operate inside the validity window are on the verifier.

Matching subjects by role name. Subjects are full role ARNs including IAM paths, as both AWS first-party integrations demonstrate. Name matching works until the first role under a path, which is usually a service-linked role.

Sharing one role across integrations. Makes revocation all-or-nothing, because the subject is the unit the far side matches and revokes on.

Constructing the issuer URL from a template. The label is generated at enable time and AWS has not published the format for the GovCloud (US) or China partitions. Read it from the API.

Broader IAM design mistakes and their root causes are collected in IAM Anti-Patterns.

12. Frequently Asked Questions

Is this the same thing as an OIDC identity provider in IAM?

No, and they point in opposite directions. An IAM OIDC identity provider object configures AWS to trust an external issuer so external workloads can assume AWS roles. Outbound identity federation makes your AWS account the issuer, so external services can trust your workloads. Section 2 covers the contrast in full.

Can I use the token to assume a role in another AWS account?

No. AWS states that tokens from GetWebIdentityToken cannot be used for OIDC federation into AWS via AssumeRoleWithWebIdentity. Cross-account access inside AWS continues to use role trust policies.

Which signing algorithm should I choose?

AWS offers ES384 and RS256 and gives direct guidance: ES384 for optimal security and performance, RS256 for broader compatibility with systems that do not support ECDSA. Check what the external service supports first, then pin your choice with sts:SigningAlgorithm so it cannot drift.

How long can a token live?

Between 60 and 3600 seconds, defaulting to 300 if you do not specify. AWS recommends shorter lifetimes, and section 9.3 argues the lifetime should be sized to the exchange rather than to the workload.

Can I revoke a token I already issued?

No. AWS documents that disabling the feature does not affect previously issued tokens, and there is no revocation API. The only controls that operate inside the validity window are on the verifier side — removing the issuer or subject from its trust configuration, or revoking whatever session it issued. Section 9 is entirely about this.

Do I need an IAM user for this?

No. AWS describes eligible callers as IAM principals such as roles and users, and the documented compute sources are EC2 instance profiles, Lambda execution roles, and other AWS compute. Roles are sufficient.

Why did my token request suddenly start failing near the end of a job?

Most likely SessionDurationEscalation. AWS returns it when the requested duration would extend past the expiry of the role session making the request. Request a shorter lifetime, or refresh the session. The original_session_exp claim exposes the same limit to the verifier.

Is the feature available in my Region?

AWS states availability in all commercial Regions, GovCloud (US) Regions, and China Regions. Note separately that GetWebIdentityToken is not available on the STS global endpoint, so calls must go to a Regional endpoint.

What is the difference between principal_tags and request_tags?

principal_tags come from identity configuration — tags on the role, or session tags passed at assume time, with session tags taking precedence when both exist. request_tags are chosen by the caller in the API call itself. Only the first is an identity attribute. Section 7.3 explains why merging them breaks authorization.

How do I correlate an AWS token issuance with an action in the external service?

AWS does not document a field linking a CloudTrail event to a specific jti, so build the correlation deliberately: pass a correlation value as a request tag, log it on your side at the moment of the call, and have the external service record it too. A correlation identifier carries no authorization weight, so this is a safe use of request tags.

Does the external service need network access to AWS?

It needs to reach the issuer URL to fetch the JWKS, and AWS recommends caching those keys rather than fetching per verification. It does not need AWS credentials, an AWS account, or any AWS SDK - which is what makes this workable for self-hosted applications.

What is the one thing to get right first?

Pin the issuer and validate aud. Issuer pinning is the root of the trust chain, and audience validation is what stops a token minted for somebody else from working on your service. Everything else in section 6 is refinement on top of those two.

13. Summary

The idea worth carrying away is that outbound identity federation replaces a stored secret with a signed assertion, and in doing so trades a revocation problem you know how to solve for one you cannot solve at all. A long-lived API key can be rotated and revoked at any moment; it just has to be stored, transmitted, and protected. An outbound token needs none of that protection, because possessing it grants nothing beyond what its claims assert to a party that chose to trust its issuer — but once signed, it is beyond reach until it expires.

The direction is the thing to fix first. Almost all federation material describes external identities entering AWS. This is AWS asserting identity outward: the issuer is your account, the trust configuration lives in the external service, and the secret that disappears is the one the external service would otherwise have given you. AWS closes the loop deliberately, stating that these tokens cannot be used for OIDC federation back into AWS.

On the AWS side the surface is small and well-bounded. One account-level enablement produces an issuer URL hosting OIDC discovery and JWKS endpoints. One API takes a required audience of up to ten values, a required signing algorithm, an optional duration between 60 and 3600 seconds, and up to 50 optional tags. Two actions and three condition keys decide who may mint what, and the choice between ForAnyValue and ForAllValues on the audience key is not stylistic — with up to ten audiences per request, only one of them expresses "these services and no others." The resulting token carries standard OIDC claims plus account, Region, organization, OU path, principal tags, and session context down to the calling Lambda function ARN. AWS attaches an explicit caveat that not all claims are present simultaneously, so absence must deny rather than default, and unknown claims must be ignored rather than rejected.

Verification is where security is decided, because the verifier is the only component that can refuse a token once it exists. Pin the issuer from an allowlist before fetching anything. Cache the key set with a bounded lifetime and refetch on an unknown kid, rate-limited. Supply the algorithm list yourself. Then check aud, sub, and exp, matching subjects as full role ARNs including IAM paths — as both AWS first-party integrations demonstrate by naming service-linked role ARNs as their subject. For attributes crossing the boundary, the line that matters is between principal_tags, which come from identity configuration and are safe to authorize on, and request_tags, which the caller chooses freely and are not, unless bounded at issuance with aws:TagKeys and aws:RequestTag.

Finally, the revocation limit shapes everything else. Removing the permission, deleting the role, and disabling the feature all leave issued tokens working; AWS documents the last of those explicitly. The lifetime is therefore the incident window, and the only control operating inside it is the verifier's trust configuration. That is the argument for one role per integration, for knowing where each trust entry lives before you need it, and for sizing the lifetime to the exchange rather than to the job.

14. References



References:
Tech Blog with curated related content

Written by Hidekazu Konishi