You only need to call`aws sso login` once for all your profiles

I’ve seen some confusion around the AWS CLI v2 command aws sso login. In particular, suppose you have an ~/.aws/config that looks like the following:

[profile AcctA-Role1]
sso_start_url = https://foo.awsapps.com/start
sso_region = us-east-2
sso_account_id = 111122223333
sso_role_name = Role1
[profile AcctB-Role2]
sso_start_url = https://foo.awsapps.com/start
sso_region = us-east-2
sso_account_id = 777788889999
sso_role_name = Role2
[profile AcctB-Role1]
sso_start_url = https://foo.awsapps.com/start
sso_region = us-east-2
sso_account_id = 777788889999
sso_role_name = Role1

(if you’ve been putting this in ~/.aws/credentials, read my explainer about AWS config files)

I have seen people get the impression that if they want to use Role1 in account A (111122223333), they need to call aws sso login --profile AcctA-Role1, and that if they subsequently want to use Role2 in account B (777788889999), they need to then call aws sso login --profile AcctB-Role2. However, this isn’t how AWS SSO or the CLI works!

Let’s start with AWS SSO. With AWS SSO, you log in as a human, and the token you receive that represent you as a logged-in human can become any of the various IAM roles in the various accounts that they have been granted access to. Even though you give a profile in the input to aws sso login, the resulting session is not scoped to the account and role in that profile, by design. In the browser, when you log in through your AWS SSO start URL (in this case, https://foo.awsapps.com/start), you are logged in for all your access, and can find the account and role and clicking through it to get into the AWS web console. When you want to switch between accounts and roles, you don’t need to log in again; you go back to the start page and click through into the console as a different account and role. In the CLI, it works the same way, you log in as a human using aws sso login, and this permits you to use any of the accounts and roles you have access to without logging in again.

A profile configured in ~/.aws/config that is not using IAM User credentials does not represent a particular human. It represents a role that any human with the right permissions may assume, and should be named accordingly. The profile is about the activities those credentials enable, not who may be performing those activities. A profile configured for AWS SSO includes the AWS SSO instance (the start URL and the region that AWS SSO is configured in), the account, and the role in that account to use, when logged in through AWS SSO as some human (and will work only if that human has been granted access to that account and role through AWS SSO).

When you call aws sso login, the AWS CLI pops up a browser window (or, on a headless machine, you open the browser on a different machine using the URL and code printed out by the CLI) that signs you in, either through username and password in AWS SSO itself or a federated identity provider like Okta. At that point, you now have cookies in your browser representing the logged-in sessions with AWS SSO and separately with the federated identity provider. The browser prompts you to grant credentials to the CLI, and when you click OK, the aws sso login command receives an SSO token from AWS SSO that represents a session for you, the human. This token is cached in ~/.aws/sso/cache in a file that represents the AWS SSO instance (that is, there is only one file per start URL). If a different human using the same local machine user — that is, with the same home directory — the cached SSO token for their session will be stored with the same file name. Again, this is the same with the browser: if they sign in to AWS SSO, the cookies for their session will replace the cookies representing your session.

What’s critical to note is that aws sso login does not deal with accounts or roles at all! You can see the code here; it uses the start URL and SSO region only. After calling aws sso login --profile AcctA-Role1, there are no credentials for Role1 in account A on your system. But if I’m already signed in, why does calling aws sso login --profile AcctB-Role2 after calling aws sso login --profile AcctA-Role1 pop up a browser window to sign in again?

It turns out that the AWS CLI forces a refresh of the SSO token every time aws sso login, regardless of whether a valid token has been cached. This is documented in the command’s help, and you can see it in the code here.

Ok, so we’ve covered how you get an SSO token that represent you as a human, which gives you access to accounts and roles. But where do the credentials for a given account and role actually come from?

Let’s say you’ve done aws sso login --profile AcctA-Role1. What happens when you do aws sts get-caller-identity?¹ If you don’t have a default profile configured (as I recommend) nor have set the profile through some other mechanism like the AWS_PROFILE environment variable, nothing will happen! It will print out Unable to locate credentials. If instead you do aws sts get-caller-identity --profile AcctA-Role1 , it will tell you that you’re using Role1 in 111122223333, i.e., account A, which means you’ve got valid credentials to that role in that account. But now, if you do aws sts get-caller-identity --profile AcctB-Role2, it will tell you that you indeed have valid credentials to Role2 in account 777788889999, i.e., account B. But didn’t you log in to AcctA-Role1?

aws sso login --profile AcctA-Role1 does not result in a session scoped to AcctA-Role1, it results in a session scoped to all the access you have. We talked above about how aws sso login results in a cached SSO token that represents you as a human, nothing related to the account and role. What’s happening when you attempt to use a particular account and role, using a profile with AWS SSO configuration, is that the SSO token that’s been cached is used to retrieve AWS credentials (the familiar access key id, secret key, and session token) for the particular account and role specified in that profile. Since the SSO token represents you as a human, it can be used to retrieve credentials for any of the account+roles you have access to, just like you can go into any of those account+roles through the start page in the browser. This credential retrieval occurs at the time of use, not during the login step. So when you call aws sts get-caller-identity --profile AcctA-Role1, it loads the cached SSO token and uses it to retrieve credentials for Role1 in account A, and then caches these credentials in ~/.aws/cli/cache. This means if you call aws sts get-caller-identity --profile AcctA-Role1 again, it won’t load the SSO token or retrieve new credentials, it will re-use the cached credentials. However, if you call aws sts get-caller-identity --profile AcctB-Role2, there aren’t cached credentials for that account+role, so it will load the SSO token, go get the credentials, and then cache those separately (the credential cache exists per account+role). And if you call aws sts get-caller-identity --profile AcctB-Role1, because the cache is per account+role — that is, per role ARN — the cache for AcctA-Role1 does not apply for AcctB-Role1².

Similarly, if you’re using an SDK³ configured to use a particular profile using AWS SSO configuration (e.g., using a boto3 session), when an API call is made, the SDK loads the SSO token that was cached by aws sso login and uses it to retrieve the credentials for the account+role in the profile. It caches those credential in memory, not on disk. Note that the SDKs do not provide any functionality to log the user in and ensure a valid SSO token exists, but my library aws_sso_lib can help with that⁴.

So if aws sso login doesn’t result in a session scoped to the the specific account+role in the input profile, why does it take a profile as its input anyway? Unfortunately, the only configuration the AWS CLI understands is profiles, so the only way to tell it the start URL and SSO region is through a profile. Worse, it doesn’t even work if you give it a profile that only has sso_start_url and sso_region, because the validation requires sso_account_id and sso_role_name to be there! I have a GitHub issue open for that. Frustratingly, aws configure sso is actually able to scan your ~/.aws/config and see all the start URLs and SSO regions in there, and present them to you to select from, but this capability has not been used in aws sso login.

Because of this, I have a feature in my CLI utility for AWS SSO, aws-sso-util to make this easier. The aws-sso-util login function will scan your ~/.aws/config and if only one AWS SSO instance is found, it’ll just use that for login. If more than one is found, you can input a part of the start URL to select it. Or (this is what I recommend) you can just set environment variables to tell it the SSO instance config to use by default. The docs for it are here. The result of aws-sso-util login is identical to aws sso login, you get an SSO token cached in the same place. Unlike aws sso login, though, it will check for the cached token and not attempt a log in if the cached token appears valid, unless you specify --force.

Note that if you want to switch the human who is logged in for the AWS CLI, you can’t simply do aws sso login again, nor will aws sso logout suffice if you’re using federated identity. The reason for this is that while that token represents your session, the actual persistent session is the cookies set during the login process in the browser. The previous user should call aws sso logout, because this invalidates their SSO token with AWS SSO, as well as clearing both the SSO token cache and the CLI credentials cache. But calling aws sso login again will re-use the existing browser cookies and sign that user back in. So you have to go to your AWS SSO start URL, sign out there, and if you’re using a federated identity provider, you have to sign out of that as well, and then log in again through the CLI (or aws-sso-util login). This is, more or less, by design, even though it’s inconvenient if it comes up (e.g., on a shared computer). And in this situation, there may be profiles that are configured for a given account+role that one user has access to that another does not. In this case, when the latter user attempts to make an API call using the configuration from that profile, when the CLI/SDK attempts to retrieve the credentials using the SSO token, they will get an access denied error⁵.

AWS SSO pretty radically changes the normal orientation we have towards AWS identity — for the better⁶ — by representing the human within AWS, which leads to improvements like the ability to list the roles you’ve got access to, which enables fun things like aws-sso-util automatically populating your ~/.aws/config with all the accounts and roles you’ve got access to. But this big change means things work a bit differently from how we’ve gotten used to thinking about roles and AWS configuration, so hopefully this has helped clear up the mechanism for AWS SSO login through the CLI. If you’ve got questions or comments, hit me up on Twitter.

¹ aws sts get-caller-identity (and the same API in the SDK) is a great way to check that you’ve got valid credentials and what account and IAM user/role they represent. It requires no permissions, so it will always work (unlike checking credentials are valid with, say, aws s3 ls). For a wrapper around it that makes the result a little more user-friendly, including getting the account name, and parsing AWS SSO role (aka permission set) names from the longer IAM role name, check out aws-whoami, which can be used as a CLI tool or as a library.

² It’s worth noting that while your AWS SSO configuration calls them “roles” (sso_role_name), they are not IAM role names, they are actually AWS SSO “permission set” names. These permission sets are collections of IAM policies that get attached to an IAM role created in each account where it’s used, where the IAM role is named AWSReservedSSO_{permission set name}_{random string}. But as a user you only deal with the name and the roles it becomes, so AWS SSO calls it a “role name”.

³ Not all AWS SDKs have support for AWS SSO configuration yet; aws-sso-util provides backfill support using the credential_process mechanism, see the docs here.

⁴ With aws_sso_lib, you can log the user in through login() with fine control over the details of the process. You can then get a boto3 session for a particular account and role using get_boto3_session(). These functions are useful if the program knows exactly what account and role to use (through AWS SSO). If, on the other hand, the user of the program is the one who should be choosing what account+role (or IAM user, or other assumed role) the program should use, it’s better to create a default session (just boto3.Session()) and let it pick up the credentials in the normal way, or if it’s CLI tool, you might provide a --profile option that gets passed to the session like boto3.Session(profile_name=profile_name_arg). See my explainer on boto3 sessions for more.

⁵ Note that if there are still valid cached credentials in the CLI cache from the previous user using that profile, those will be picked up and used with the new user, regardless of whether the SSO token allows access to that account+role. aws sso logout clears both the SSO token cache and the credentials cache (only of credentials obtained through AWS SSO), and makes a best-effort attempt to contact AWS SSO to invalidate the SSO tokens in the cache, so it is critical to use this when switching users.

⁶ It’s reasonable to ask why the SSO token has to have access to all the accounts and roles, when you know that you’re only going to use it for a subset of those. The AWS SSO authentication API includes a parameter to include scope when getting a token, but as far as I know, it’s not yet usable. If scope was available, you might be able to say you wanted to log in, but only for the accounts and roles in your “developer” scope, not your “admin” scope. And maybe asking for a token with “admin” scope would require you to enter your credentials again. Even with this, I think you’d still be logging in to AWS SSO for all the access granted by the given scopes, not logging in for individual profiles.

Cloud Robotics Research Scientist at @iRobot