skip to content

Roles, STS & Temporary Credentials

Roles are identities nobody owns: you assume them through STS and get credentials that expire. I should be able to walk both halves — the trust policy that says who may assume, and the permission policy that says what they get.

part ofAWSoverview, primer and where to startread it →
on this pageshow

questions

5

In AWS, what is the difference between an IAM user with long-lived access keys and an IAM role assumed through AWS STS, and why do teams prefer the role?

level: juniorimportance: must knowfreq 80%

answer

  1. nobody owns a role
  2. credentials that expire on their own
  3. three parts, not two
  4. ASIA versus AKIA prefix
  5. swap identity, do not stack it

basics

~20 s

An IAM user's access keys are permanent static secrets belonging to one identity. A role owns no credentials: an allowed principal calls STS AssumeRole and receives a temporary access key, secret and session token that expire on their own.

solid answer

~50 s

An IAM user is a permanent identity whose access key pair (the ones beginning `AKIA`) stays valid until a human rotates or deletes it, so a leaked key is useful to an attacker indefinitely and rotation is a manual chore someone forgets. A role is an identity that carries permissions but no credentials of its own. A principal that the role's trust policy allows calls `sts:AssumeRole` and gets back three things — an access key ID beginning `ASIA`, a secret key, and a session token — together with an explicit expiration timestamp. The AWS SDKs refresh that set transparently, so nothing durable sits on disk. Assuming also produces a `RoleSessionName` that shows up in CloudTrail, so you can tell which human or workload acted, and it is the normal mechanism for cross-account access. The rule of thumb: workloads and humans get roles; long-lived keys survive only where nothing else is possible.

code

bash · 11 lines
bash
# Assume a role and use the three-part temporary credential
aws sts assume-role \
  --role-arn arn:aws:iam::111122223333:role/ReportsReader \
  --role-session-name alice-adhoc \
  --query 'Credentials' --output json

# All three parts are required, including the session token
export AWS_ACCESS_KEY_ID=ASIAEXAMPLEKEYID
export AWS_SECRET_ACCESS_KEY=examplesecret
export AWS_SESSION_TOKEN=exampletoken
aws sts get-caller-identity

go deeper

for a junior

Be able to say plainly that a role has no keys of its own and that STS hands you credentials with an expiry. Know that a temporary credential is three parts: access key, secret, and session token.

for a middle

Explain the mechanics: who may assume, what sts:AssumeRole returns, that the session takes the role's permissions rather than adding to the caller's, and how the SDK refreshes before expiry so nothing is stored.

for a senior

Show the operational judgment — where long-lived keys still legitimately exist, how you hunt for and retire them, how session naming gives you attribution in CloudTrail, and why revoking a live session is awkward.

for a principal

Own the tradeoff at fleet scale: setting a house rule that no new IAM user keys are issued, deciding the exceptions and their compensating controls, and weighing session lifetime against refresh complexity and audit needs.

