If you juggle a lot of AWS accounts through IAM Identity Center, you know the ritual. You sit down, run a command, and get slapped with this:
aws: [ERROR]: Error when retrieving token from sso: Token has expired and refresh failed
So you type aws sso login again. And again tomorrow. And again after lunch.
Here's the thing I figured out the hard way: if, just like me, your config file was initiated years ago, then maybe your config is still using the old format. Back then the AWS CLI wrote SSO settings inline into every single profile, and that style never really opted into refresh tokens the way the modern one does. The file kept working, so I never touched it, and I paid for that with a login prompt several times a day.
If you get hit by frequent login requests, most of the time this isn't AWS being annoying. It's either a config that never asks for refresh tokens, or your org making it painful for you. Let's fix both.
The two tokens you need to understand
There isn't a single "session" behind aws sso. There are two tokens, they have very different lifetimes, and almost every "why did I get logged out?" question comes from mixing them up.
| Token | Typical validity | Configurable? | What it does |
|---|---|---|---|
| Access token | ~8 hours | No (fixed by AWS OIDC) | The short-lived token the CLI presents to get role credentials |
| Refresh token | Up to your sign-in session duration (max 90 days) | Yes, in Identity Center settings | Silently mints a new access token when the old one expires |
The access token expiring is completely normal. It happens roughly every 8 hours and you're not supposed to notice, because the CLI is meant to take the refresh token and quietly exchange it for a new access token behind your back.
If you keep having to log in by hand, it usually comes down to one of three things.
- Either your cached session has no refresh token at all, which is a config and scope problem you can fix.
- Or your sign-in session duration is short, so the refresh token itself dies overnight.
- Or a single refresh just failed for a transient reason, like being offline, behind a VPN, or with the laptop asleep at the wrong moment.
Fix number one: use a shared sso-session block
This is the change that matters most. Adopt the modern [sso-session] construct in ~/.aws/config. It's what makes your logins refresh-token capable, and as a bonus it lets one aws sso login cover every profile at once.
Here is the old, painful style, with the SSO settings copied into each profile:
[profile my-account]
sso_start_url = https://d-xxxxxxxxxx.awsapps.com/start/
sso_region = eu-west-1
sso_account_id = 123456789012
sso_role_name = AdministratorAccess
region = eu-west-1
Repeating sso_start_url and sso_region in every profile is the legacy token-provider style. It's verbose, it drifts over time, and it doesn't share a refreshable session across profiles.
Here is the modern style, where you declare the session once and point profiles at it:
# Declare the SSO session ONCE. This is the refresh-capable part.
[sso-session sso]
sso_start_url = https://d-xxxxxxxxxx.awsapps.com/start/
sso_region = eu-west-1
sso_registration_scopes = sso:account:access
[default]
region = us-east-1
[profile account-one]
sso_session = sso
sso_account_id = 111111111111
sso_role_name = AdministratorAccess
region = eu-west-1
[profile account-two]
sso_session = sso
sso_account_id = 222222222222
sso_role_name = AdministratorAccess
region = eu-west-1
A few reasons this is simply better.
- One login now covers all your profiles, because
aws sso login --profile account-onerefreshes the token for everything that referencessso_session = sso. Thesso_registration_scopes = sso:account:accessline is the piece that makes the client registration ask for a refresh grant in the first place; without it you can end up holding only an 8-hour access token with nothing to refresh from, which is exactly the "refresh failed" trap. - You also get a single source of truth, so changing the start URL or region is a one-line edit instead of a find-and-replace across dozens of profiles. The only requirement is AWS CLI v2, which you almost certainly already run.
Assume-role and chained profiles sit happily on top of this. Just point their source_profile at an SSO profile that uses the shared session:
[profile prod-admin]
role_arn = arn:aws:iam::111122223333:role/MyAdminRole
role_session_name = me
source_profile = account-one
Fix number two: raise the sign-in session duration
Even with a perfect config, if your sign-in session duration is stuck at the 8-hour default, the refresh token can't survive the night. That setting is the real ceiling on how long a refresh token stays valid.
It's a console-only, org-wide parameter. There is no public API, CLI, CloudFormation, or Terraform surface for it, so this is one you have to click through yourself. Open the console, go to IAM Identity Center in your instance's home account and region, then Settings, then the Authentication tab, then Session settings, and edit the maximum session duration. You'll see separate values for user interactive sessions, user background sessions, and CLI or tool sessions. The maximum is 90 days.
One warning worth taking seriously: this is an org-wide policy. It affects every user in the Identity Center instance, not just you. Raising it trades a little security posture for a lot fewer logins, so pick a number your security team is comfortable with. Seven days is a common sweet spot, and 90 days is the ceiling. If you want your organisation to raise its duration, be prepared to make a strong case for Security people (mentioning that IDC tokens can be revoked is a good start for a security conversation).
There's a second, different duration that people confuse with this one: the permission set session, which controls how long the assumed-role credentials last (often 12 hours). That one you can read from the CLI:
INSTANCE_ARN=$(aws sso-admin list-instances \
--query 'Instances[0].InstanceArn' --output text --region eu-west-1)
aws sso-admin describe-permission-set \
--instance-arn "$INSTANCE_ARN" \
--permission-set-arn <ps-arn> \
--query 'PermissionSet.{Name:Name,SessionDuration:SessionDuration}' \
--region eu-west-1
Just remember it's a different clock. It governs role-credential lifetime, not SSO refresh.
How refresh timing actually works
A common assumption is that you have to refresh "every 8 hours." You don't, and there's no background daemon doing it for you either. Refresh is lazy and on demand. The CLI only refreshes when you run a command that needs credentials. If the access token is still valid it just uses it. If it's expired, it silently swaps the refresh token for a new access token and carries on. And each successful refresh usually rotates the refresh token, sliding the window forward.
In practice that means you should use any profile well within your session ceiling, and daily use is more than enough. During a normal working week you'll basically never type aws sso login. The first command each morning triggers a silent refresh, and everything after it rides the fresh 8-hour access token.
Keeping the token cache tidy
The CLI caches tokens under ~/.aws/sso/cache/. Over the months this fills up with stale files from old client registrations. They rarely cause trouble, but a cluttered cache can occasionally make the CLI match an older registration than you expect. It's safe to remove the expired token files as long as you leave the active one for your current session in place. This snippet shows what's there and when each entry expires, without printing any secrets:
for f in ~/.aws/sso/cache/*.json; do
python3 -c "import json,sys;
try: d=json.load(open('$f'))
except: sys.exit()
print('$f', '->', d.get('expiresAt'), '| refresh:', 'refreshToken' in d)"
done
To sum up
The access token lasts about 8 hours and you can't change that. The refresh token lasts up to 90 days depending on your Identity Center setting, and that's the one that actually keeps you logged in. Move to a shared [sso-session] block with sso_registration_scopes = sso:account:access so refresh tokens get issued and one login covers everything.
Raise the maximum session duration in IAM Identity Center under Settings and Authentication, remembering it's console-only and org-wide. Don't bother scheduling refreshes, because the CLI renews lazily on your next command and daily use is plenty.
Fix the config once, set a sane session duration, and aws sso login turns into something you type a handful of times a year instead of a handful of times a day.
Top comments (1)
Great explanation! I didn’t realize the shared
sso-sessioncould make AWS SSO so much less annoying. Definitely worth checking your config if you’re logging in multiple times a day. Also, CodeCan.net has some useful developer resources worth checking out.