
AWS STS: AssumeRole, Federation, and Temporary Credentials
An examination of AWS STS capabilities including AssumeRole, federation, and temporary credentials for secure cross-account access.
The Trust Broker: How AWS STS Replaces Static Keys
AWS Security Token Service (STS) replaces permanent static keys with time-bound temporary credentials, significantly reducing the window of exposure if secrets are compromised. Instead of trusting a user indefinitely, STS issues a token with an explicit expiration and a strictly defined scope. This mechanism serves as the backbone of cross-account access and cloud federation, enabling external entities to operate securely without holding master keys.
The AssumeRole Mechanism: Scoped Delegation
The most common entry point into STS is the AssumeRole API call. This mechanism allows a principal in one AWS account (Account A) to assume a role in another account (Account B). The security boundary here is not defined by the user's identity, but by the trust relationship established between the two accounts.
Consider Alice, a developer in Account A (111122223333), who needs to upload logs to an S3 bucket in Account B (444455556666). Alice does not have an IAM user in Account B. Instead, Account B has an IAM Role named LogDeliveryRole. This role contains a trust policy that explicitly states Account A is allowed to assume it.
When Alice's application calls AssumeRole, it sends a request containing the RoleArn of LogDeliveryRole and optionally a Policy document to the STS endpoint in Account B. STS validates the request against two critical conditions:
- Does the calling principal (Alice) have permission to call
sts:AssumeRoleon the target role? - Does the target role's trust policy allow the calling principal (or its parent account) to assume it?
If valid, STS generates a temporary security token. This token is not a new password; it is a signed JWT-like structure containing the AccessKeyId, SecretAccessKey, and SessionToken. Crucially, the SessionToken is required for all subsequent API calls made with these credentials. Without it, the request fails.
# Example: Requesting a temporary session
aws sts assume-role \
--role-arn arn:aws:iam::444455556666:role/LogDeliveryRole \
--role-session-name AliceLogUploadSession \
--duration-seconds 3600The response includes a Credentials object with an Expiration field. This timestamp is absolute UTC time. The SDKs handle the rotation automatically, but the underlying mechanism relies on the server rejecting any request where the current time exceeds the Expiration timestamp.
Federation: External Identities Without IAM Users
While AssumeRole is for AWS-to-AWS delegation, AssumeRoleWithWebIdentity and AssumeRoleWithSAML handle federation. This is the mechanism that allows users authenticated by Google, Facebook, or an on-premises Active Directory (via SAML) to access AWS resources without creating an IAM user for them.
In a web federation scenario, a user logs into your application using Google OAuth. Google returns an ID token (a JWT). Your backend application then calls AssumeRoleWithWebIdentity with this token. STS validates the token signature using the public key of the identity provider (IdP) and extracts the sub (subject) claim.
The critical difference here is the source of truth. In AssumeRole, the source of truth is the AWS IAM policy. In AssumeRoleWithWebIdentity, the source of truth is the external IdP's assertion. STS maps the external sub claim to the Principal in the role's trust policy.
# Example: Assuming a role via a web identity token
aws sts assume-role-with-web-identity \
--role-arn arn:aws:iam::444455556666:role/FederatedWebRole \
--role-session-name WebSession \
--web-identity-token <ID_TOKEN_FROM_GOOGLE> \
--duration-seconds 3600This mechanism enables "Just-in-Time" access. If a user leaves your organization, you simply revoke their identity at the IdP. They immediately lose access to AWS because the next token they present will be invalid or revoked, without you needing to delete an IAM user in AWS. This reduces the attack surface by eliminating static credentials for external users entirely.
The Lifecycle of a Temporary Credential
Temporary credentials are not just "shorter" versions of static keys; they operate on a different lifecycle model that forces rotation. Every STS credential has a SessionToken and an Expiration timestamp.
When an application uses these credentials, the AWS SDK maintains a local cache of the credentials. It does not call STS before every API request. Instead, the SDK checks the cached Expiration time only when a token is about to expire or has already expired (typically within a 5-minute buffer). At that specific moment, the SDK silently calls sts:AssumeRole (or sts:GetSessionToken) again to fetch a fresh set of credentials. This is why you rarely see "expired token" errors in production applications that use the official SDKs.
However, if you are using raw HTTP clients or legacy scripts that do not implement this rotation logic, you must manually manage the token. A common failure mode occurs when a long-running process (like a batch job) holds a token for 1 hour, but the role was configured with a 1-hour duration. When the job tries to run at minute 61, the token is invalid, and the service returns a 403 Forbidden error with the message The security token included in the request is expired.
The duration is capped based on the role configuration. For AssumeRole, the maximum duration is 12 hours (43200 seconds). For AssumeRoleWithSAML and AssumeRoleWithWebIdentity, the default is 1 hour, but this can be increased up to 12 hours if the role's MaxSessionDuration attribute allows it.
Common Pitfalls
Misconfiguring trust policies is the most frequent source of STS failures and security gaps.
- Omitting
ExternalId: In cross-account scenarios, failing to include anExternalIdin the trust policy is a critical error. Without it, any user in the trusted account who knows the Role ARN can assume the role, turning a specific trust relationship into a global one. This opens the door to lateral movement if a single account is compromised. - Incorrect
MaxSessionDuration: Setting theMaxSessionDurationattribute on a role too high (e.g., 12 hours) when the operational requirement is only 1 hour increases the blast radius. If a token is leaked, the attacker has access for a much longer window than necessary. - Overly Permissive Principals: Using
*or overly broad account ranges in thePrincipalfield of a trust policy can inadvertently grant access to unintended entities. Always specify the exact account ID or user ARN required.
Trust Policies and Cross-Account Dependencies
The security of the entire STS model rests on the Trust Policy attached to the IAM Role. This is a JSON policy that defines who can assume the role. It is distinct from the Permission Policy (which defines what the role can do).
A typical trust policy looks like this:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111122223333:root"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "secure-external-id-123"
}
}
}
]
}Notice the Condition block. In cross-account scenarios, this is the primary defense against lateral movement. Without the ExternalId, an attacker who compromises a user in Account A could potentially assume the role in Account B if they know the Role ARN. By requiring a unique ExternalId, you ensure that only a specific, trusted entity (like a specific AWS SSO configuration or a specific partner account) can initiate the assumption.
Many teams treat ExternalId as optional, but in any multi-tenant or cross-account architecture, omitting it is a critical misconfiguration. It turns a specific trust relationship into a global one.
Furthermore, the Principal can be a specific user, an entire account (arn:aws:iam::...:root), or even an AWS service (like lambda.amazonaws.com). When a service assumes a role, it does not require a password; the service itself is the principal. This allows a Lambda function in Account A to write to an S3 bucket in Account B simply by having the trust policy allow lambda.amazonaws.com to assume the role.
Practical Takeaways
- Prefer Temporary Credentials: Always use STS
AssumeRoleor federation over long-lived access keys to limit the impact of credential leaks. - Enforce
ExternalId: Never rely solely on account-level trust; always require a uniqueExternalIdfor cross-account assumptions to prevent unauthorized access. - Trust SDKs for Rotation: Rely on official AWS SDKs to handle credential caching and automatic renewal; avoid manual token management unless absolutely necessary.
- Align Durations: Set
MaxSessionDurationto the minimum time required for your workload to reduce the window of opportunity for attackers.
FAQ
Q: What is the maximum duration for a temporary session?
A: The maximum duration for AssumeRole is 12 hours (43,200 seconds). For AssumeRoleWithWebIdentity and AssumeRoleWithSAML, the default is 1 hour, but this can be extended up to 12 hours if the role's MaxSessionDuration attribute permits it.
Q: Can I use STS to access resources in a different AWS region? A: Yes. STS is a global service, but the temporary credentials generated are valid for the region where the API call was made, or can be used globally depending on the resource permissions. The session token itself is valid across regions unless restricted by specific IAM policies.
Q: How does AWS SSO integrate with STS?
A: AWS SSO acts as an identity provider. When a user logs in via SSO, it triggers an AssumeRoleWithSAML flow. SSO passes the user's identity and group memberships as assertions to STS, which then issues temporary credentials mapped to the appropriate IAM roles.
Q: Why do I get a "SecurityTokenIncludedInRequestIsExpired" error?
A: This error occurs when the SessionToken embedded in your request has passed its Expiration timestamp. This usually happens if you are using a script or client that does not automatically refresh credentials before they expire.
Conclusion: The Shift to Ephemeral Security
STS transforms AWS security from a static perimeter model to a dynamic, context-aware model. By forcing the use of temporary credentials, it limits the blast radius of any leaked key to a specific time window and a specific role. Whether through AssumeRole for internal delegation or AssumeRoleWithWebIdentity for external users, the mechanism remains the same: validate the request, issue a time-bound token, and enforce expiration.
The tradeoff is complexity in implementation. You must ensure your applications rotate credentials correctly and that your trust policies are granular enough to prevent unauthorized assumption. However, the security benefit of eliminating long-lived static keys for cross-account access is non-negotiable in modern cloud architecture. As you scale your infrastructure, relying on STS is not just a best practice; it is the only viable way to manage access securely across multiple accounts and external identities.
Related posts
Integrating SAML with AWS IAM Federated Access
This guide covers integrating SAML with AWS IAM for federated access, explaining IAM roles, AWS SSO, and STS usage.
Implementing AWS IAM Identity Center: From Legacy SSO to Federated Control
A guide to implementing AWS IAM Identity Center for federated access, multi-account management, and SCIM provisioning.