UPDATED for latest boto3 on 2023.10.23 (hat tip commenter @Adam Smith whose invisible hand guided us to updating extracting the role credentials in newer versions of boto3):
So here's the long and hairy answer tested on boto3==1.28.69
:
It's an eight-step process where:
- register the client using
sso-oidc.register_client
- start the device authorization flow using
sso-oidc.start_device_authorization
- redirect the user to the sso login page using
webbrowser.open
- poll
sso-oidc.create_token
until the user completes the signin
- list and present the account roles to the user using
sso.list_account_roles
- get role credentials using
sso.get_role_credentials
- create a new boto3 session with the session credentials from (6)
- eat a cookie
Step 8 is really key and should not be overlooked as part of any successful authorization flow.
In the sample below the account_id
should be the account id of the account you are trying to get credentials for. And the start_url
should be the url that aws generates for you to start the sso flow (in the AWS SSO management console, under Settings).
from time import time, sleep
import webbrowser
from boto3.session import Session
# if your sso is setup in a different region, you will
# want to include region_name=sso_region in the
# session constructor below
session = Session()
account_id = '1234567890'
start_url = 'https://d-0987654321.awsapps.com/start'
region = 'us-east-1'
sso_oidc = session.client('sso-oidc')
client_creds = sso_oidc.register_client(
clientName='myapp',
clientType='public',
)
device_authorization = sso_oidc.start_device_authorization(
clientId=client_creds['clientId'],
clientSecret=client_creds['clientSecret'],
startUrl=start_url,
)
url = device_authorization['verificationUriComplete']
device_code = device_authorization['deviceCode']
expires_in = device_authorization['expiresIn']
interval = device_authorization['interval']
webbrowser.open(url, autoraise=True)
for n in range(1, expires_in // interval + 1):
sleep(interval)
try:
token = sso_oidc.create_token(
grantType='urn:ietf:params:oauth:grant-type:device_code',
deviceCode=device_code,
clientId=client_creds['clientId'],
clientSecret=client_creds['clientSecret'],
)
break
except sso_oidc.exceptions.AuthorizationPendingException:
pass
access_token = token['accessToken']
sso = session.client('sso')
account_roles = sso.list_account_roles(
accessToken=access_token,
accountId=account_id,
)
roles = account_roles['roleList']
# simplifying here for illustrative purposes
role = roles[0]
# earlier versions of the sso api returned the
# role credentials directly, but now they appear
# to be in a subkey called `roleCredentials`
role_creds = sso.get_role_credentials(
roleName=role['roleName'],
accountId=account_id,
accessToken=access_token,
)['roleCredentials']
session = Session(
region_name=region,
aws_access_key_id=role_creds['accessKeyId'],
aws_secret_access_key=role_creds['secretAccessKey'],
aws_session_token=role_creds['sessionToken'],
)
sso
file? You can specify new location of the credential file using AWS_SHARED_CREDENTIALS_FILE env variable if this is what you want to do. – Glossographysso
is not a file but a directory. I"m updating the ticket. – GherardiclientId
andclientSecret
andaccessToken
temporary AWS credentials? If yes, than you have to load it in boto3 manually I think, and create new boto3 session with the credentials. – Glossography