Unicode-objects must be encoded before hashing when requesting data using Flask-OAuth - python-3.x

I'm integrating Google's login with a Flask site using Flask-OAuth.
Everything is working fine. I can authorise the login and get a token back etc without any difficulties. But when I use Flask-OAuth's get method to request the logged in user's email address I get an error saying:
TypeError: Unicode-objects must be encoded before hashing
I'm using Python3 and this has the smell of a Python version issue but I can't figure out what I'd need to change.
The code I'm using is this:
def get_additional_data(self):
access_token = session.get('oauth_token')
headers = {'Authorization': 'OAuth ' + access_token[0]}
return self.service.get(
'https://www.googleapis.com/oauth2/v1/userinfo', None,
headers=headers)
I'm not sure what I can encode in that request. Even if I don't pass the headers I get the same error (rather than an invalid request or something like that).
I've run 2to3 on oauth2/__init__.py and the tweaks is suggests are very minor and shouldn't prevent the code from running in Python 3. Also, everything else OAuth2 related is working.

The bad news is that the solution to this problem is switching to Flask-OAuthlib.
The good news is it required very few changes from Flask-OAuth to get it working.

Related

Python Sockets 400 status when authorization ends with "="

I am trying to a socket application in Python3. However, in some tests it gives 400 status code even though some tests are working correctly. Then I realized that this causes whenever the Authorization header line ends with "=".
I use base64 for authorization header line
token = b.b64encode(u_p.encode()).decode()
Here u_p is the username and password in format of username:password.
How can I solve this issue, I am new to socket so I may make an obvious mistake. THanls for your sharings.

When I use pycurl to execute a curl command, I get error 3 "illegal characters found in URL" but when pasting said URL in Chome, it can be resolved

Good day. I'm writing a python program that requests some posts from my Facebook page. To do so, Facebook offers a tool that they call "Graph API Explorer". Using something similar to a GET request, I can get anything that I want (granted that I have access and a valid token). I've come up with my own solution for the Graph API Explorer and that is generating my URLs. After generating a URL, I use pycurl to get a JSON object from Facebook that contains all of my data.
When I use pycurl, I get the following error:
pycurl.error: (3, 'Illegal characters found in URL')
but when printing said URL and pasting it to a browser, I got a valid response.
URL: https://graph.facebook.com/v7.0/me?fields=posts%7Bmessage%2Cfrom%7D&access_token=<and my access token which is valid>
my code looks like this:
def get_posts_curl(nodes=['posts'], fields=[['message', 'from']], token_file='Facebook/token.txt'):
curl = pycurl.Curl()
response = BytesIO()
token = get_token_from_file(token_file)
# constructing request.
url = parse_facebook_url_request(nodes, fields, token)
url = convert_to_curl(url)
print("---URL---: " + url)
# curl session and settings.
curl.setopt(curl.CAINFO, certifi.where())
curl.setopt(curl.URL, url)
curl.setopt(curl.WRITEDATA, response)
curl.perform()
curl.close()
return response.getvalue().decode('utf-8')
The error pops up at curl.perform()
Some info that might be relevant:
All was working great a while ago. After transferring my program from my workstation (that is running Windows 10) to my server (Ubuntu 18.04 Server) still, all was working fine and I placed that project to the side. Only now that error pops up and I haven't touched the project in a while.
It seems that the token is causing the issue. I've tried about 100 tokens and some cause the problem and some don't. Also, a fix that solved it all was using urllib3.unquote
from urllib.parse import unquote
...
url = unquote(url)

PRIVACY Card API Authorization

I have recently been working with API's but I am stuck on one thing and it's been holding me back for a few days.
I am trying to work with Privacy's API and I do not understand the Authentication/Authorization process. When I enter the url in a browser I get the error "message": "Please provide API key in Authorization header", even when I use the correct format of Authorization. I also get an error when I make a request in Python. The format I'm using for the url is https://api.privacy.com/v1/card "Authorization: api-key:".
If someone could explain how to work this or simply give an example of how I would make a request through Python3. The API information is in the link below.
Thank you in advance.
https://developer.privacy.com/docs
This is the code I am using in Python. After I run this I receive a 401 status code.
import requests
headers={'Authorization': 'api-key:200e6036-6894-xxxx-xxxx-xxxx'}
url = 'https://api.privacy.com/v1/card'
r = requests.get(url)
print("Status code:", r.status_code)
You need to add the authentication header to the get call. It isn't enough to include it in a header variable. You need to provide those headers to requests
import requests
response = requests.get('https://api.privacy.com/v1/card', headers={'Authorization': 'api-key 65a9566c-XXXXXXXXXXXX'})
print(response.json())

JWT Token auth using python requests for DockerHub

