title: implementing USER_SRP_AUTH with python boto3 for AWS Cognito
body: <p>Amazon provides iOS, Android, and Javascript Cognito SDKs that offer a high-level authenticate-user operation.</p>
  
  <p>For example, see Use Case 4 here:</p>
  
  <p><a href="https://github.com/aws/amazon-cognito-identity-js" rel="noreferrer">https://github.com/aws/amazon-cognito-identity-js</a></p>
  
  <p>However, if you are using python/boto3, all you get are a pair of primitives: <code>cognito.initiate_auth</code> and <code>cognito.respond_to_auth_challenge</code>. </p>
  
  <p>I am trying to use these primitives along with the <code>pysrp</code> lib authenticate with the <code>USER_SRP_AUTH</code> flow, but what I have is not working.</p>
  
  <p>It always fails with "An error occurred (NotAuthorizedException) when calling the RespondToAuthChallenge operation: Incorrect username or password."  (The username/password pair work find with the JS SDK.)</p>
  
  <p>My suspicion is I'm constructing the challenge response wrong (step 3), and/or passing Congito hex strings when it wants base64 or vice versa.</p>
  
  <p>Has anyone gotten this working?  Anyone see what I'm doing wrong?</p>
  
  <p>I am trying to copy the behavior of the <code>authenticateUser</code> call found in the Javascript SDK:</p>
  
  <p><a href="https://github.com/aws/amazon-cognito-identity-js/blob/master/src/CognitoUser.js#L138" rel="noreferrer">https://github.com/aws/amazon-cognito-identity-js/blob/master/src/CognitoUser.js#L138</a></p>
  
  <p>but I'm doing something wrong and can't figure out what.</p>
  
  <pre><code>#!/usr/bin/env python
  import base64
  import binascii
  import boto3
  import datetime as dt
  import hashlib
  import hmac
  
  # http://pythonhosted.org/srp/
  # https://github.com/cocagne/pysrp
  import srp
  
  bytes_to_hex = lambda x: "".join("{:02x}".format(ord(c)) for c in x)
  
  cognito = boto3.client('cognito-idp', region_name="us-east-1")
  
  username = "foobar@foobar.com"
  password = "123456"
  
  user_pool_id = u"us-east-1_XXXXXXXXX"
  client_id = u"XXXXXXXXXXXXXXXXXXXXXXXXXX"
  
  # Step 1:
  # Use SRP lib to construct a SRP_A value.
  
  srp_user = srp.User(username, password)
  _, srp_a_bytes = srp_user.start_authentication()
  
  srp_a_hex = bytes_to_hex(srp_a_bytes)
  
  # Step 2:
  # Submit USERNAME &amp; SRP_A to Cognito, get challenge.
  
  response = cognito.initiate_auth(
      AuthFlow='USER_SRP_AUTH',
      AuthParameters={ 'USERNAME': username, 'SRP_A': srp_a_hex },
      ClientId=client_id,
      ClientMetadata={ 'UserPoolId': user_pool_id })
  
  # Step 3:
  # Use challenge parameters from Cognito to construct 
  # challenge response.
  
  salt_hex         = response['ChallengeParameters']['SALT']
  srp_b_hex        = response['ChallengeParameters']['SRP_B']
  secret_block_b64 = response['ChallengeParameters']['SECRET_BLOCK']
  
  secret_block_bytes = base64.standard_b64decode(secret_block_b64)
  secret_block_hex = bytes_to_hex(secret_block_bytes)
  
  salt_bytes = binascii.unhexlify(salt_hex)
  srp_b_bytes = binascii.unhexlify(srp_b_hex)
  
  process_challenge_bytes = srp_user.process_challenge(salt_bytes,                          
                                                       srp_b_bytes)
  
  timestamp = unicode(dt.datetime.utcnow().strftime("%a %b %d %H:%m:%S +0000 %Y"))
  
  hmac_obj = hmac.new(process_challenge_bytes, digestmod=hashlib.sha256)
  hmac_obj.update(user_pool_id.split('_')[1].encode('utf-8'))
  hmac_obj.update(username.encode('utf-8'))
  hmac_obj.update(secret_block_bytes)
  hmac_obj.update(timestamp.encode('utf-8'))
  
  challenge_responses = {
      "TIMESTAMP": timestamp.encode('utf-8'),
      "USERNAME": username.encode('utf-8'),
      "PASSWORD_CLAIM_SECRET_BLOCK": secret_block_hex,
      "PASSWORD_CLAIM_SIGNATURE": hmac_obj.hexdigest()
  }
  
  # Step 4:
  # Submit challenge response to Cognito.
  
  response = cognito.respond_to_auth_challenge(
      ClientId=client_id,
      ChallengeName='PASSWORD_VERIFIER',
      ChallengeResponses=challenge_responses)
  </code></pre>