Verify Metamask signature (ethereum) using Python - python

I would like to verify an ethereum (ETH) signature made in MetaMask using python. I'm developing a website using flask as backend.
Javascript code send a POST requests to the back end containing the 3 following variables:
{'signature': '0x0293cc0d4eb416ca95349b7e63dc9d1c9a7aab4865b5cd6d6f2c36fb1dce12d34a05039aedf0bc64931a439def451bcf313abbcc72e9172f7fd51ecca30b41dd1b', 'nonce': '6875972781', 'adress': '0x3a806c439805d6e0fdd88a4c98682f86a7111789'}
My goal is to verify that the signature contains the nonce (random integer) and was sign by the public adress
I using javascript to sign the nonce using the ether library
const ethereum = window.ethereum;
const provider = new ethers.providers.Web3Provider(ethereum)
const signer = provider.getSigner()
var signature = await signer.signMessage(nonce);
I tried with several python libraires, but I'm unable to format signature, adress and nonce so that it works. here is unsuccessfull try made using ecdsa librairy:
vk = ecdsa.VerifyingKey.from_string(bytes.fromhex(address), curve=ecdsa.SECP256k1, hashfunc=sha256)
vk.verify(bytes.fromhex(hex(signature)), bytes(nonce, 'utf-8'))
I get the following error:
ValueError: non-hexadecimal number found in fromhex() arg at position 1
Thanks for your help !

Using web3.py you could use w3.eth.account.recover_message to recover the address from the signature and the data. After that you compare the adress to the correct adress(with lowercase, because i think web3.py would give you lower and uppercase)
from web3 import Web3
from hexbytes import HexBytes
from eth_account.messages import encode_defunct
w3 = Web3(Web3.HTTPProvider(""))
mesage= encode_defunct(text="6875972781")
address = w3.eth.account.recover_message(mesage,signature=HexBytes("0x0293cc0d4eb416ca95349b7e63dc9d1c9a7aab4865b5cd6d6f2c36fb1dce12d34a05039aedf0bc64931a439def451bcf313abbcc72e9172f7fd51ecca30b41dd1b"))
print(address)

You can verify using JS but I commend you for wanting to verify on the backend with python. I was faced with the exact same choice and opted for the latter. Basically, I created 2 apis, the first of which generates the message and nonce and the second verifies the signature.
I'm using FastAPI for this but you can pattern it to any framework you prefer such as Django.
Generate message:
# ethaccount is the user's wallet included in the body of the request
async def generate_message(ethaccount: str = Body(...)) -> str:
# I save the wallet to cache for reference later
set_cache_address(ethaccount)
# Generate the nonce
nonce = uuid.uuid4()
# Generate the message
message = f'''
Welcome! Sign this message to login to the site. This doesn't cost you
anything and is free of any gas fees.
Nonce:
{nonce}.
'''
return message
Metamask takes over from here. Afterwards verify the signature generated by metamask:
from web3.auto import w3
from eth_account.messages import encode_defunct
async def signature(data: dict = Body(...)): # noqa
# User's signature from metamask passed through the body
sig = data.get('signature')
# The juicy bits. Here I try to verify the signature they sent.
message = encode_defunct(text=data.get('message'))
signed_address = (w3.eth.account.recover_message(message, signature=sig)).lower()
# Same wallet address means same user. I use the cached address here.
if get_cache_address() == signed_address:
# Do what you will
# You can generate the JSON access and refresh tokens here
pass
NOTE: I've cleaned this code to show only the logic/methods you might need. The actual code I'm using is longer as I generate tokens, etc.

JackDonMClovin, Body is a request auto parse that is provided by fastapi. For other libs you can get the signature data from the request object.

Related

How to decode opaque Access Token with Oauth2.0

