AWS STS – Complete Guide

Bits Lovers
Written by Bits Lovers on
AWS STS – Complete Guide

AWS STS is the Security Token Service - an API that gives you temporary access to your AWS resources. Think of it as a way to borrow credentials for a limited time instead of creating permanent access keys.

What Changed Recently (2024-2026)

STS evolved significantly over the past couple of years:

  • AWS STS Regional endpoints became mandatory for most operations in 2024. Global STS endpoints (sts.amazonaws.com) still work but AWS strongly recommends regional endpoints for lower latency and better resilience. For example: sts.us-east-1.amazonaws.com instead of sts.amazonaws.com.
  • AWS STS with VPC Endpoints — PrivateLink support for STS allows assuming roles from within a VPC without the traffic leaving the AWS network. No more traversing the public internet for credential fetches.
  • IAM Roles Anywhere (GA 2022, matured significantly 2024) — allows workloads running outside AWS (on-premises servers, edge locations, other clouds) to obtain temporary AWS credentials using X.509 certificates. This eliminates long-lived access keys on hybrid workloads — a huge security win.
  • Session tagging in STSAssumeRole now propagates session tags (up to 50) and transitive tag keys, enabling fine-grained ABAC (Attribute-Based Access Control) across federated workloads. You can tag sessions with project, environment, or cost-center metadata.
  • Web Identity Federation — support for OIDC providers (including AWS Cognito, Auth0, Okta) via AssumeRoleWithWebIdentity is now the standard approach for mobile and web apps. The older Amazon Cognito-specific flow is being phased out in favor of direct OIDC integration.
  • AWS Config rules can now detect IAM role sessions approaching expiration, helping you catch stale credentials before they cause problems.
  • Credential expiration tracking — CloudTrail logs every STS call, making it straightforward to build dashboards showing active role assumption patterns.

What is AWS STS? How does it work?

The main idea behind STS is simple: you request temporary credentials, use them for a specific period, and then they automatically become invalid. This is different from IAM users, which have permanent access keys. If you need a deeper breakdown of how IAM users, groups, roles, and policies interact before diving into STS, the AWS IAM roles and policies guide covers the full evaluation model.

Here’s what you get with STS credentials that you don’t get with long-lived access keys:

  • Every request needs a session token alongside the temporary access key
  • AWS validates the session token on every call
  • Credentials expire after a set time — once they’re gone, AWS rejects any request using them
  • No need to embed long-term credentials in your application
  • You can grant access to resources without creating AWS identities for external users
  • Since credentials expire automatically, you don’t have to worry about rotating or revoking them

STS runs as a global service, so you can call it at https://sts.amazonaws.com. But — use a regional endpoint. More on this below.

How STS differs from IAM

One thing to note: you can create a VPC endpoint for STS to keep all your requests within the AWS network instead of going over the internet. This is especially important for security-sensitive workloads where you don’t want credential traffic leaving your VPC.

Latency considerations

STS is physically hosted in us-east-1 (North Virginia), but most regions have their own STS endpoints. If you’re noticing latency issues, try using a regional endpoint instead of the global one.

For example, if you’re in or near Frankfurt:

sts.eu-central-1.amazonaws.com

Not all regions have STS enabled by default — you’ll need to enable it if it’s not available in yours.

AWS CLI tip: set the environment variable AWS_STS_REGIONAL_ENDPOINTS=regional to force the AWS SDK and CLI to use regional endpoints instead of the global STS endpoint.

Who uses STS?

Mostly IAM users and enterprise users who authenticate through federation (like SAML or OIDC). Common real-world scenarios:

  • Cross-account access — Account A’s role is assumed by a user in Account B
  • CI/CD pipelines — Build agents assume a deployment role instead of having a long-lived access key
  • On-premises workloads — Servers outside AWS using IAM Roles Anywhere to get temporary credentials via X.509
  • Web and mobile apps — Federating via Cognito or an external OIDC provider
  • Service-to-service — ECS tasks, Lambda functions, and EC2 instances all assume roles automatically via instance profiles

How to call STS

AWS SDK

The SDK works with 12 languages:

  1. Swift
  2. Rust
  3. Ruby
  4. Python
  5. PHP
  6. Node.js
  7. .NET
  8. Kotlin
  9. JavaScript
  10. Java
  11. Go
  12. C++