I found out that editing a full_description of a DockerHub repository can be done via a JavaScript API, and figured this would be a fun excuse to learn the requests package for python. The JavaScript API definitely works, e.g. using this simple docker image.
The JS API basically does
Send a POST request to https://hub.docker.com/v2/users/login with the username and password. The server responds with a token.
Send a PATCH request to the specific https://hub.docker.com/v2/repositories/{user or org}/{repo}, making sure the header has Authorization: JWT {token}, and in this case with content body of {"full_description":"...value..."}.
What is troubling is that the PATCH request on the python side gets a 200 response back from the server (if you intentionally set a bad auth token, you get denied as expected). But it's response actually contains the current information (not the patched info).
The only "discoveries" I've made:
If you add the debug logging stuff, there's a 301. But this is the same URL for the javascript side, so it doesn't matter?
send: b'{"full_description": "TEST"}'
reply: 'HTTP/1.1 301 MOVED PERMANENTLY\r\n'
The token received by doing a POST in requests is the same as if I GET to auth.docker.io as decribed in Getting a Bearer Token section here. Notably, I didn't specify a password (just did curl -X GET ...). This is not true. They are different, I don't know how I thought they were the same.
This second one makes me feel like I'm missing a step. Like I need to decode the token or something? I don't know what else to make of this, especially the 200 response from the PATCH despite no changes.
The code:
import json
from textwrap import indent
import requests
if __name__ == "__main__":
username = "<< SET THIS VALUE >>"
password = "<< SET THIS VALUE >>"
repo = "<< SET THIS VALUE >>"
base_url = "https://hub.docker.com/v2"
login_url = f"{base_url}/users/login"
repo_url = f"{base_url}/repositories/{username}/{repo}"
# NOTE: if I use a `with requests.Session()`, then I'll get
# CSRF Failed: CSRF token missing or incorrect
# Because I think that csrftoken is only valid for login page (?)
# Get login token and create authorization header
print("==> Logging into DockerHub")
tok_req = requests.post(login_url, json={"username": username, "password": password})
token = tok_req.json()["token"]
headers = {"Authorization": f"JWT {token}"}
print(f"==> Sending PATCH request to {repo_url}")
payload = {"full_description": "TEST"}
patch_req = requests.patch(repo_url, headers=headers, json=payload)
print(f" Response (status code: {patch_req.status_code}):")
print(indent(json.dumps(patch_req.json(), indent=2), " "))
Additional information related to your CSRF problem when using requests.Session():
It seems that Docker Hub is not recognizing csrftoken named header/cookie (default name of the coming cookie), when making requests in this case.
Instead, when using header X-CSRFToken on the following requests, CSRF is identified as valid.
Maybe reason is cookie-to-header token pattern.
Once updating session header with cookie of login response
s.headers.update({"X-CSRFToken": s.cookies.get("csrftoken")})
There is no need to set JWT token manually anymore for further requests - token works as cookie already.
Sorry, no enough privileges to just comment, but I think this is relevant enough.
As it turns out the JWT {token} auth was valid the entire time. Apparently, you need a / at the end of the URL. Without it, nothing happens. LOL!
# ----------------------------------------------------V
repo_url = f"{base_url}/repositories/{username}/{repo}/"
As expected, the PATCH then responds with the updated description, not the old description. WOOOOT!
Important note: this is working for me as of January 15th 2020, but in my quest I came across this dockerhub issue that seems to indicate that if you have 2FA enabled on your account, you can no longer edit the description using a PATCH request. I don't have 2FA on my account, so I can (apparently). It's unclear what the future of that will be.
Related note: the JWT token has remained the same the entire time, so for any web novices like myself, don't share those ;)

DocuSign Authorization Code Grant flow returns invalid_grant error

