i am trying to make a function that uses multiple aws services in a lambda function :
import os
import boto3
import hmac, hashlib, base64, copy
def lambda_handler(event, context):
secretsmanager = boto3.client('secretsmanager')
secret_response = secretsmanager.get_secret_value(SecretId = os.environ.get('cognitoSecretID'))
cognito = boto3.client('cognito-idp')
cognito_pool_id = os.environ.get('cognitoPoolID')
event_body = event['body']
username = event_body['email']
secret = secret_response['SecretString']
raw_message = username + cognito_pool_id
message = bytes(raw_message, 'utf-8')
secret = bytes(secret,'utf-8')
secret_hash = base64.b64encode(hmac.new(secret, message, digestmod=hashlib.sha256).digest()).decode()
authParameters = {
'USERNAME': username,
'PASSWORD': event_body['password'],
'SECRET_HASH': secret_hash
}
cognito.admin_initiate_auth(UserPoolId = cognito_pool_id, ClientId = os.environ.get('cognitoClientID'), AuthFlow = "ADMIN_USER_PASSWORD_AUTH", AuthParameters = authParameters )
return True
i am testing git with pytest as follows :
import os
import pytest
from unittest.mock import call
import boto3
import hmac, hashlib, base64
from src.DacAdminsLogin import lambda_handler
from __mocks__.SecretsManagerMock import SecretsManagerMock
MockLambdaHandlerEvent = {
'body' : {
'email': "abdelouahedelhariri#gmail.com",
'password': "test"
}
}
#pytest.fixture
def dummy_secret(monkeypatch):
dummySecret = 'adummytest'
monkeypatch.setenv('cognitoSecretID', dummySecret, prepend=False)
return dummySecret
#pytest.fixture
def dummy_cognito_id(monkeypatch):
dummyCognitoID = 'adummycognitoid'
monkeypatch.setenv('cognitoClientID', dummyCognitoID, prepend=False)
return 'adummycognitoid'
#pytest.fixture
def dummy_cognito_pool_id(monkeypatch):
dummyCognitoPoolID = 'adummycognitopoolid'
monkeypatch.setenv('cognitoPoolID', dummyCognitoPoolID, prepend=False)
return 'adummycognitopoolid'
#pytest.fixture
def secret_manager(mocker, dummy_secret):
mock = mocker.patch('__mocks__.SecretsManagerMock.SecretsManagerMock')
mock.get_secret_value.return_value = {'SecretString' : dummy_secret}
return mock
#pytest.fixture
def cognito(mocker):
return mocker.patch('__mocks__.CognitoMock.CognitoMock', return_value= "Test secret")
#pytest.fixture
def client(mocker, secret_manager, cognito):
return mocker.patch('boto3.client', side_effect= [secret_manager, cognito])
def test_create_secrets_manager_client(client):
lambda_handler(MockLambdaHandlerEvent,1)
client.assert_has_calls([call('secretsmanager')])
def test_get_secret_value_call(client ,secret_manager, dummy_secret):
lambda_handler(MockLambdaHandlerEvent,1)
secret_manager.get_secret_value.assert_called_once_with(SecretId= dummy_secret)
def test_create_cognito_client(client ,secret_manager, dummy_secret):
lambda_handler(MockLambdaHandlerEvent,1)
client.assert_has_calls([call('secretsmanager'), call('cognito')])
def test_admin_initiate_auth_call(client, monkeypatch , cognito, dummy_secret, dummy_cognito_id, dummy_cognito_pool_id):
MockLambdaHandlerEventBody = MockLambdaHandlerEvent['body']
username = MockLambdaHandlerEventBody['email']
app_client_id = dummy_cognito_pool_id
key = dummy_secret
message = bytes(username+app_client_id,'utf-8')
key = bytes(dummy_secret,'utf-8')
secret_hash = base64.b64encode(hmac.new(key, message, digestmod=hashlib.sha256).digest()).decode()
authParameters = {
'USERNAME': username,
'PASSWORD': MockLambdaHandlerEventBody['password'],
'SECRET_HASH': secret_hash
}
lambda_handler(MockLambdaHandlerEvent,1)
cognito.admin_initiate_auth.assert_called_once_with(UserPoolId = dummy_cognito_pool_id, ClientId = dummy_cognito_id, AuthFlow = "ADMIN_USER_PASSWORD_AUTH", AuthParameters = authParameters )
I am new to python and i don't know why i keep getting the following error : TypeError: can only concatenate str (not "NoneType") to str, coming from the raw_message concatenation.
When i comment out the cognito.admin_initiate_auth.assert_called_once_with(UserPoolId = dummy_cognito_pool_id, ClientId = dummy_cognito_id, AuthFlow = "ADMIN_USER_PASSWORD_AUTH", AuthParameters = authParameters )
or change for example the UserPoolId to another value the error disappears.
Related
I have a chalice project (1.26.2) which is always exiting with a time out when deployed, chalice local works fine.
This is my project structure:
This is the code in my app.py:
from datetime import datetime
from decimal import Decimal
import boto3
import firebase_admin
from chalice import Chalice, AuthResponse
from chalice.app import AuthRequest
from firebase_admin import credentials, auth
from chalicelib import crud
from chalicelib import models
from chalicelib import schemas
from chalicelib.auth import full_user_from_context, user_from_context
from chalicelib.db import SessionLocal, engine
app = Chalice(app_name='server-aws')
BUCKET = 'meet-app'
s3_client = boto3.client('s3')
cred = credentials.Certificate('chalicelib/serviceAccountKey.json')
firebase_admin.initialize_app(cred)
models.Base.metadata.create_all(bind=engine)
DISTANCE_IN_METERS = 100
_DB = None
def get_db():
global _DB
if _DB is None:
_DB = SessionLocal()
return _DB
#app.lambda_function(name='test-function')
def create_user(event, context):
return {'hello': 'world'}
#app.route('/health')
def health():
return {'status': 'ok'}
#app.authorizer()
def token_authorizer(auth_request: AuthRequest) -> AuthResponse:
token = auth_request.token
try:
decoded_token = auth.verify_id_token(token)
decoded = decoded_token
allowed_routes = [
'/auth',
'/me',
]
if 'permission_verified' in decoded and decoded['permission_verified'] is True:
allowed_routes.append('/me/location')
allowed_routes.append('/nearby')
allowed_routes.append('/me/profile-image')
print('routes', allowed_routes)
return AuthResponse(routes=allowed_routes, principal_id=decoded['sub'], context=decoded)
except Exception as e:
print('error', e)
return AuthResponse(routes=[], principal_id='non-user')
#app.route('/auth', methods=['GET'], authorizer=token_authorizer)
def authorize():
u = user_from_context(app.current_request.context)
user = crud.get_user(get_db(), u['uid'])
if user is None:
user = crud.create_user(get_db(), schemas.UserCreate(
uid=u['uid'],
phone=u['phone_number'],
permission_verified=True # TODO: find verification method
))
token = auth.create_custom_token(user.uid, {
"permission_verified": user.permission_verified,
"uid": user.uid,
"phone": user.phone,
"name": user.name,
"linkedin": user.linkedin,
"instagram": user.instagram,
})
return {
'user': user.__json__(),
'token': token.decode()
}
#app.route('/me', methods=["PUT"], authorizer=token_authorizer)
def update_me():
r = app.current_request
u = full_user_from_context(r.context)
data = r.json_body
u = crud.update_user(get_db(), schemas.UserUpdate(
uid=u.uid,
name=data.get('name'),
phone=data.get('phone'),
instagram=data.get('instagram'),
linkedin=data.get('linkedin'),
))
if u is None: # todo: code
return {
"error": "could not update"
}
return {
"user": u.__json__()
}
#app.route('/me/profile-image', methods=["PUT"], content_types=['application/octet-stream'],
authorizer=token_authorizer)
def update_me():
r = app.current_request
u = full_user_from_context(r.context)
data = r.raw_body
file_name = u.uid
tmp_file_name = '/tmp/' + file_name + '.jpg'
with open(tmp_file_name, 'wb') as tmp_file:
tmp_file.write(data)
key = 'profile-images/' + file_name
try:
s3_client.upload_file(tmp_file_name, BUCKET, key, ExtraArgs={'ACL': 'public-read'})
except Exception as e:
app.log.error(e)
return {
"error": str(e)
}
url = f'https://{BUCKET}.s3.amazonaws.com/{key}'
u = crud.update_user(get_db(), schemas.UserUpdate(
uid=u.uid,
profile_image_url=url
))
return {
"url": url
}
#app.route('/me/location', methods=["PUT"], authorizer=token_authorizer)
def update_me_location():
r = app.current_request
u = full_user_from_context(r.context)
data = r.json_body
lat = Decimal(str(data.get('latitude')))
lng = Decimal(str(data.get('longitude')))
loc = crud.update_user_location(get_db(), schemas.UserLocationUpdate(
uid=u.uid,
latitude=lat,
longitude=lng,
timestamp=datetime.now(),
geo=f'POINT({lng} {lat})'
))
if loc is None:
loc = crud.create_user_location(get_db(), schemas.UserLocationCreate(
uid=u.uid,
latitude=lat,
longitude=lng
))
loc = schemas.UserLocationGet(
uid=u.uid,
user=schemas.UserGet(
uid=u.uid,
name=u.name,
linkedin=u.linkedin,
instagram=u.instagram,
profile_image_url=u.profile_image_url
),
latitude=loc.latitude,
longitude=loc.longitude,
timestamp=loc.timestamp.isoformat()
)
return {
'loc': loc.json()
}
#app.route('/nearby', methods=["GET"], authorizer=token_authorizer)
def nearby():
r = app.current_request
u = full_user_from_context(r.context)
user_location = crud.get_user_location(get_db(), u.uid)
if user_location is None:
return {
"error": "no user location"
} # todo: better errro
nearby_users = crud.get_nearby_users(get_db(), u.uid, schemas.UserLocationGet(
uid=user_location.user_id,
latitude=user_location.latitude,
longitude=user_location.longitude,
user=u,
timestamp=user_location.timestamp.isoformat()
), DISTANCE_IN_METERS)
return {
"nearby_distance_in_meters": DISTANCE_IN_METERS,
"nearby_users": list(map(lambda x: schemas.UserLocationGet(
uid=x.user_id,
latitude=x.latitude,
longitude=x.longitude,
timestamp=x.timestamp.isoformat(),
user=schemas.UserGet(
uid=x.user.uid,
name=x.user.name,
instagram=x.user.instagram,
linkedin=x.user.linkedin,
profile_image_url=x.user.profile_image_url
)
).dict(), nearby_users))
}
This is the response when I invoke the test-function from the AWS console:
This is what I get when I do chalice logs:
Traceback (most recent call last):[ERROR] Runtime.ImportModuleError: Unable to import module 'app': No module named 'crud'
Traceback (most recent call last):[ERROR] Runtime.ImportModuleError: Unable to import module 'app': No module named 'crud'
2021-11-14 19:29:04.197000 88eb68 2021-11-14T19:29:04.197Z c143dcd4-e212-41f1-a786-7d86e4b58e59 Task timed out after 60.06 seconds
This is my requirements.txt:
chalice~=1.26.2
firebase-admin
boto3~=1.18.53
botocore
sqlalchemy
pydantic
# psycopg2
GeoAlchemy2
psycopg2-binary
I need to use psycopg2 so maybe that's the problem.
Every http request results in a 504 time out.
In local mode everything works fine.
Thanks in advance.
your chalicelib package folder's __init__.py (it must be written like this) has an extra underscore at the end of it. So python import system doesn't recognize such folder as a package, hence the error.
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 and device_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):
from warrant import aws_srp
from warrant.aws_srp import AWSSRP
import boto3
import base64
import os
def generate_hash_device(device_group_key, device_key):
# source: https://github.com/amazon-archives/amazon-cognito-identity-js/blob/6b87f1a30a998072b4d98facb49dcaf8780d15b0/src/AuthenticationHelper.js#L137
# random device password, which will be used for DEVICE_SRP_AUTH flow
device_password = base64.standard_b64encode(os.urandom(40)).decode('utf-8')
combined_string = '%s%s:%s' % (device_group_key, device_key, device_password)
combined_string_hash = aws_srp.hash_sha256(combined_string.encode('utf-8'))
salt = aws_srp.pad_hex(aws_srp.get_random(16))
x_value = aws_srp.hex_to_long(aws_srp.hex_hash(salt + combined_string_hash))
g = aws_srp.hex_to_long(aws_srp.g_hex)
big_n = aws_srp.hex_to_long(aws_srp.n_hex)
verifier_device_not_padded = pow(g, x_value, big_n)
verifier = aws_srp.pad_hex(verifier_device_not_padded)
device_secret_verifier_config = {
"PasswordVerifier": base64.standard_b64encode(bytearray.fromhex(verifier)).decode('utf-8'),
"Salt": base64.standard_b64encode(bytearray.fromhex(salt)).decode('utf-8')
}
return device_password, device_secret_verifier_config
client = boto3.client('cognito-idp')
username='xxx'
password='xxx'
client_id='xxx'
client_secret='xxx'
pool_id='xxx'
# 1. Login with the password via standard SRP flow
aws = AWSSRP(username=username, password=password, pool_id=pool_id,
client_id=client_id, client_secret=client_secret, client=client)
auth_init = client.initiate_auth(
AuthFlow='USER_SRP_AUTH',
AuthParameters=aws.get_auth_params(),
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
)
# 2. Get device_key and device_group_key returned after successful login
device_key = response['AuthenticationResult']['NewDeviceMetadata']['DeviceKey']
device_group_key = response['AuthenticationResult']['NewDeviceMetadata']['DeviceGroupKey']
# 3. Generate random device password, device salt and verifier
device_password, device_secret_verifier_config = generate_hash_device(device_group_key, device_key)
response_dev = client.confirm_device(
AccessToken=response['AuthenticationResult']['AccessToken'],
DeviceKey=device_key,
DeviceSecretVerifierConfig=device_secret_verifier_config,
DeviceName='some_device_name'
)
# 4. Remember the device
response_dev_upd = client.update_device_status(
AccessToken=response['AuthenticationResult']['AccessToken'],
DeviceKey=device_key,
DeviceRememberedStatus='remembered'
)
Then on a clean session you can login using device credentials (Amazon Cognito Identity SDK for JavaScript source):
import re
import datetime
import base64
import hmac
import hashlib
import boto3
from warrant import aws_srp
from warrant.aws_srp import AWSSRP
class AWSSRPDEV(AWSSRP):
# source: https://github.com/amazon-archives/amazon-cognito-identity-js/blob/6b87f1a30a998072b4d98facb49dcaf8780d15b0/src/CognitoUser.js#L498
def __init__(self, username, device_group_key, device_key, device_password,
client_id, client, region=None, client_secret=None):
self.username = username
self.device_group_key = device_group_key
self.device_key = device_key
self.device_password = device_password
self.client_id = client_id
self.client_secret = client_secret
self.client = client or boto3.client('cognito-idp', region_name=region)
self.big_n = aws_srp.hex_to_long(aws_srp.n_hex)
self.g = aws_srp.hex_to_long(aws_srp.g_hex)
self.k = aws_srp.hex_to_long(aws_srp.hex_hash('00' + aws_srp.n_hex + '0' + aws_srp.g_hex))
self.small_a_value = self.generate_random_small_a()
self.large_a_value = self.calculate_a()
def get_auth_params(self):
auth_params = super(AWSSRPDEV, self).get_auth_params()
auth_params['DEVICE_KEY'] = self.device_key
return auth_params
def get_device_authentication_key(self, device_group_key, device_key, device_password, server_b_value, salt):
u_value = aws_srp.calculate_u(self.large_a_value, server_b_value)
if u_value == 0:
raise ValueError('U cannot be zero.')
username_password = '%s%s:%s' % (device_group_key, device_key, device_password)
username_password_hash = aws_srp.hash_sha256(username_password.encode('utf-8'))
x_value = aws_srp.hex_to_long(aws_srp.hex_hash(aws_srp.pad_hex(salt) + username_password_hash))
g_mod_pow_xn = pow(self.g, x_value, self.big_n)
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 = aws_srp.compute_hkdf(bytearray.fromhex(aws_srp.pad_hex(s_value)),
bytearray.fromhex(aws_srp.pad_hex(aws_srp.long_to_hex(u_value))))
return hkdf
def process_device_challenge(self, challenge_parameters):
username = challenge_parameters['USERNAME']
salt_hex = challenge_parameters['SALT']
srp_b_hex = challenge_parameters['SRP_B']
secret_block_b64 = challenge_parameters['SECRET_BLOCK']
# re strips leading zero from a day number (required by AWS Cognito)
timestamp = re.sub(r" 0(\d) ", r" \1 ",
datetime.datetime.utcnow().strftime("%a %b %d %H:%M:%S UTC %Y"))
hkdf = self.get_device_authentication_key(self.device_group_key,
self.device_key,
self.device_password,
aws_srp.hex_to_long(srp_b_hex),
salt_hex)
secret_block_bytes = base64.standard_b64decode(secret_block_b64)
msg = bytearray(self.device_group_key, 'utf-8') + bytearray(self.device_key, 'utf-8') + \
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())
response = {'TIMESTAMP': timestamp,
'USERNAME': username,
'PASSWORD_CLAIM_SECRET_BLOCK': secret_block_b64,
'PASSWORD_CLAIM_SIGNATURE': signature_string.decode('utf-8'),
'DEVICE_KEY': self.device_key}
if self.client_secret is not None:
response.update({
"SECRET_HASH":
self.get_secret_hash(username, self.client_id, self.client_secret)})
return response
client = boto3.client('cognito-idp')
username='xxx'
client_id='xxx'
client_secret='xxx'
device_key = 'xxx'
device_group_key = 'xxx'
device_password = 'xxx'
aws_dev = AWSSRPDEV(username=username,
device_group_key=device_group_key, device_key=device_key, device_password=device_password,
client_id=client_id, client_secret=client_secret, client=client)
# Note that device auth flow doesn't start with client.initiate_auth(),
# but rather with client.respond_to_auth_challenge() straight away
response_auth = client.respond_to_auth_challenge(
ClientId=client_id,
ChallengeName='DEVICE_SRP_AUTH',
ChallengeResponses=aws_dev.get_auth_params()
)
cr = aws_dev.process_device_challenge(response_auth['ChallengeParameters'])
response_verifier = client.respond_to_auth_challenge(
ClientId=client_id,
ChallengeName='DEVICE_PASSWORD_VERIFIER',
ChallengeResponses=cr
)
Note that in my case Cognito client does have a client_secret, however the code above should potentially work if it doesn't.
To make things a little bit more consice you can use Pycognito python library
So in case you want to pass sms mfs (or software token mfs) challenge:
from pycognito import Cognito
from pycognito.exceptions import SoftwareTokenMFAChallengeException, SMSMFAChallengeException
client_id = 'client_id'
user_pool_id = 'pool_id'
username = 'username'
password = 'password'
user = Cognito(user_pool_id,client_id, username=username)
try:
user.authenticate(password)
except SoftwareTokenMFAChallengeException:
code = input("Enter the code from your authenticator app: ")
user.respond_to_software_token_mfa_challenge(code)
except SMSMFAChallengeException:
code = input("Enter the SMS code: ")
user.respond_to_sms_mfa_challenge(code)
print(f"My access token: {user.access_token}")
I have successfully subscribed to a websocket and am receiving data. Like:
Received '[0, 'hb']'
Received '[1528, 'hb']'
Received '[1528, [6613.2, 21.29175815, 6613.3, 37.02985217, 81.6, 0.0125, 6611.6, 33023.06141807, 6826.41966538, 6491]]'
Received '[1528, 'hb']'
Received '[0, 'hb']'
Now I want the individual values in python as variable memory.
For example:
ChanID = 1528
Price = 6613.2
I need this module in Python: https://pypi.org/project/websocket-client/
Many thanks for your help
Here is the code:
import json
import hmac, hashlib
from time import time, sleep
from websocket import create_connection
url = 'wss://api.bitfinex.com/ws/2'
key = ""
secret = ""
ws = create_connection(url)
class Bitfinex():
def __init__(self, secret, key):
self.secret = secret
self.key = key
self.url = url
self.nonce = str(int(time.time() * 1000000))
self.signature = hmac.new(str.encode(self.secret), bytes('AUTH' + self.nonce, 'utf8'),
hashlib.sha384).hexdigest()
self.payload = {'apiKey': self.key,
'event': 'auth',
'authPayload': 'AUTH' + self.nonce,
'authNonce': self.nonce,
'authSig': self.signature}
ws.send(json.dumps(self.payload))
def get_chanid(self, symbol):
get_chanid = {
'event': "subscribe",
'channel': "ticker",
'symbol': "t" + symbol,
}
ws.send(json.dumps(get_chanid))
Bitfinex.__init__(Bitfinex, secret, key)
sleep(1)
Bitfinex.get_chanid(Bitfinex, 'BTCUSD')
sleep(1)
this may be help
import json
import hmac
import hashlib
import time
from websocket import create_connection
key = ""
secret = ""
class Bitfinex(object):
def __init__(self, secret, key):
self.secret = secret
self.key = key
self.url = 'wss://api.bitfinex.com/ws/2'
self.nonce = str(int(time.time() * 1000000))
self.signature = hmac.new(str.encode(self.secret), bytes('AUTH' + self.nonce, 'utf8'),
hashlib.sha384).hexdigest()
self.payload = {'apiKey': self.key,
'event': 'auth',
'authPayload': 'AUTH' + self.nonce,
'authNonce': self.nonce,
'authSig': self.signature}
self.ws = create_connection(url)
def get_chanid(self, symbol):
self.ws.send(json.dumps(self.payload))
get_chanid = {
'event': "subscribe",
'channel': "ticker",
'symbol': "t" + symbol,
}
self.ws.send(json.dumps(get_chanid))
while True:
data = self.ws.recv()
print(data)
bit = Bitfinex(secret, key)
bit.get_chanid('BTCUSD')
class MsgConnection(tornado.websocket.WebSocketHandler):
def on_message(self,message):
session_id = self.get_cookie('session')
session = get_session_in_flask(session_id)
How to code get_session_in_flask?
def get_session_in_flask(secret_key, cookie_str):
import hashlib
from itsdangerous import URLSafeTimedSerializer
from flask.sessions import TaggedJSONSerializer
salt = 'cookie-session'
serializer = TaggedJSONSerializer()
signer_kwargs = {
'key_derivation': 'hmac',
'digest_method': hashlib.sha1
}
s = URLSafeTimedSerializer(secret_key, salt=salt, serializer=serializer, signer_kwargs=signer_kwargs)
return s.loads(cookie_str)
I am trying to use a WooCommerce Python client to make a POST request but
I get "Provided Signature does not match"
The client I use is from:
WooCommerce API OAuth in python
This is my client:
#!/usr/bin/env python
import requests
import random
import string
import time
from hashlib import sha1
import hmac
import binascii
import re
import collections
from urllib import quote, urlencode
def uksort(dictionary):
return collections.OrderedDict(sorted(dictionary.items(), cmp = cmp))
class WooCommerce(object):
def __init__(self, consumer_key, consumer_secret, endpoint):
self.consumer_key = consumer_key
self.consumer_secret = consumer_secret
self.endpoint = endpoint
def _make_request(self, resource, params, method = "GET"):
oauth_params = {
"oauth_consumer_key": self.consumer_key,
"oauth_nonce": self._gen_nonce(),
"oauth_timestamp": self._gen_timestamp(),
"oauth_signature_method": "HMAC-SHA1",
}
oauth_params["oauth_signature"] = self._gen_signature(resource, dict(params.items() + oauth_params.items()), method)
params = dict(params.items() + oauth_params.items())
if method == "GET":
print self.endpoint + resource + "?" + urlencode(params)
elif method == "POST":
print self.endpoint + resource + "?" + urlencode(params)
req = urllib.request.Request(self.endpoint + resource + "?" + urlencode(params))
open = urllib.request.urlopen(req)
requestContent = open.read()
#print(open)
def _gen_nonce(self):
ran_string = ''.join(random.choice(string.ascii_uppercase + string.digits) for i in range(32)).encode("base64")
alnum_hash = re.sub(r'[^a-zA-Z0-9]', "", ran_string)
return alnum_hash
def _gen_timestamp(self):
return int(time.time())
def _gen_signature(self, resource, params, method):
base_request_uri = quote(self.endpoint + resource, safe = "")
normalized_params = self._normalize_params(params)
sorted_params = uksort(normalized_params)
query_string = "%26".join([key + "%3D" + value for key, value in sorted_params.iteritems()])
raw_string = method + "&" + base_request_uri + "&" + query_string
hashed = hmac.new(self.consumer_secret, raw_string, sha1)
return binascii.b2a_base64(hashed.digest()).rstrip("\n")
def _normalize_params(self, params):
normalized = {}
for key, value in params.iteritems():
key = quote(str(key), safe = "")
value = quote(str(value), safe = "")
normalized[key] = value
return normalized
And I use it like this from another class:
woo_client = WooCommerce('ck_7bb1951bee7454b2e29bf5eef9205e0e', 'cs_155cd9420201c0a7e140bebd6a9794c7', 'http://dima.bg/wc-api/v2')
data = {
"product": {
"title": "testname",
}
}
result = self.woo_client._make_request("/products/", data, 'POST')
Can you see something wrong with my URL ? Thanks for your time.
http://xxxxxxxxx.xx/wc-api/v2/products/?product=%7B%27title%27%3A+%27testname%27%7D&oauth_nonce=NThWODczRFIyWkxRNFZOVkUxNFdRSVo0QjFSNllIVFk&oauth_timestamp=1423647865&oauth_consumer_key=ck_7bb1951bee7454b2e29bf5eef9205e0e&oauth_signature_method=HMAC-SHA1&oauth_signature=3PSnEEf08gFthIRAr8AUKQiDjco%3D
I used your code as a starting point for my own solution to this problem and had some success! Here's what I did:
import requests
import random
import string
import time
from hashlib import sha1
import hmac
import binascii
import re
from urllib import quote, urlencode
import httplib2
from collections import OrderedDict
def key_compare(a, b):
return cmp(a[0], b[0])
class Restful_Client(object):
"""docstring for Restful_Client"""
def __init__(self, endpoint, consumer_key, consumer_secret):
super(Restful_Client, self).__init__()
self.consumer_key = consumer_key
self.consumer_secret = consumer_secret
self.endpoint = endpoint
def make_request(self, resource, params, method='GET' ):
oauth_params = {
'oauth_consumer_key': self.consumer_key,
'oauth_nonce': self.gen_nonce(),
'oauth_timestamp': self.gen_timestamp(),
'oauth_signature_method': 'HMAC-SHA1',
# 'oauth_version':'1.0'
}
oauth_params['oauth_signature'] = self.gen_signature(
resource,
OrderedDict( params.items() + oauth_params.items() ),
method
)
params = OrderedDict( params.items() + oauth_params.items() )
clean_params = self.sort_params(self.normalize_params(params))
uri = endpoint + resource
p_string = urlencode(clean_params)
print 'p string:'
print '\n'.join(p_string.split('&'))
return httplib2.Http().request(uri + '?' + p_string)
def gen_signature(self, resource, params, method):
base_request_uri = quote(self.endpoint + resource, safe = "")
clean_params = self.sort_params(
self.normalize_params(
self.normalize_params(
params
)
)
)
query_string = '%26'.join([
key + '%3D' + value\
for key, value in clean_params.iteritems()
])
raw_string = '&'.join([method, base_request_uri, query_string])
print "raw string: "
print '\n'.join(raw_string.split('%26'))
hashed = hmac.new(self.consumer_secret, raw_string, sha1)
return binascii.b2a_base64( hashed.digest() )[:-1]
def normalize_string(self, string):
return quote(str(string), safe="")
def normalize_params(self, params):
return OrderedDict( [
(self.normalize_string(key), self.normalize_string(value))\
for key, value \
in params.iteritems()
])
def sort_params(self, params):
return OrderedDict(sorted(
params.iteritems(),
cmp=key_compare
))
def gen_timestamp(self):
return int(time.time())
# return 1429451603
def gen_nonce(self):
return hex(self.gen_timestamp())
#todo: make this more secure
if __name__ == "__main__":
store_url = '<STORE URL HERE>'
api_base = 'wc-api'
api_ver = 'v2'
endpoint = "%s/%s/%s/" % (store_url, api_base, api_ver)
consumer_key = '<CK HERE>'
consumer_secret = '<CS HERE>'
resource = 'customers'
parameters = {
# 'fields':'id,first_name'
}
rc = Restful_Client(endpoint, consumer_key, consumer_secret)
r, c = rc.make_request(resource, parameters, 'GET')
print r
print c
What I would suggest is ensuring that the parameters are encoded the correct number of times, I found that the parameters needed to be "double encoded" to work. It looks like in this case your parameters (the key and value strings) are not "double encoded" as suggested in the API doc http://woothemes.github.io/woocommerce-rest-api-docs/#authentication
You can test this hypothesis by trying a simple GET request without any parameters like the one in my code. if that works then that might be your problem. Good luck!