I'm using both the boto3 and warrant libraries to try to get a device authenticated to skip multi-factor authentication after it's been recognized. I've got through a user/password auth but can't seem to figure out the right way to authenticate the device. The following is my code:
from warrant import aws_srp
from warrant.aws_srp import AWSSRP
import boto3
client = boto3.client('cognito-idp')
import datetime
username='xxx'
password='xxx'
client_id='xxx'
aws = AWSSRP(username=username, password=password, pool_id='xxx',
client_id=client_id, client=client)
auth_init = client.initiate_auth(
AuthFlow='USER_SRP_AUTH',
AuthParameters={
'USERNAME': username,
'SRP_A': aws_srp.long_to_hex(aws.large_a_value),
},
ClientId=client_id,
)
cr = aws.process_challenge(auth_init['ChallengeParameters'])
response = client.respond_to_auth_challenge(
ClientId=client_id,
ChallengeName=auth_init['ChallengeName'],
ChallengeResponses=cr
)
response = client.respond_to_auth_challenge(
ClientId=client_id,
ChallengeName='SMS_MFA',
Session=response['Session'],
ChallengeResponses={
'USERNAME': username,
'SMS_MFA_CODE':'xxx'
}
)
response_dev = client.confirm_device(
AccessToken=response['AuthenticationResult']['AccessToken'],
DeviceKey=response['AuthenticationResult']['NewDeviceMetadata']['DeviceKey'],
DeviceSecretVerifierConfig={
"PasswordVerifier": aws_srp.long_to_hex(aws.large_a_value),
"Salt": aws_srp.long_to_hex(aws.small_a_value)
}
)
response = client.update_device_status(
AccessToken=response['AuthenticationResult']['AccessToken'],
DeviceKey=device,
DeviceRememberedStatus='remembered'
)
Then on a clean session, do:
device='xxx'
auth_init = client.initiate_auth(
AuthFlow='USER_SRP_AUTH',
AuthParameters={
'USERNAME': username,
'SRP_A': aws_srp.long_to_hex(aws.large_a_value),
'DEVICE_KEY':device
},
ClientId=client_id,
)
cr = aws.process_challenge(auth_init['ChallengeParameters'])
cr['DEVICE_KEY'] = device
response = client.respond_to_auth_challenge(
ClientId=client_id,
ChallengeName='DEVICE_SRP_AUTH',
ChallengeResponses={
'USERNAME': username,
'SRP_A': aws_srp.long_to_hex(aws.large_a_value),
'DEVICE_KEY': device,
'TIMESTAMP': datetime.datetime.utcnow().strftime( "%a %b %d %H:%M:%S UTC %Y").upper()
}
)
challenge_params = response['ChallengeParameters']
challenge_params['USER_ID_FOR_SRP'] = challenge_params['USERNAME']
cr2 = aws.process_challenge(challenge_params)
response2 = client.respond_to_auth_challenge(
ClientId=client_id,
ChallengeName=response['ChallengeName'],
ChallengeResponses={
'USERNAME': username,
'PASSWORD_CLAIM_SIGNATURE': cr2['PASSWORD_CLAIM_SIGNATURE'],
'PASSWORD_CLAIM_SECRET_BLOCK': response['ChallengeParameters']['SECRET_BLOCK'],
'DEVICE_KEY': device,
'TIMESTAMP': datetime.datetime.utcnow().strftime( "%a %b %d %H:%M:%S UTC %Y").upper()
}
)
Everything seems to work properly until the last respond_to_auth_challenge
which results in:
botocore.errorfactory.NotAuthorizedException: An error occurred (NotAuthorizedException) when calling the RespondToAuthChallenge operation: Incorrect username or password.
Should I be using a different User/pass for a DEVICE_PASSWORD_VERIFIER
challenge that I haven't included? The documentation is a bit light, just saying:
DEVICE_PASSWORD_VERIFIER: Similar to PASSWORD_VERIFIER, but for devices only.
source
The reason your solution did not work is because in order for device verifier and salt to work, they have to be calculated based on
device_group_key
anddevice_key
. Also calculation of a challenge response for device authentication differs from the standard password SRP flow. Here is how it worked out for me:First, confirm and remember the device (Amazon Cognito Identity SDK for JavaScript source):
Then on a clean session you can login using device credentials (Amazon Cognito Identity SDK for JavaScript source):
Note that in my case Cognito client does have a
client_secret
, however the code above should potentially work if it doesn't.