boto3 icon indicating copy to clipboard operation
boto3 copied to clipboard

More graceful failure with expired Identity Center (SSO) credentials for sts.get_caller_identity

Open buchs opened this issue 1 year ago • 3 comments

Describe the feature

There should be a way for a user application to simply and gracefully report to the user that they need to login to Identity Center. They should not need to see Python tracebacks or exceptions. Question on this originally posted here: https://stackoverflow.com/questions/75415616/aws-boto3-sts-get-caller-identity-catching-exceptions-if-credentials-are-inval .

With a Python app, using Boto3 v1.26.59 (and botocore of same version) about the first thing done is to try to get the username of the user. We have Identity Center (SSO) users. With aged credentials (token), two exceptions are thrown and I don't seem to be able to catch them. Here is a snippet:

import boto3  # type: ignore
import botocore.errorfactory as ef
import botocore.exceptions as bcexp


def profile_user_name(profile_name: str) -> Optional[str]:
    session = boto3.Session(profile_name=profile_name)
    sts = session.client("sts")
    try:
        user_id = sts.get_caller_identity().get("UserId")
        return user_id.split(":")[-1].split("@")[0]
    except ef.UnauthorizedException as e:
        _logger.error(f'Not authenticated. Please execute:  aws sso login --profile {profile_name}')
        return None
    except bcexp.UnauthorizedSSOTokenError as e:
        _logger.error(f'Not authenticated. Please execute:  aws sso login --profile {profile_name}')
        return None
    except Exception as e:
        _logger.error(f"Encountered exception '{str(e)}'!")
        return None

Exceptions thrown by the above code look like these:

Refreshing temporary credentials failed during mandatory refresh period.
Traceback (most recent call last):
  File "/Users/kevinbuchs/lib/python3.11/site-packages/botocore/credentials.py", line 2121, in _get_credentials
    response = client.get_role_credentials(**kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/kevinbuchs/lib/python3.11/site-packages/botocore/client.py", line 530, in _api_call
    return self._make_api_call(operation_name, kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/kevinbuchs/lib/python3.11/site-packages/botocore/client.py", line 960, in _make_api_call
    raise error_class(parsed_response, operation_name)
botocore.errorfactory.UnauthorizedException: An error occurred (UnauthorizedException) when calling the GetRoleCredentials operation: Session token not found or invalid

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/kevinbuchs/lib/python3.11/site-packages/botocore/credentials.py", line 510, in _protected_refresh
    metadata = self._refresh_using()
               ^^^^^^^^^^^^^^^^^^^^^
  File "/Users/kevinbuchs/lib/python3.11/site-packages/botocore/credentials.py", line 657, in fetch_credentials
    return self._get_cached_credentials()
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/kevinbuchs/lib/python3.11/site-packages/botocore/credentials.py", line 667, in _get_cached_credentials
    response = self._get_credentials()
               ^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/kevinbuchs/lib/python3.11/site-packages/botocore/credentials.py", line 2123, in _get_credentials
    raise UnauthorizedSSOTokenError()
botocore.exceptions.UnauthorizedSSOTokenError: The SSO session associated with this profile has expired or is otherwise invalid. To refresh this SSO session run aws sso login with the corresponding profile.
Encountered exception 'The SSO session associated with this profile has expired or is otherwise invalid. To refresh this SSO session run aws sso login with the corresponding profile.'!
The auth profile 'dev-devaccess-default' is not logged in. Login with 'aws sso login --profile dev-devaccess-default' and retry!

Use Case

Application that uses ID Center credentials for access.

Proposed Solution

Step 1 - eliminate second exception during handling of first exception. Step 2 - Then pass back an invalid response like None or failing headers - something the application can process to know the situation that exists.

Other Information

n/a

Acknowledgements

  • [ ] I may be able to implement this feature request
  • [ ] This feature might incur a breaking change

SDK version used

v1.26.59

Environment details (OS name and version, etc.)

MacOS and GNU/Linux

buchs avatar Feb 14 '23 21:02 buchs

Hi @buchs thanks for the feature request. I saw that there was recently another comment on your Stack Overflow post. Does the snippet there accomplish what you were hoping?

I think some further review by the team is needed to consider your suggestions. Changes to this workflow likely need to get discussed at a cross-SDK level as well in order to maintain some consistency across SDKs.

tim-finnigan avatar Feb 20 '23 18:02 tim-finnigan

Thanks, @tim-finnigan . It looks like that user code handles it and catches the exception. Maybe a great thing for you to document for users!

buchs avatar Feb 24 '23 21:02 buchs

I am also frustrated by this behavior, and would like to see this issue solved.

I have been unable to figure out how to actually catch this exception, so there's no way for my scripts to avoid a very long intimidating traceback for what is a very very simple straightforward issue. I've tried catching UnauthorizedSSOTokenError but that doesn't work for some things, because the UnauthorizedException gets thrown first, and there does not appear to be any way to catch that (or if there is, no boto3 documentation covers how to do it).

botocore.errorfactory.UnauthorizedException: An error occurred (UnauthorizedException) when calling the GetRoleCredentials operation: Session token not found or invalid

gitcos avatar Mar 02 '24 17:03 gitcos