Skip to content

Commit

Permalink
Added support for a client with a client secret
Browse files Browse the repository at this point in the history
  • Loading branch information
armicron committed Sep 26, 2017
1 parent 5142202 commit 92a4be5
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 21 deletions.
27 changes: 17 additions & 10 deletions warrant/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,8 @@ class Cognito(object):

def __init__(
self, user_pool_id, client_id,user_pool_region=None,
username=None,
id_token=None,refresh_token=None,
access_token=None,secret_hash=None,
username=None, id_token=None, refresh_token=None,
access_token=None, client_secret=None,
access_key=None, secret_key=None,
):
"""
Expand All @@ -158,7 +157,7 @@ def __init__(
self.id_token = id_token
self.access_token = access_token
self.refresh_token = refresh_token
self.secret_hash = secret_hash
self.client_secret = client_secret
self.token_type = None

boto3_client_kwargs = {}
Expand Down Expand Up @@ -319,7 +318,11 @@ def admin_authenticate(self, password):
'USERNAME': self.username,
'PASSWORD': password
}

if self.client_secret is not None:
auth_params.update({
'SECRET_HASH':
AWSSRP.get_secret_hash(self.username, self.client_id,
self.client_secret)})
tokens = self.client.admin_initiate_auth(
UserPoolId=self.user_pool_id,
ClientId=self.client_id,
Expand All @@ -340,7 +343,8 @@ def authenticate(self, password):
:return:
"""
aws = AWSSRP(username=self.username, password=password, pool_id=self.user_pool_id,
client_id=self.client_id, client=self.client)
client_id=self.client_id, client=self.client,
client_secret=self.client_secret)
tokens = aws.authenticate_user()
self.verify_token(tokens['AuthenticationResult']['IdToken'],'id_token','id')
self.refresh_token = tokens['AuthenticationResult']['RefreshToken']
Expand All @@ -354,7 +358,8 @@ def new_password_challenge(self, password, new_password):
:param password: The user's new passsword
"""
aws = AWSSRP(username=self.username, password=password, pool_id=self.user_pool_id,
client_id=self.client_id, client=self.client)
client_id=self.client_id, client=self.client,
client_secret=self.client_secret)
tokens = aws.set_new_password_challenge(new_password)
self.id_token = tokens['AuthenticationResult']['IdToken']
self.refresh_token = tokens['AuthenticationResult']['RefreshToken']
Expand Down Expand Up @@ -508,12 +513,14 @@ def renew_access_token(self):
"""
Sets a new access token on the User using the refresh token.
"""
auth_params = {'REFRESH_TOKEN': self.refresh_token}
if self.client_secret is not None:
auth_params.update({'SECRET_HASH':
AWSSRP.get_secret_hash(self.username, self.client_id, self.client_secret)})
refresh_response = self.client.initiate_auth(
ClientId=self.client_id,
AuthFlow='REFRESH_TOKEN',
AuthParameters={
'REFRESH_TOKEN': self.refresh_token
},
AuthParameters=auth_params,
)

self._set_attributes(
Expand Down
35 changes: 26 additions & 9 deletions warrant/aws_srp.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ class AWSSRP(object):
NEW_PASSWORD_REQUIRED_CHALLENGE = 'NEW_PASSWORD_REQUIRED'
PASSWORD_VERIFIER_CHALLENGE = 'PASSWORD_VERIFIER'

def __init__(self, username, password, pool_id, client_id, pool_region=None, client=None):
def __init__(self, username, password, pool_id, client_id, pool_region=None,
client=None, client_secret=None):
if pool_region is not None and client is not None:
raise ValueError("pool_region and client should not both be specified "
"(region should be passed to the boto3 client instead)")
Expand All @@ -104,6 +105,7 @@ def __init__(self, username, password, pool_id, client_id, pool_region=None, cli
self.password = password
self.pool_id = pool_id
self.client_id = client_id
self.client_secret = client_secret
self.client = client if client else boto3.client('cognito-idp', region_name=pool_region)
self.big_n = hex_to_long(n_hex)
self.g = hex_to_long(g_hex)
Expand Down Expand Up @@ -152,12 +154,23 @@ def get_password_authentication_key(self, username, password, server_b_value, sa
int_value2 = server_b_value - self.k * g_mod_pow_xn
s_value = pow(int_value2, self.small_a_value + u_value * x_value, self.big_n)
hkdf = compute_hkdf(bytearray.fromhex(pad_hex(s_value)),
bytearray.fromhex(pad_hex(long_to_hex(u_value))))
bytearray.fromhex(pad_hex(long_to_hex(u_value))))
return hkdf

def get_auth_params(self):
return {'USERNAME': self.username,
'SRP_A': long_to_hex(self.large_a_value)}
auth_params = {'USERNAME': self.username,
'SRP_A': long_to_hex(self.large_a_value)}
if self.client_secret is not None:
auth_params.update({
"SECRET_HASH":
self.get_secret_hash(self.username,self.client_id, self.client_secret)})
return auth_params

@staticmethod
def get_secret_hash(username, client_id, client_secret):
message = bytearray(username + client_id, 'utf-8')
hmac_obj = hmac.new(bytearray(client_secret, 'utf-8'), message, hashlib.sha256)
return base64.standard_b64encode(hmac_obj.digest()).decode('utf-8')

def process_challenge(self, challenge_parameters, test_timestamp=None):
user_id_for_srp = challenge_parameters['USER_ID_FOR_SRP']
Expand All @@ -176,11 +189,15 @@ def process_challenge(self, challenge_parameters, test_timestamp=None):
bytearray(secret_block_bytes) + bytearray(timestamp, 'utf-8')
hmac_obj = hmac.new(hkdf, msg, digestmod=hashlib.sha256)
signature_string = base64.standard_b64encode(hmac_obj.digest())

return {"TIMESTAMP": timestamp,
"USERNAME": user_id_for_srp,
"PASSWORD_CLAIM_SECRET_BLOCK": secret_block_b64,
"PASSWORD_CLAIM_SIGNATURE": signature_string.decode('utf-8')}
response = {'TIMESTAMP': timestamp,
'USERNAME': user_id_for_srp,
'PASSWORD_CLAIM_SECRET_BLOCK': secret_block_b64,
'PASSWORD_CLAIM_SIGNATURE': signature_string.decode('utf-8')}
if self.client_secret is not None:
response.update({
"SECRET_HASH":
self.get_secret_hash(self.username, self.client_id, self.client_secret)})
return response

def authenticate_user(self, client=None):
boto_client = self.client or client
Expand Down
6 changes: 4 additions & 2 deletions warrant/tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,11 @@ class CognitoAuthTestCase(unittest.TestCase):
def setUp(self):
self.cognito_user_pool_id = env('COGNITO_USER_POOL_ID')
self.app_id = env('COGNITO_APP_ID')
self.client_secret = env('COGNITO_CLIENT_SECRET')
self.username = env('COGNITO_TEST_USERNAME')
self.password = env('COGNITO_TEST_PASSWORD')
self.user = Cognito(self.cognito_user_pool_id,self.app_id,
username=self.username)
username=self.username, client_secret=self.client_secret)


def test_authenticate(self):
Expand Down Expand Up @@ -163,12 +164,13 @@ class AWSSRPTestCase(unittest.TestCase):
def setUp(self):
self.cognito_user_pool_id = env('COGNITO_USER_POOL_ID')
self.app_id = env('COGNITO_APP_ID')
self.client_secret = env('COGNITO_CLIENT_SECRET')
self.username = env('COGNITO_TEST_USERNAME')
self.password = env('COGNITO_TEST_PASSWORD')

self.aws = AWSSRP(username=self.username, password=self.password,
pool_id=self.cognito_user_pool_id,
client_id=self.app_id)
client_id=self.app_id, client_secret=self.client_secret)

def tearDown(self):
del self.aws
Expand Down

0 comments on commit 92a4be5

Please sign in to comment.