On Windows, you can also use PowerShell.

HTTPS requests

You can call STS with plain HTTPS requests if you prefer not to use an SDK. You’ll need your Access Key, Secret Key, and the session token you got from STS. Add a header called X-Amz-Security-Token with your session token value.

AWS CLI

aws sts assume-role \
  --role-arn arn:aws:iam::123456789012:role/MyRole \
  --role-session-name my-session \
  --duration-seconds 3600

I’ll walk through a full example with S3 later in this post.

Getting temporary credentials with STS

Using AssumeRole

AssumeRole lets existing IAM users access AWS resources they don’t normally have permission for. Common scenarios include cross-account access or temporarily assuming elevated privileges (useful when you need MFA for certain operations).

You need active credentials to call this API.

AssumeRole example

Let’s walk through downloading an object from an S3 bucket using STS tokens.

First, create an IAM Role:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": [
                    "arn:aws:iam::123456789011:user/bits_lovers"
                ]
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

AWS STS - Assume Role Example - Trusted Entities

AWS STS – Assume Role Example – Trusted Entities

Now attach a policy to this role:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "s3:*",
            "Resource": "*"
        }
    ]
}

Copy S3 Object using AWS STS

Copy S3 Object using AWS STS

Using AssumeRole via CLI

Request the temporary token:

aws sts assume-role \
  --role-arn arn:aws:iam::123456789011:role/AllowS3 \
  --role-session-name dev

Replace the ARN with your actual role. You’ll get back something like this:

{
    "Credentials": {
        "AccessKeyId": "ASIAWPFXN1E7YYZPNVFI",
        "SecretAccessKey": "GE9wScakZ1mjCvdRidIYxbGgqy+PWtqnHYhaql0F",
        "SessionToken": "IQoJb3JpZ2luX2VjEBkaCXVzLWVsc3QtsSJIMEYCIQDj//7HD1woBOEIE5ZZMBjmMhrR5gX1zoeQCkqL...",
        "Expiration": "2026-04-05T13:00:00Z"
    },
    "AssumedRoleId": "AROAWPFXN1E7WLHSILNFG:dev"
}

Save these three values: AccessKeyId, SecretAccessKey, and SessionToken. You’ll need all three for your requests.

Export them as environment variables:

export AWS_ACCESS_KEY_ID=<AccessKeyId value>
export AWS_SECRET_ACCESS_KEY=<SecretAccessKey value>
export AWS_SESSION_TOKEN=<SessionToken value>

One common pitfall: if you forget to set AWS_SESSION_TOKEN, you’ll get an Access Denied error:

An error occurred (403) when calling the HeadObject operation: Forbidden

Downloading an S3 object

With the variables set, download your file:

aws s3 cp s3://bitslovers-bucket/accesslogs/aws-sts-with-s3.log .

Recovering a session token

If you lose your session token somehow, you can retrieve a new one using:

aws sts get-session-token

Note that when MFA is required, get-session-token returns credentials valid for only 15 minutes.

Advanced STS Patterns

AssumeRole with External ID (Cross-Account Access)

If you’re granting cross-account access to a third party, always use an external_id. This prevents the “confused deputy” attack where an unauthorized party tricks your system into letting them assume your role.

aws sts assume-role \
  --role-arn arn:aws:iam::987654321098:role/CrossAccountRole \
  --role-session-name cross-account-session \
  --external-id "my-unique-external-id-per-client" \
  --duration-seconds 900

Generate a unique external ID per client/account relationship. If an external ID is ever leaked, rotate it immediately.

AssumeRole with Session Tags (ABAC)

Session tags let you pass attributes through the AssumeRole chain, enabling Attribute-Based Access Control:

aws sts assume-role \
  --role-arn "arn:aws:iam::123456789012:role/DeveloperRole" \
  --role-session-name dev-session \
  --tags Key=Project,Value=Platform Key=Environment,Value=prod \
  --transitive-tag-keys Project \
  --duration-seconds 3600

Then in your role’s trust policy, you can use conditions like:

{
  "Condition": {
    "StringEquals": {
      "aws:RequestTag/Project": "Platform"
    }
  }
}

This lets you grant access based on tags rather than explicit ARNs — much more flexible at scale.