I have got a token key which contains the logged in person email address as well as the name and other end points.This was actually used in xero API connection.
scope = 'offline_access accounting.reports.read accounting.settings.read openid profile email'
I need to decode this token key and get the logged in email address and the name of the person who is logged in.
For an example my token key is as below.
b9b73c12b40a3bc1441f5bda331c4d7c64c0394956d5105eec61a71de19f8153
How can I decode this opaque Access Token and get the relevant information using python.
Clients should never decode access tokens directly, as jps says. You have these options:
READ USER FIELDS FROM ID TOKEN
The UI reads this JWT directly. An id token always has JWT format and is designed to be read by clients.
USE USER INFO ENDPOINT
The UI can send the access token to the User Info endpoint, using the message from step 24 of the above blog post.
GET USER INFO FROM API
This tends to be the most extensible option, since you can return any info you want, and you are not limited to what is contained in access tokens.
const token = req.headers.authorization.split(" ")[1]; //Bearer +token
const decodedToken = jwt.verify(token, "secret_message_long_string");
req.userData = { email: decodedToken.email, userId: decodedToken.userId };
This example is for decoding data for nodejs.
try the same way in python. Importing jwt and using the method verfiy passing the token and secret string as an argument.

Python: how to generate signed URL with custom policy cloudfront

I have some code that generates a signed URL
expire_time = in_an_hour()
conn = CloudFrontConnection(access_key_id, access_key)
##enter the id or domain name to select a distribution
distribution = Distribution(connection=conn, config=None, domain_name=domain, id=dist_id, last_modified_time=None, status='')
signed_url = distribution.create_signed_url(url=url_tosign, keypair_id=keypair,expire_time=expire_time,private_key_file="/path/to/priv.pem")
return signed_url
I want to include a custom policy that takes into account user IP. I wrote a method to generate the json that works fine. I tried adding policy=get_policy(url_tosign, ip) to the distribution.create_signed_url call, but I just get an error saying it's an unexpected keyword argument. How can I modify this code to generate my signed URL with source IP restrictions?
James Dean's link had the solution. There is an ip_address parameter that we can use.
Updated line of code
signed_url = distribution.create_signed_url(url=url_tosign, keypair_id=keypair,expire_time=expire_time,private_key_file="/path/to/priv.pem", ip_address= "x.x.x.x/32") # CIDR notation optional. x.x.x.x also works as a /32

Zeep with namespace(?) variables

