classCognitoIdentityProviderWrapper:"""Encapsulates Amazon Cognito actions"""def__init__(self, cognito_idp_client, user_pool_id, client_id, client_secret=None):"""
:param cognito_idp_client: A Boto3 Amazon Cognito Identity Provider client.
:param user_pool_id: The ID of an existing Amazon Cognito user pool.
:param client_id: The ID of a client application registered with the user pool.
:param client_secret: The client secret, if the client has a secret.
"""
self.cognito_idp_client = cognito_idp_client
self.user_pool_id = user_pool_id
self.client_id = client_id
self.client_secret = client_secret
defrespond_to_mfa_challenge(self, user_name, session, mfa_code):"""
Responds to a challenge for an MFA code. This completes the second step of
a two-factor sign-in. When sign-in is successful, it returns an access token
that can be used to get AWS credentials from Amazon Cognito.
:param user_name: The name of the user who is signing in.
:param session: Session information returned from a previous call to initiate
authentication.
:param mfa_code: A code generated by the associated MFA application.
:return: The result of the authentication. When successful, this contains an
access token for the user.
"""try:
kwargs = {"UserPoolId": self.user_pool_id,
"ClientId": self.client_id,
"ChallengeName": "SOFTWARE_TOKEN_MFA",
"Session": session,
"ChallengeResponses": {"USERNAME": user_name,
"SOFTWARE_TOKEN_MFA_CODE": mfa_code,
},
}
if self.client_secret isnotNone:
kwargs["ChallengeResponses"]["SECRET_HASH"] = self._secret_hash(
user_name
)
response = self.cognito_idp_client.admin_respond_to_auth_challenge(**kwargs)
auth_result = response["AuthenticationResult"]
except ClientError as err:
if err.response["Error"]["Code"] == "ExpiredCodeException":
logger.warning(
"Your MFA code has expired or has been used already. You might have ""to wait a few seconds until your app shows you a new code."
)
else:
logger.error(
"Couldn't respond to mfa challenge for %s. Here's why: %s: %s",
user_name,
err.response["Error"]["Code"],
err.response["Error"]["Message"],
)
raiseelse:
return auth_result