AssumeRoleWithWebIdentity (OIDC Federation)

For web and mobile apps, use OIDC federation instead of embedding long-lived credentials:

aws sts assume-role-with-web-identity \
  --role-arn "arn:aws:iam::123456789012:role/WebIdentityRole" \
  --role-session-name "webapp-session" \
  --web-identity-token file://token.json \
  --provider-id cognito-identity.amazonaws.com

This works with any OIDC provider — Cognito, Auth0, Okta, your own Keycloak instance. The token is short-lived, issued by the IdP, and exchanged for AWS credentials.

Get Caller Identity

The simplest STS call — useful for debugging which identity your current credentials belong to:

aws sts get-caller-identity
# Returns: Account, UserId, Arn

Decode Authorization Message

When AWS denies a request, the error response sometimes includes an encoded message with more details. You can decode it:

aws sts decode-authorization-message \
  --encoded-message "BASE64_ENCODED_MESSAGE"

IAM Roles Anywhere: Credentials for On-Premises Workloads

This is one of the most useful STS-related features that doesn’t get enough attention. IAM Roles Anywhere lets servers running outside AWS get temporary AWS credentials — no long-lived access keys needed.

Here’s how it works:

1. Set up a Trust Anchor (references your ACM Private CA):

aws iam create-trust-anchor \
  --name my-anchor \
  --trust-anchor-type ACM_PCA_ARN \
  --certificate-authority-arn arn:aws:acm-pca:us-east-1:123456789012:certificate-authority/id

2. Create a Profile (maps to IAM roles):

aws iam create-profile --name my-workload-profile

3. Attach a role trust policy for IAM Roles Anywhere:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "rolesanywhere.amazonaws.com"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "rolesanywhere:TrustAnchorArn": "arn:aws:rolesanywhere:us-east-1:123456789012:trust-anchor/ta-1",
          "rolesanywhere:ProfileArn": "arn:aws:rolesanywhere:us-east-1:123456789012:profile/p-1"
        }
      }
    }
  ]
}

4. Fetch credentials using aws-signing-helper:

./aws-signing-helper credential-process \
  --profile default \
  --role-arn arn:aws:iam::123456789012:role/MyRole \
  --trust-anchor-arn arn:aws:rolesanywhere:us-east-1:123456789012:trust-anchor/ta-1 \
  --profile-arn arn:aws:rolesanywhere:us-east-1:123456789012:profile/p-1

Credentials are valid for up to 12 hours (configurable). No long-lived keys on your server — just a X.509 certificate provisioned via your ACM PCA.

AWS CLI Profile-Based Role Assumption

The AWS CLI supports automatic role assumption via profiles. No SDK code needed:

# ~/.aws/config
[profile source]
region = us-east-1

[profile cross-account]
role_arn = arn:aws:iam::987654321098:role/CrossAccountRole
source_profile = source
external_id = my-external-id
region = us-east-1

Then just use the profile:

aws s3 ls --profile cross-account

The CLI automatically fetches and caches temporary credentials. Set credential_process or sso_start_url for more complex scenarios.

Using STS with Python

Here’s how to get temporary credentials using MFA and then list S3 buckets:

import boto3

def get_s3_buckets_session_token_mfa(mfa_sn, mfa_totp, sts_client):
    if mfa_sn is not None:
        response = sts_client.get_session_token(
            SerialNumber=mfa_sn, TokenCode=mfa_totp)
    else:
        response = sts_client.get_session_token()

    temp_credentials = response['Credentials']

    # Using client instead of resource for better control
    client = boto3.client(
        's3',
        aws_access_key_id=temp_credentials['AccessKeyId'],
        aws_secret_access_key=temp_credentials['SecretAccessKey'],
        aws_session_token=temp_credentials['SessionToken'])

    print("List of Buckets:")
    for bucket in client.list_buckets()['Buckets']:
        print(f"  - {bucket['Name']}")

This assumes you have an MFA device set up and know its serial number.

Federation support

STS also works with federation, which means you can give users access to AWS resources without creating IAM users for them. If your company uses single sign-on (SSO), authenticated employees can get temporary session tokens automatically and access whatever AWS resources they need.

SAML 2.0 federation via tools like aws-adfs, aws-saml, and HashiCorp Vault is well-established for enterprise SSO environments.

