JSON Web Token no longer has 'decode' attribute despite no changes - python

I have a function which provides a token for a user so they can access a video chat room using the Twilio Video API. Following their docs, we decode a JSON Web Token with the following code to give us their token:
token = AccessToken(ACCOUNT_SID, API_KEY, API_SECRET, identity=f'{request.user.email}')
token.add_grant(VideoGrant(room='My Room'))
context = {
'token': token.to_jwt().decode()
}
This worked perfectly locally, and upon pushing it to live servers, continued to work there without fault. However, a few pushes later, with no changes to this code, and this error (below) has suddenly occurred. This also comes at the same time as another error, which is an ascii encoding error, which I believe may be linked.
It's strange that although no changes have been made to this token object, or the function, that we suddenly encounter this error with decoding this token. If anyone has any pointers, would be greatly appreciated.

PyJWT changed the return type of jwt.encode from bytes to str: https://pyjwt.readthedocs.io/en/stable/changelog.html#jwt-encode-return-type
Solution is to either downgrade/pin the PyJWT package to 1.7.1 or remove the call to decode
context = {
'token': token.to_jwt()
}

Related

difficulty getting oauth 2 token file with yagmail

I am trying to use the python package yagmail to send emails but am having a tough time getting authorization to work.
My issue is getting an Oauth 2 token, but there is a disconnect with yagmail, as specified in a github thread. As stated in this post, https://github.com/kootenpv/yagmail/issues/143, it appears that google does not supply the credential file in the correct format. But I tried a bunch of things and each has its own problem.
When I set up a Client ID in the Google API console, download the
json as credentials.json and let the system create the token.json,
things work to a point: I am brought through google to "pick an
account, do you want to continue" and token is created. I am able to
print labels for the gmail account. But when I issue
yag.send(to='xxx#gmail.com', subject='Testing Yagmail',
contents='Hurray, it worked!'), I get an error "TypeError:
refresh_authorization() got an unexpected keyword argument 'token'."
When I look at the token file, it does contain the key 'token' which
it should not per this github post https://github.com/kootenpv/yagmail/issues/143#issuecomment-527115298][2].
If I go into the token and edit it to reflect the the expected
contents as identified in the above link by removing keys that are
not specified and prefixing the names with 'google_', I get an error
"ValueError: Authorized user info was not in the expected format,
missing fields refresh_token, client_id, client_secret." It doesn't
seem to like the 'google_' prefix.
editing the token file as above without the 'google_' prefix seems to
get further producing a different error "An error occurred:
<HttpError 403 when requesting
https://gmail.googleapis.com/gmail/v1/users/me/labels?alt=json
returned "Request had insufficient authentication scopes"
I am stuck. Relatively new to Oauth2, but it seems others are able to use yagmail. Is there a trick I am missing? I originally posted on Github because I found that other related post, but it seems SO is more geared toward Q&A. Is there a relation betweeen Github and SO? Difference?
Thanks for any assistance,
Brian
I finally found a solution and the answer was hidden in plain sight.
First the Oauth authorization needed to be set up as outlined in this post (which is excellent): Sending email via Gmail & Python
As stated, when yagmail is run the first time the authorization process gives instructions, the final stating to "Navigate to the following URL to auth:" and asks "Enter the localhost URL you were redirected to:"
The problem is the browser window shows what appears to be an error message, a sad face with a message "This site can’t be reached, localhost refused to connect, reload", etc. I never thought this was expected behavior. The url is the one being navigated to in the error screen.
Simply stating the error should be expected, and the url needs to be copied and pasted in the post above would help a lot.

Missing secret gists in GitHub's API

According to the documentation, I can either send the request with authorization (token) in order to get all of my gists, or anonymously and I will get public popular gists.
My Python code is:
url = "https://api.github.com/gists"
with Get(
url,
headers={"Accept": accept},
params={"since": since, "per_page": per_page, "page": page},
auth=("token", token)
) as response:
return response
When token is set to None, I get all public gists (not mine) and when token is set to my OAuth token, I get all of my gists.
However, the issue is that it only gives me my non-secret gists instead of secret and public together.
Initially I was thinking that my token was wrong and therefore I was not getting the secret gists, but turns out that the token is correct (for sure, I can even post new gists) and also has permissions to read/write gists, and that is why it is weird.
The issue is also not related to either params or headers, tested.
Additional Information:
Get is a class which implements a context-manager and sends a get request [link].
After a long research I found out that GitHub's OAuth token from Developer Settings is not enough to perform this action and I need to create a GitHub App in order to extend GitHub.
I used this tool:
https://github.com/defunkt/gist
in order to ask GitHub for such a particular token (which is being used in the GitHub App), and then I started using it, and it worked!
With the new fine grained personal access tokens this can now be done without a GitHub App:
You need to give read-write access to Gists under Account Permissions:

ROPC flow: The request body must contain the following parameter: 'client_assertion' or 'client_secret'