The DocuSign documentation goes through an easy to follow authorization flow for code grant. I'm able to get the "code" from the initial GET request to /oath/auth but getting the tokens gives me an error of "invalid_grant" when I try in postman. I've followed the steps and have a request that looks like this using account-d.docusign.com for host:
POST /oauth/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic MjMwNTQ2YTctOWM1NS00MGFkLThmYmYtYWYyMDVkNTQ5NGFkOjMwODc1NTVlLTBhMWMtNGFhOC1iMzI2LTY4MmM3YmYyNzZlOQ==
grant_type=authorization_code&code=ey2dj3nd.AAAA39djasd3.dkn4449d21d
Two other members of my team have also tried with their developer accounts and all are getting invalid_grant errors. Is this no longer supported or are there common errors associated with this error that we might be able to investigate?
Re-check all of your values.
I was also getting the same invalid_grant response and could not figure out why at first. It turns out that I had a typo in the Content-Type header. I was using application/x-www-form-urlencode instead of application/x-www-form-urlencoded.
You may not be, but if you are submitting the exact Authorization Header as you've posted it here in your question (MjMwNTQ2YTctOWM1NS00MGFkLThmYmYtYWYyMDVkNTQ5NGFkOjMwODc1NTVlLTBhMWMtNGFhOC1iMzI2LTY4MmM3YmYyNzZlOQ==) it will fail with that message.
That is the base64 value for the sample integration key and sample secret key provided in their documentation. If you decode that string with an online base64decoder it will result in 230546a7-9c55-40ad-8fbf-af205d5494ad:3087555e-0a1c-4aa8-b326-682c7bf276e9. This is the same sample integration key and secret in the documentation.
Check the Authorization header you are submitting by encoding your integration key and secret (integrationKey:secret) using this online base64encoder. This will make sure the issue isn't with your base64 encoding of your integration key and secret. Once you have that value make sure your Authorization uses the word Basic before the value you got from this website. (Basic base64stringFromOnlineEncoder)
Check that the code your are submitting in the body of the post is not the sample code from their documentation. ey2dj3nd.AAAA39djasd3.dkn4449d21d is the sample code from their documentation. You may just be using that in your question as a placeholder but if you are submitting any of those values it will return invalid_grant. Make sure that the body of your post does not have any leading or trailing spaces.
Have the correct Content-Type set application/x-www-form-urlencoded
Have the correct Authorization header set Basic base64EncodedIntegrationKey:Secret
Have the correct body using the valid code received from the GET request to /oauth/auth with no leading or trailing spaces, making sure you're not using the values from your question.
If you are still having trouble and you are not doing a user application but are doing a service integration you can use Legacy Authentication to get your oAuth2 token.
Alternative Method using Legacy Authentication for Service Integrations
This method does not use a grant code. You pass in the integration key, username and password into the X-DocuSign-Authentication header in JSON format.
Demo Server: demo.docusign.net
Production Server: www.docusign.net API
Version: v2
POST https://{server}/restapi/{apiVersion}/oauth2/token
Content-Type: application/x-www-form-urlencoded
X-DocuSign-Authentication: {"IntegratorKey":"your_integrator_key","Password":"docusign_account_password","Username":"docusign_account_username"}
grant_type=password&client_id=your_integrator_key&username=docusign_account_username&password=docusign_account_password&scope=api
If you are building a user application that requires the user enter their docusign credentials to generate the token, this alternative will not work for you.
For anyone who is facing this error, I'd like to point out this note in the documentation:
Note: The obtained authorization code is only viable for 2 minutes. If more then two minutes pass between obtaining the authorization code and attempting to exchange it for an access token, the operation will fail.
I was struggling with the same error until I spotted the note and sped up my typing to meet the 2 minutes.
Hope it helps someone else.
In my case the problem was related to having set a wrong value for Content-Type header, namely "application/x-www-form-URIencoded" instead of the correct "application/x-www-form-urlencoded". Note though that in my case the problem was not a "typo" but an excessive trust in DocuSign documentation.
Indeed the wrong Content-Type is, at the time of writing, suggested directly into the documentation page where they describe the Authorization Code Grant workflow, see the image below for the relevant part.
Hopefully they will fix the documentation soon but for the time being be careful not to blindly copy & paste the code from their examples without thinking, as I initially did.
anyone have an idea what is wrong here I am getting a BadRequest with the following
{"error":"invalid_grant","error_description":"unauthorized_client"}
var client = new RestClient(ESIGNURL);
var request = new RestRequest("/oauth/token");
request.Method = Method.POST;
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddHeader("Authorization", "Basic " + Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(integrationkey+ ":" + secret)));
string body = "grant_type=authorization_code&code=" + code;
request.Parameters.Clear();
request.AddParameter("application/x-www-form-urlencoded", body, ParameterType.RequestBody);
var response = client.Execute(request);
I was getting this error as well. What I realized is I was appending the state at the end of the code before passing it to the oauth token endpoint.
This snippet is from Docusign explaining what are some other reasons for getting that error.
Invalid-error explanation
I just spent a day doing this (in NodeJS). I'll add a couple of things to the answers from before. First, I had to put:
"Content-Type": "application/x-www-form-urlencoded"
in the header. Otherwise it gave me the message:
{
"error": "invalid_grant",
"error_description": "unsupported_grant_type"
}
Second, the base64 encoding:
I used this in NodeJS and it worked
const integration_key = process.env.INTEGRATION_KEY;
const secret_key = process.env.SECRET_KEY;
const authinfo =
integration_key.toString("utf8") + ":" + secret_key.toString("utf8");
const buff2 = Buffer(authinfo, "utf8").toString("base64");
If you use "base64url" it dosen't work because it strips the == off of the end of the string. The = symbol is used as padding and apparently it's needed. You see a similar difference on this site https://www.base64encode.org/ when you toggle the url safe encoding option. If you don't have the padding on the end of your base64 encoded string (or if it's generally incorrect) you get this message:
{
"error": "invalid_grant",
"error_description": "unauthorized_client"
}
Finally, if you're using Postman (I'm using DocuSign's Postman Collection) remember to reset and save the codeFromUrl variable after you update it. Otherwise it doesn't update and you get the message:
{
"error": "invalid_grant",
"error_description": "expired_client_token"
}
This means the old URL code has expired and your new one didn't save.

Resources