Credential Expiration

You control how long credentials last by passing DurationSeconds when calling STS. The minimum is 900 seconds (15 minutes), and the maximum depends on your role’s settings. By default, IAM roles allow a maximum session duration of 1 hour. You can increase this up to 12 hours per role.

# Short session for CI/CD (1 hour, the default)
--duration-seconds 3600

# Longer session for long-running operations (up to 12 hours)
--duration-seconds 43200

AWS STS - Maximum Session Duration

AWS STS – Maximum Session Duration

Common Gotchas and Pitfalls

Session token expiration. STS credentials expire. If you cache them, implement auto-refresh logic. Most AWS SDKs handle this automatically via the default credential provider chain — but if you’re manually caching credentials, expired tokens will cause silent failures.

Maximum session duration. The IAM Role’s MaxSessionDuration setting (default: 1 hour, max: 12 hours) limits what duration-seconds values work. If you pass --duration-seconds 43200 but the role’s max is 3600, it fails. Adjust via aws iam update-role.

MFA and AssumeRole. To assume a role with MFA enforcement, first call GetSessionToken with MFA, then use the session token to assume the role with transitiveTagKeys. You can’t call AssumeRole directly with an MFA code.

Role chaining. Assuming a role from within an already-assumed role session creates a new session with reduced permissions. More importantly: you cannot use DurationSeconds > 3600 when role chaining. This is an AWS security constraint.

External ID leakage. Treat the external_id as a secret. If it leaks, the other party should rotate it immediately — the same way you’d rotate a password.

Regional vs. Global STS endpoints. Some operations (e.g., GetSessionToken, GetFederationToken) only work at the global endpoint. AssumeRole works in all regions. Set AWS_STS_REGIONAL_ENDPOINTS=regional to standardize on regional endpoints.

Credential chain precedence. AWS SDK credential chain (in order): Environment variables → AWS credentials file → Web identity token → Container credentials (ECS) → Instance profile. Note that source_profile in AWS config overrides environment variables — which can be confusing if you expect env vars to win.

Auditing STS Calls

CloudTrail records all STS API calls — every AssumeRole, GetSessionToken, and GetCallerIdentity call. If CloudTrail is enabled in your account (and it should be), you have a complete audit trail of who assumed what role and when.

Set up CloudWatch alarms on unusual STS patterns: assume-role calls from unexpected IPs, roles being assumed outside business hours, or high volumes of assume-role calls.

Decoding authorization messages

When AWS denies a request, the error response sometimes includes an encoded message with more details. You can decode it:

aws sts decode-authorization-message \
  --encoded-message YOUR_BASE64_ENCODED_MESSAGE

This often contains policy evaluation details that explain exactly which condition in which policy caused the denial.

SAML 2.0 Federation for Enterprise SSO

For enterprise environments using Active Directory or an identity provider (IdP) like Okta, Azure AD, or Ping Identity, SAML 2.0 federation lets employees assume AWS roles via their corporate credentials. The flow:

  1. Employee authenticates to the corporate IdP
  2. IdP issues a SAML assertion (a signed XML document)
  3. AWS receives the assertion via AssumeRoleWithSAML
  4. AWS returns temporary credentials

This means employees never have long-lived AWS credentials. Their access is as temporary as their IdP session. When they log out or their AD account is deactivated, they lose AWS access automatically.

# The assume-role-with-saml call (typically handled by the IdP integration)
aws sts assume-role-with-saml \
  --role-arn arn:aws:iam::123456789012:role/DeveloperRole \
  --principal-arn arn:aws:iam::123456789012:saml-provider/MyCorpIdP \
  --saml-metadataocument file://saml-metadata.xml

Tools like aws-adfs (Python CLI) and aws-saml automate this on macOS and Linux, fetching a SAML assertion from your ADFS or Okta endpoint and writing credentials to ~/.aws/credentials. For organizations already running Okta or Azure AD, AWS IAM Identity Center (formerly AWS SSO) is the modern replacement — it provides a web-based portal and CLI integration without needing to configure SAML directly.

Cross-Account Access: A Practical Architecture

Cross-account access is one of the most common STS use cases in enterprise environments. A clean pattern:

Account A (Security/Admin account):
  - Owns all IAM roles
  - Centralizes permissions management
  - No workloads here