I am testing MSAL and ROPC flow. When I run sample https://github.com/Azure-Samples/ms-identity-python-desktop I get error:
python username_password_sample.py parameters.json
invalid_client
AADSTS7000218: The request body must contain the following parameter: 'client_assertion' or 'client_secret'.
I was having same problem while following MS documentation and used curl. Please check my other question where SO community helped me to solve problem. After adding application registration secret to curl request, I got valid token and I was able to fetch mails.
What is confusing me is fact that secret is not present in parameters.json file.
You missed the step No.6 from this link: https://github.com/Azure-Samples/ms-identity-python-desktop/tree/master/1-Call-MsGraph-WithUsernamePassword
If the application was set as Public Client, then the secret is not required.
For more details, you can read https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth-ropc#authorization-request for reference.

ast.literal_eval InternalServerError

I'm working with Python for AWS Lambda. I have Profile PATCH and Profile DELETE api calls. PATCH request works fine. DELETE returns InternalServerError on
profile_request = ast.literal_eval(str(event['_body']))
PATCH uses the exact same thing. I even tried copy-pasting the line from PATCH to make sure there wasn't a typo. I've seen people say a syntax issue can cause this. I copy-pasted the body from PATCH and replaced profile with archive_status to make sure there wasn't a syntax issue. event is a dictionary. event['_body'] is unicode type. Both requests have the exact same Authorization and Headers requirements. I checked CloudWatch logs to make sure the body is actually listed as _body in the event. I can't figure out why I am getting the InternalServerError for DELETE profile and not PATCH profile. Any suggestions would be appreciated.
PATCH body:
{
"AccessToken": "eyJraWQiOiIzRUZtNG1lRE45c1wvQU9XUGdiYkZJNm1wakVkOVRFR05PYTY0SXJZOWRDdz0iLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIwZTQ1ZDYyZi1mYzQxLTQxNTQtYjZiOC04NjBiNTdkZWNkOGUiLCJkZXZpY2Vfa2V5IjoidXMtZWFzdC0xXzhkYTA3NWQ2LWEzNmItNDZhNS04YjAyLWY3ZmNjY2UxMGZiOCIsInRva2VuX3VzZSI6ImFjY2VzcyIsInNjb3BlIjoiYXdzLmNvZ25pdG8uc2lnbmluLnVzZXIuYWRtaW4iLCJpc3MiOiJodHRwczpcL1wvY29nbml0by1pZHAudXMtZWFzdC0xLmFtYXpvbmF3cy5jb21cL3VzLWVhc3QtMV96dnNGSkYzRzEiLCJleHAiOjE1MDYzNzI2MjksImlhdCI6MTUwNjM2OTAyOSwianRpIjoiMDU3NTkwMzItYmQ5YS00MzljLWJjM2QtYWY2OGZiZjJmNTBiIiwiY2xpZW50X2lkIjoiNWNscm9vNDhtamZpb2QzaDRoZGgyZDNvZHUiLCJ1c2VybmFtZSI6IjBlNDVkNjJmLWZjNDEtNDE1NC1iNmI4LTg2MGI1N2RlY2Q4ZSJ9.G2-gGYyZajrsAikFPuuttYQAj1Yc7uj-UUz469NEUk0SoEFvcJ3E6MOINmIWYg1W6BwJG09W4C_DvBrbybNZep-TtVoAHqNeuiEubt4IABeycZ9ELhKS4G-aaIvbV6CTVFVljFcUdTqCBf0TP7kxKp6P2kRRx08PjCqfQX-34XE-YJt2FyIGzSc958OT0MasROdHxr_ozJS6LGHw52BF1UG1llpR3YPGtMe8Gz9Y65RFEIDIpXllShKSFogvy-rdJFnaJqAYYn6WfjtmqDUjYzFTNZmGNOWy7_BxGFX90lUBB0V47k8M4nxvC_qWQ9o1LTkCYXIDD9sWMCtY4ewYmw",
"profile": {
"name": "Test"
}
}
DELETE body:
{
"AccessToken": "eyJraWQiOiIzRUZtNG1lRE45c1wvQU9XUGdiYkZJNm1wakVkOVRFR05PYTY0SXJZOWRDdz0iLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIwZTQ1ZDYyZi1mYzQxLTQxNTQtYjZiOC04NjBiNTdkZWNkOGUiLCJkZXZpY2Vfa2V5IjoidXMtZWFzdC0xXzhkYTA3NWQ2LWEzNmItNDZhNS04YjAyLWY3ZmNjY2UxMGZiOCIsInRva2VuX3VzZSI6ImFjY2VzcyIsInNjb3BlIjoiYXdzLmNvZ25pdG8uc2lnbmluLnVzZXIuYWRtaW4iLCJpc3MiOiJodHRwczpcL1wvY29nbml0by1pZHAudXMtZWFzdC0xLmFtYXpvbmF3cy5jb21cL3VzLWVhc3QtMV96dnNGSkYzRzEiLCJleHAiOjE1MDYzNzI2MjksImlhdCI6MTUwNjM2OTAyOSwianRpIjoiMDU3NTkwMzItYmQ5YS00MzljLWJjM2QtYWY2OGZiZjJmNTBiIiwiY2xpZW50X2lkIjoiNWNscm9vNDhtamZpb2QzaDRoZGgyZDNvZHUiLCJ1c2VybmFtZSI6IjBlNDVkNjJmLWZjNDEtNDE1NC1iNmI4LTg2MGI1N2RlY2Q4ZSJ9.G2-gGYyZajrsAikFPuuttYQAj1Yc7uj-UUz469NEUk0SoEFvcJ3E6MOINmIWYg1W6BwJG09W4C_DvBrbybNZep-TtVoAHqNeuiEubt4IABeycZ9ELhKS4G-aaIvbV6CTVFVljFcUdTqCBf0TP7kxKp6P2kRRx08PjCqfQX-34XE-YJt2FyIGzSc958OT0MasROdHxr_ozJS6LGHw52BF1UG1llpR3YPGtMe8Gz9Y65RFEIDIpXllShKSFogvy-rdJFnaJqAYYn6WfjtmqDUjYzFTNZmGNOWy7_BxGFX90lUBB0V47k8M4nxvC_qWQ9o1LTkCYXIDD9sWMCtY4ewYmw",
"archive_status": "archive"
}
Sorry. I'm fairly new to python and brand new to json and AWS. Another company setup chalice for deployment and a skeleton of our api with some basic code. I'm supposed to figure it all out and learn how to make it work as my company wants it to. I was testing through postman. The lambda code logger that goes to cloudwatch was how I was tracking down the issue. It turns out the issue was the log line immediately after ast.literal_eval:
profile_request = ast.literal_eval(str(event['_body']))
logger.info(profile_request)
I assumed not seeing that log in cloudwatch meant the ast.literal_eval was the issue. Finding a way to test locally so I could see the trace allowed me to figure out the log line was causing AttributeError: 'dict' object has no attribute 'strip'. That's an easy one so the problem is resolved. Glad I know how to test locally now also. Thank you for replying so quickly.

