awslabs / aws-crt-python

Python bindings for the AWS Common Runtime
Apache License 2.0
87 stars 42 forks source link

Delegate credentials provider is not recalled after the credentials were expired #430

Closed mrtj closed 1 year ago

mrtj commented 1 year ago

I have temporary STS credentials and I am trying to implement a delegate credentials provider setting the expiration field of the returned AwsCredentials. I would expect that before the declared expiration of the credentials, the authorization implementation of aws-crt calls my delegate back for new credentials, however this does not happen. I plan to use the credential provider for a websocket based mqtt client from awsiot library.

def credentials_factory():
    # I expect this to be printed every time aws-crt needs to renew the credentials:
    print('credentials_factory was called!')
    session = boto3.Session()
    botocore_credential_provider = session.get_component('credential_provider')
    credentials = botocore_credential_provider.load_credentials()
    frozen = credentials.get_frozen_credentials()
    expiry_time = None
    if isinstance(credentials, botocore.credentials.RefreshableCredentials):
        # This is set manually to test the refresh mechanism:
        expiry_time = datetime.datetime.utcnow() + datetime.timedelta(minutes=5)
        # Original impl:
        # expiry_time = credentials._expiry_time
    return AwsCredentials(
        access_key_id=frozen.access_key,
        secret_access_key=frozen.secret_key,
        session_token=frozen.token,
        expiration=expiry_time
    )

def on_connection_interrupted(connection, error, **kwargs):
    print('MQTT connection interrupted. Error: %s', str(error))

def on_connection_resumed(connection, return_code, session_present, **kwargs):
    print('MQTT connection resumed. return_code: %s session_present: %s', return_code, session_present)

credentials_provider = AwsCredentialsProvider.new_delegate(credentials_factory)

mqtt_connection = mqtt_connection_builder.websockets_with_default_aws_signing(
    credentials_provider=credentials_provider,
    on_connection_interrupted=on_connection_interrupted,
    on_connection_resumed=on_connection_resumed,
    # ...
)

# use mqtt_connection ...

I would expect the credentials_factory was called! line to be printed every time when the credentials expire and should be renewed (in the snippet above once in every 5 minutes). Instead, aws-crt calls my delegate only once, at the beginning of the connection. When the token indeed expires (as reported back by the server), my on_connection_interrupted callback is called with a "AWS_ERROR_MQTT_UNEXPECTED_HANGUP" error, and then it tries to build back the connection. But I would prefer to prevent this situation and provide a new token before the old one expires.

bretambrose commented 1 year ago

The expiration interval of session credentials has no effect on a sigv4-signed websocket connection to AWS IoT Core. Credentials will only be refreshed when existing ones have expired and a new connection needs to be established.