I have an XML/WCF API I need to implement something against. The API client library is only provided as c# in Windows and our company does not do either c# or Windows. I am now experimenting with Python and zeep. The api is Symmetry access control system API if anyone is interested.
I can connect to the server and read the wsdl structure. This works:
URL='https://localhost/smsXMLWebService/SMSXMLWebService.svc?singleWsdl'
URL2='https://localhost/smsXMLWebService/smsXMLWebService.svc'
session = Session()
session.verify = False
transport = Transport(session=session)
self.client = zeep.Client(URL, transport=transport)
self.service = self.client.create_service('{http://tempuri.org/}WSHttpBinding_ISMSXMLWebService', URL2)
Now everything from that point forward will require login to the platform. In the example c# code this is done as follows:
G4TAPI = new SMSXMLWebServiceClient();
G4TAPI.ClientCredentials.UserName.UserName = txtUserName.Text
G4TAPI.ClientCredentials.UserName.Password = txtPassword.Text.ToLower();
G4TAPI.G4TLogin();
My self.service has now G4TLogin() call and it seems to attempt to connect when I wireshark the traffic. But how do I set the username and password as they are not given as parameters to G4TLogin() method?
This does not work:
self.service.ClientCredentials.UserName.UserName = "api"
This is very much out of my comfort zone and I may be using incorrect terminology here. Any ideas?
The error message is
AttributeError: Service has no operation 'ClientCredentials'
When using Zeep make sure to study the namespaces in WSDL URL, using
python -mzeep "YourWsdlUrlGoesHere"
Get the parameters and make a Python dictionary from them (in my case facing C#, including username and password in the dictionary) note that one might need to make nested dictionary like in my case.
from requests import Session
from requests.auth import HTTPBasicAuth, HTTPDigestAuth
from zeep import Client
from zeep.transports import Transport
request = { "Credential":{"Username": "yourusername",
"Password": "yourpassword"},
"RN": "150147119"
}
session = Session()
client = Client('http://your url and wsdl../Invoice.svc?Wsdl',transport=Transport(session=session))
r = client.service.NameOfYourService(request)
print(r)
Do not pass user and password in Zeep formal format. Passing the dictionary worked for me.
In my case the WSDL suggested user and password be in credential and a string be passed in RN and finally all be passed in a one variable.

How can I get crypto keys via SL api as in portal?

I'm trying to get the cryptographic keys just like portal, but I can't make my mask work, can someone tell me what's wrong with the following request, btw I am using the following url
https://sldn.softlayer.com/reference/services/softlayer_security_certificate_request/getsslcertificaterequests
and jic python api client is used and also rest requests can work for me to get the socket layers.
mask = "mask[accountId, certificateSigningRequest, certificateAuthorityName, id]
response = client['SoftLayer_Security_Certificate_Request'].getsslcertificaterequests()
I also want to find how to search the virtual ips associated to the certificates but I don't find what api method does what i need.
Your current code will retrieve only one of the certificates and not the stored security certificates, in order to make your mask work you need to add close the string with " double quote, and the method you are calling should be getSslCertificateRequests, see below:
accountId = 202768 #change this value
mask = "mask[accountId, certificateSigningRequest, certificateAuthorityName, id]"
response = client['SoftLayer_Security_Certificate_Request'].getSslCertificateRequests(accountId)
Currently the portal use SoftLayer_Account::getSecurityCertificate to retrieve the stored security certificates including SSL, use the following Python script:
import SoftLayer
from pprint import pprint as pp
USERNAME = 'set-me'
# Endpoint url that contains all the Api Services.
API_KEY = 'set-me'
# Generate one for you or your users, or view yours at
https://control.softlayer.com/account/users
client = SoftLayer.create_client_from_env(username=USERNAME,
api_key=API_KEY)
accountService = client['SoftLayer_Account']
try:
""""
getSecurityCertificates() retrieves stored security certificates (ie. SSL)
"""""
result = accountService.getSecurityCertificates()
pp(result)
except SoftLayer.SoftLayerAPIError as e:
""""
If there was an error returned from the SoftLayer API then bomb out with the
error message.
"""""
print("Unable to retrieve the Account's stored security certificates (i.e. SSL) . %s %s " % (e.faultCode, e.faultString))
To find the virtual ip addresses associated you should use the method getAdcLoadBalancers and send the id value obtained in the previous method, try this Rest request.
https://[username]:[apiKey]#api.softlayer.com/rest/v3.1/SoftLayer_Account/getAdcLoadBalancers?objectFilter={"adcLoadBalancers":{"securityCertificateId":{"operation":[id]}}}
Remember to change the username and apiKey for valid credentials, and the id mentioned above to retrieve the associated load balancer ip addresses.

python linkedin oauth2 - where is http_api.py?

I'm trying to get this example to work from https://github.com/ozgur/python-linkedin. I'm using his example. When I run this code. I don't get the RETURN_URL and authorization_code talked about in the example. I'm not sure why, I think it is because I'm not setting up the HTTP API example correctly. I can't find http_api.py, and when I visit http://localhost:8080, I get a "this site can't be reached".
from linkedin import linkedin
API_KEY = 'wFNJekVpDCJtRPFX812pQsJee-gt0zO4X5XmG6wcfSOSlLocxodAXNMbl0_hw3Vl'
API_SECRET = 'daJDa6_8UcnGMw1yuq9TjoO_PMKukXMo8vEMo7Qv5J-G3SPgrAV0FqFCd0TNjQyG'
RETURN_URL = 'http://localhost:8000'
authentication = linkedin.LinkedInAuthentication(API_KEY, API_SECRET, RETURN_URL, linkedin.PERMISSIONS.enums.values())
# Optionally one can send custom "state" value that will be returned from OAuth server
# It can be used to track your user state or something else (it's up to you)
# Be aware that this value is sent to OAuth server AS IS - make sure to encode or hash it
#authorization.state = 'your_encoded_message'
print authentication.authorization_url # open this url on your browser
application = linkedin.LinkedInApplication(authentication)
http_api.py is one of the examples provided in the package. This is an HTTP server that will handle the response from LinkedIn's OAuth end point, so you'll need to boot it up for the example to work.
As stated in the guide, you'll need to execute that example file to get the server working. Note you'll also need to supply the following environment variables: LINKEDIN_API_KEY and LINKEDIN_API_SECRET.
You can run the example file by downloading the repo and calling LINKEDIN_API_KEY=yourkey LINKEDIN_API_SECRET=yoursecret python examples/http_api.py. Note you'll need Python 3.4 for it to work.

Categories

Resources