Account B (Production workload):
  - Has no IAM users
  - Developers assume roles from Account A
  - Clean separation: permissions live in one place

The role in Account A has a trust policy that allows specific principals from Account B:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::111111111111:root"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "Bool": {
          "aws:MultiFactorAuthPresent": "true"
        }
      }
    }
  ]
}

The MFA condition means users must have authenticated with an MFA device before assuming this role — a good baseline practice for any role with elevated privileges.

Then in Account B, developers have a minimal IAM policy allowing them to assume specific roles in Account A:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "sts:AssumeRole",
      "Resource": "arn:aws:iam::111111111111:role/DeveloperRole"
    }
  ]
}

This is cleaner than managing IAM users in every account. When someone leaves the company, you disable their account in the IdP — no hunting for credentials scattered across dozens of accounts.

IAM Identity Center vs. Manual STS

AWS IAM Identity Center (formerly AWS SSO) is the modern replacement for manually configuring SAML federation. It provides:

  • A centralized portal for accessing all your AWS accounts
  • Built-in permission sets (predefined IAM role collections)
  • Integration with external IdPs (Okta, Azure AD, Google Workspace)
  • CLI access via aws sso login --profile <name>

For new environments, IAM Identity Center is the recommended approach. It handles the STS calls behind the scenes, manages permission sets, and provides a cleaner administrative experience than managing trust policies manually.

However, IAM Identity Center still uses STS under the hood. Every time an IAM Identity Center user accesses an account, STS AssumeRole fires. CloudTrail logs it. The same STS best practices (session duration limits, MFA enforcement, least privilege on roles) all still apply.

FAQ

Q: How long do STS credentials last? The default minimum is 15 minutes and maximum is 12 hours. For CI/CD pipelines, 1 hour (3600 seconds) is usually the right balance. For long-running operations like data migrations or overnight batch jobs, you can go up to 12 hours by increasing MaxSessionDuration on the IAM role.

Q: Can I assume a role from within another assumed role session? Yes — this is called role chaining. Be aware of two constraints: the maximum session duration when chaining is 1 hour regardless of the role’s MaxSessionDuration, and the chained session loses some of the original session’s attributes. For most CI/CD use cases this is fine; for long-running tasks it’s a limitation to plan around.

Q: What’s the difference between GetSessionToken and AssumeRole? GetSessionToken returns credentials for the calling IAM user or federated user — it doesn’t grant new permissions, it just wraps existing permissions in a temporary credential. AssumeRole returns credentials for a different IAM role, granting whatever permissions that role allows. Use GetSessionToken when you just need to add MFA to your existing credentials. Use AssumeRole when you need to switch to a different permission set entirely.

Q: Does STS work with VPC Endpoints? Yes. Create a VPC Interface Endpoint for sts.us-east-1.amazonaws.com (regional) or sts.amazonaws.com (global) and STS calls from EC2 instances stay within the AWS network. This is especially important for security-sensitive workloads — you don’t want credential requests traversing the public internet.

Q: How do I audit STS usage? Enable CloudTrail on all regions (or use an organization-wide trail). Every AssumeRole, GetSessionToken, GetFederationToken, and GetCallerIdentity call is logged with the ARN of the assumed role, the ARN of the principal that assumed it, the source IP, and the timestamp. Set CloudWatch alarms on unusual patterns — assume-role calls from unfamiliar IPs, roles assumed outside business hours, or sudden spikes in assume-role volume.

Q: What’s the difference between STS and IAM Roles Anywhere? STS is the underlying API that generates temporary credentials. IAM Roles Anywhere is a feature built on top of STS that lets workloads outside AWS (on-premises servers, edge devices) assume roles using X.509 certificates instead of long-lived access keys. Inside AWS, you typically use instance profiles and IAM roles directly. IAM Roles Anywhere is the bridge for hybrid environments.

For more on AWS IAM, the AWS NAT Gateway guide covers VPC endpoint patterns that eliminate the need for NAT Gateway in many scenarios. The terraform and Ansible post covers how STS credentials flow through CI/CD pipelines using IAM roles.

Bits Lovers

Bits Lovers

Professional writer and blogger. Focus on Cloud Computing.

Comments

comments powered by Disqus