## Two shapes of AWS credential Every call to an AWS API is signed with a credential, and AWS issues exactly two shapes of them. The first is a **long-term access key pair** belonging to an IAM user: an access key ID (they begin with `AKIA`) and a secret access key. It is created once, printed once, and remains valid until somebody explicitly deactivates or deletes it. The second is a **temporary security credential** minted by AWS STS (Security Token Service): an access key ID beginning with `ASIA`, a secret access key, **and a session token**, plus an expiration time after which all three are dead. An IAM **role** is the identity you assume to get the second shape. A role looks like a user in that it has permissions policies attached, but it has no password and no access keys. Nobody "owns" it. Instead it carries a trust policy naming who may assume it, and anyone who satisfies that policy can call STS and borrow the role's permissions for a bounded window. ## What AssumeRole actually returns ```bash aws sts assume-role \ --role-arn arn:aws:iam::111122223333:role/ReportsReader \ --role-session-name alice-adhoc ``` The response contains a `Credentials` block with `AccessKeyId`, `SecretAccessKey`, `SessionToken` and `Expiration`, and an `AssumedRoleUser` block with the session's ARN. All three credential parts are mandatory: temporary credentials are only accepted when the session token travels with them, as the `X-Amz-Security-Token` header on the signed request. Sending just the key and secret of a temporary credential produces an authentication failure, which is the single most common first-time mistake when people copy credentials by hand. ## Replacement, not addition A session created by assuming a role has **the role's** permissions. It does not keep the caller's. This surprises people who think of a role as a bag of extra rights bolted onto their user. If you can read S3 as yourself and assume a role that can only read DynamoDB, the assumed session cannot read S3 — you have swapped identities, not stacked them. (A session policy passed at assume time can narrow that further, never widen it.) ## Why expiry is the whole point The security argument is about the **exposure window**. A long-lived key that leaks into a git repository, a CI log, a laptop backup or a container image is a working credential the day it leaks and a working credential a year later. A temporary credential that leaks the same way is worthless within hours, usually within one hour, because that is the default session length. You have not made theft impossible; you have made the stolen thing perishable. The operational argument matters just as much. Long-lived keys must be distributed, stored and rotated, and every one of those steps is a place a secret gets written down. With roles, the credential is fetched at runtime by the SDK and refreshed before it expires, so there is no secret to store and no rotation calendar to maintain. The third argument is **attribution**. When you assume a role you supply a `RoleSessionName`, and that string appears in every CloudTrail event the session produces. A shared IAM user's key tells you only that "the deploy user" acted; a role session tells you which pipeline run or which engineer did. ## Revocation is the awkward part Because a temporary credential is self-contained, you cannot cancel it the way you delete an access key. Deleting or detaching the role's permissions policies stops *future* authorization, and in practice the standard emergency lever is to attach a policy that denies everything for sessions issued before a cutoff, keyed on `aws:TokenIssueTime`. It works, but it is clumsy — which is another reason short session lifetimes are the real control. ## Where long-lived keys still legitimately appear They have not vanished. A third-party tool that cannot federate, a legacy on-premises script, or a partner integration may still need an IAM user with keys. When you must, the discipline is: one user per purpose, no console password, minimum policy, and monitoring of last-used data so dormant keys get deleted. Everything else — anything running inside AWS, anything a human uses interactively — should be a role.

  • If a stolen temporary credential still works until it expires, what has short-lived credentialing actually bought you?
    A bounded exposure window. The attacker gets minutes-to-hours of the role's permissions instead of open-ended access, so an old leak in a log or an image is inert by the time anyone finds it. It also removes the stored secret entirely, which is where most credential leaks originate in the first place.
  • You paste an assumed role's access key ID and secret into environment variables and every call fails to authenticate. Why?
    Temporary credentials are a three-part set. Without `AWS_SESSION_TOKEN` — sent as the `X-Amz-Security-Token` header — AWS treats the request as presenting an unknown long-term key and rejects it. Copy all three values, or better, let the SDK assume the role itself.
  • How do you cut off a temporary session that is already in an attacker's hands?
    You cannot delete it like an access key. You revoke by policy: attach a deny to the role that is conditioned on `aws:TokenIssueTime` being earlier than now, which kills every session issued before that moment while letting new assumptions work. Detaching the role's permissions policies is the blunter alternative.

A long-lived access key is a cut house key you hand out and hope nobody copies. Assuming a role is a hotel keycard: issued on request, scoped to one room, and dead by checkout whether or not you return it.

saying these in an interview costs you the question

  • Calling a role a group of permissions attached to a user
  • Thinking an assumed session keeps the caller's own permissions too
  • Assuming temporary credentials can be deleted like an access key
  • Sending only the key and secret without the session token
  • Claiming rotating an IAM user key makes it equivalent to a role

context

open as a page

An IAM role carries two separate policy documents: a trust policy and one or more permissions policies. What does each control, and how do you tell from an AccessDenied error which one is at fault?

level: middleimportance: must knowfreq 70%

basics

~20 s

The trust policy is the role's resource policy: it names the principals allowed to call sts:AssumeRole on it. The permissions policies say what the resulting session may do. A denial on sts:AssumeRole points at the trust policy; any later denial points at the permissions policies.

open as a page

What does the `aws sts get-caller-identity` command return, and how do you read the ARN it prints when the caller is using an assumed role?

level: middleimportance: should knowfreq 48%

basics

~20 s

It returns the Account, UserId and Arn that AWS attributes to the credentials just used. For a role session the Arn has the form arn:aws:sts::<account>:assumed-role/<RoleName>/<SessionName>, naming the role and the session rather than any user.

open as a page

A SaaS vendor asks you to create an IAM role in your AWS account that its account can assume, and insists the trust policy pin an sts:ExternalId condition. What attack does that condition prevent, and who is supposed to choose the value?

level: seniorimportance: should knowfreq 38%

basics

~20 s

It stops one of the vendor's other customers from making the vendor use your role on their behalf. The vendor generates a unique ExternalId per customer and passes it on AssumeRole; your trust policy accepts only that value, so a role ARN alone is not enough to be acted on.

open as a page

A long-running batch job assumes an IAM role and then fails with an ExpiredToken error about an hour in, even though the role's MaxSessionDuration is set to 12 hours. What would you check?

level: seniorimportance: nice to knowfreq 32%

basics

~20 s

Check whether the job requested a longer DurationSeconds at all, whether it is role chaining — assuming a second role from the first role's credentials caps the session at one hour — and whether the credentials were captured once into environment variables so nothing can refresh them.

open as a page