Google service account access token request: "Required parameter is missing: grant_type"

UPDATE: Solved. I was indeed making a basic mistake, among other things. My usage of the session.headers.update function was incorrect; instead of session.headers.update = {foo:bar}, I needed to be doing session.headers.update({foo:bar}). Additionally, I changed the following section of code:
From this:
payload = urllib.parse.urlencode({
"grant_type":"urn:ietf:params:oauth:grant-type:jwt-bearer",
"assertion": str(token)
})
To this:
payload = "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=" + token.decode("utf-8")
The code now works as intended.
Original question below
I've seen several hits on SO and Google about this problem; none of them have helped, although I've certainly made sure to double-check my code to make sure I'm not guilty of the same problems they detail. The problems people tend to have involve passing the POST data as parameters or POSTing to the wrong URL, which I'm not doing, as far as I can tell. Additionally, most of the hits I've found involve 3-legged OAuth2 involving users; I've found comparatively few hits pertaining to service accounts and JWTs, which differ enough from the user flow that I'm concerned about how relevant they are to my problem.
I'm trying to get an access token from the Google Authentication server for a service account. I've generated my JWT and now want to POST to the server to receive back my access token. I've set the headers according to the documentation described here, under "Making the access token request," and as far as I can tell, my request is up to spec, but Google responds back with a 400 response, and the following JSON:
{'error': 'invalid_request', 'error_description': 'Required parameter is missing: grant_type'}
Here's the code causing the problem:
# Returns the session, now with the Host and Authorization headers set.
def gAuthenticate(session):
token = createJWT()
session.headers.update = {
"Host": "www.googleapis.com",
"Content-Type": "application/x-www-form-urlencoded"
}
payload = urllib.parse.urlencode({
"grant_type":"urn:ietf:params:oauth:grant-type:jwt-bearer",
"assertion": str(token)
})
response = session.post("https://www.googleapis.com/oauth2/v4/token", data = payload)
session.headers.update = {"Authorization": "Bearer " + response.json()["access_token"]}
return session
I'm having a lot of strange issues with this code. First of all, if I don't urllib.parse.urlencode my dictionary (i.e. simply payload = {dictionary}), I get only a Bad Request / 'invalid_request' error, which I assume from the less specific error message means that this is less acceptable than what I'm currently doing. Why do I have to do this? Isn't Requests supposed to encode my data for me? I've never had this problem when POSTing with Requests before.
Second, examining the prepared request before it's sent reveals that my headers aren't being correctly set, despite the header update. Neither of the headers I've added to the request are being transmitted.
I've examined the request body and it looks to be identical (except of course the content of the JWT) to the one that Google provides as an example in the documentation.
All of this leads me to believe that I'm making a very basic error somewhere, but I haven't had any success finding it. What am I doing wrong here? Links to any helpful documentation would be extremely appreciated; thanks for your time and attention.
Try "grant_type": "authorization_code". And add grant type as header.
Also, check this link - Accessing Google oAuth giving invalid grant_type (Bad request)

Categories

Resources