PyJWT validate custom claims - python

Been using authlib for a while and it has been real easy to validate both the existence of a claim but also its value. According to the example:
claims_options = {
"iss": { "essential": True, "value": "https://idp.example.com" },
"aud": { "essential": True, "value": "api1" },
"email": { "essential": True, "value": "user1#email.com" },
}
claims = jwt.decode(token, jwk, claims_options=claims_options)
claims.validate()
However, with PyJWT I find it to be a bit unclear. I only seem to be able to check for the existence of a claim but not its value (aud and iss obviously works):
decoded_token = jwt.decode(
token,
key,
audience="api1",
issuer="issuer"
algorithms=["RS256"],
options={"require": ["exp", "iss", "aud", "email"]}
)
This is even mentioned in the documentation. However, the documentation seem incomplete. Simply put, is it possible to validate custom claim values or do I simply need to manually parse the decoded token and look for my desired values?

In PyJWT you can validate the issuer and audience, not just the existence of the claim but also the value. For custom claims the situation is different as mentioned on the end of my answer.
The values for the validation of audience and issuer are optional parameters after the options in the decode function.
The documentation mentions this under the options parameter
verify_aud=True but will be ignored if verify_signature is False.
Check that aud (audience) claim matches audience
So the conclusion is, that you need to parse custum claims manually.
import jwt
token = jwt.encode({"iss":"me", "aud":"you"}, "secret", "HS256")
print(token)
decoded_token = jwt.decode(token, "secret", ["HS256"],
{"verify_aud":"true", "verify_iss":"true"},
audience="you", issuer="me")
print(decoded_token)
If you create the token like shown above, the output will be:
{'iss': 'me', 'aud': 'you'}
If you change the "aud" claim in the token to a different value, you'll get an exception:
jwt.exceptions.InvalidAudienceError: Invalid audience
Regarding custom claims, the part that was added to the question after I wrote about the audience and issuer claims, the documentation is IMHO quite clear:
require=[] list of claims that must be present. E.g. require=["exp", "iat", "nbf"]. Only verifies that the claims exists. Does NOT verify that the claims are valid.
And there's also no place where you could add the values. Even for audience and issuer these values are provided as optional parameters instead of being part of the options dictionary.
So the conclusion is that you would need to verify custom claims manually after you successfully decoded the token.

Related

Preventing script-exiting exceptions when finding JSON values that might not exist?

How would I prevent KeyError when grabbing a value from an API query JSON?
Explanation: I am calling an API for a user one at a time. Each user returns a JSON with a set of information. I am creating a dictionary from a few specific pieces of information in that JSON. However, sometimes users don't have a specific value (such as Role or Username). How can I write my program so it doesn't throw an exception and end the program when it can't find this value? And instead of throwing an exception it just enters a blank for that specific dictionary value? I have looked into .get but I cannot seem to get it to work with my specific use case.
Code:
APResponse = requests.request('POST', APurl, headers=headers, json={'offset': str(AssetPandaCurrentUser), 'limit': '1'})
APResponseJSON = APResponse.json()
AssetPandaUserDict = {
"Username": [APResponseJSON['objects'][0]['data']['field_52']],
"Role": [APResponseJSON['objects'][0]['data']['field_49']['value']],
"User Status": [APResponseJSON['objects'][0]['data']['field_6']['value']],
"Laptop Status": [],
"Laptop Serial": []
}
So if "Role" is blank or missing in the JSON, it will just set "Role" to a blank instead of throwing a KeyError exception.
I have tried this:
"Username": [APResponseJSON.get(['objects'][0]['data']['field_52'])]
But I get an error
TypeError: string indices must be integers
Any ideas?
You can use get() method.
For example:
"Role": [APResponseJSON.get(['objects'][0].get("data", {}).get("field_49"{}).get("value")]
of course if you are sure that APResponseJSON.get(['objects'][0] exists.
You can also prepare pydantic model to easily parse your response.

Google Business Profile API readMask

After the deprecation of my discovery url, I had to make some change on my code and now I get this error.
googleapiclient.errors.HttpError: <HttpError 400 when requesting https://mybusinessbusinessinformation.googleapis.com/v1/accounts/{*accountid*}/locations?filter=locationKey.placeId%3{*placeid*}&readMask=paths%3A+%22locations%28name%29%22%0A&alt=json returned "Request contains an invalid argument.". Details: "[{'#type': 'type.googleapis.com/google.rpc.BadRequest', 'fieldViolations': [{'field': 'read_mask', 'description': 'Invalid field mask provided'}]}]">
I am trying to use this end point accounts.locations.list
I'm using :
python 3.8
google-api-python-client 2.29.0
My current code look likes :
from google.protobuf.field_mask_pb2 import FieldMask
googleAPI = GoogleAPI.auth_with_credentials(client_id=config.GMB_CLIENT_ID,
client_secret=config.GMB_CLIENT_SECRET,
client_refresh_token=config.GMB_REFRESH_TOKEN,
api_name='mybusinessbusinessinformation',
api_version='v1',
discovery_service_url="https://mybusinessbusinessinformation.googleapis.com/$discovery/rest")
field_mask = FieldMask(paths=["locations(name)"])
outputLocation = googleAPI.service.accounts().locations().list(parent="accounts/{*id*}",
filter="locationKey.placeId=" + google_place_id,
readMask=field_mask
).execute()
From the error, i tried a lot of fieldmask path and still don't know what they want.
I've tried things like location.name, name, locations.name, locations.location.name and it did'nt work.
I also try to pass the readMask params without use the FieldMask class with passing a string and same problem.
So if someone know what is the format of the readMask they want it will be great for me !
.
Can help:
https://www.youtube.com/watch?v=T1FUDXRB7Ns
https://developers.google.com/google-ads/api/docs/client-libs/python/field-masks
You have not set the readMask correctly. I have done a similar task in Java and Google returns the results. readMask is a String type, and what I am going to provide you in the following line includes all fields. You can omit anyone which does not serve you. I am also writing the request code in Java, maybe it can help you better to convert into Python.
String readMask="storeCode,regularHours,name,languageCode,title,phoneNumbers,categories,storefrontAddress,websiteUri,regularHours,specialHours,serviceArea,labels,adWordsLocationExtensions,latlng,openInfo,metadata,profile,relationshipData,moreHours";
MyBusinessBusinessInformation.Accounts.Locations.List request= mybusinessaccountLocations.accounts().locations().list(accountName).setReadMask(readMask);
ListLocationsResponse Response = request.execute();
List<Location>= Response.getLocations();
while (Response.getNextPageToken() != null) {
locationsList.setPageToken(Response.getNextPageToken());
Response=request.execute();
locations.addAll(Response.getLocations());
}
-- about the question in the comment you have asked this is what I have for placeId:
As other people said you before, the readmask is not set correctly.
Here Google My Business Information api v1 you can see:
"This is a comma-separated list of fully qualified names of fields"
and here Google Json representation you can see the field.
With this, I tried this
read_mask = "name,title"
and it worked for me, hope this work for you too.

How to send a direct message with Twython?

I know this is a beginner question, but can someone please provide some sample code of sending a Twitter direct message (just text) with Twython? I can't seems to find a lot of specific documentation over this (I know it's briefly covered in the official docs but they aren't super clear to me). Thank you!
Solution
twitter.send_direct_message(event = {"type": "message_create",
"message_create":{"target": {"recipient_id": ID goes here},
"message_data":
{"text": "Hello World!"}}})
Explanation
In short, you take the raw JSON data that you would send as a POST request to Twitter, and use it as a parameter in the twitter.send_direct_message() function. When using the JSON as a parameter in Python, we must interpret it as a dictionary. This can be done by setting the parent object as the dictionary key, and what follows as the dictionary value. So, in my case the JSON:
{"event" : {"type": "message_create",
"message_create":{"target": {"recipient_id": ID goes here},
"message_data":
{"text": "Hello World!"}}}}
becomes:
event = {"type": "message_create",
"message_create":{"target": {"recipient_id": ID goes here},
"message_data":
{"text": "Hello World!"}}}
More information about what JSON data to send to Twitter for specific direct message requests can be found here.

Setting Extended Properties in Microsoft Graph API using proptags

I am trying to set a property tag according to Graph API documents as stated here: https://learn.microsoft.com/en-us/graph/api/resources/extended-properties-overview?view=graph-rest-1.0
I am trying to set a extended property on a MailFolder using the patch endpoint as listed here: https://learn.microsoft.com/en-us/graph/api/mailfolder-update?view=graph-rest-1.0&tabs=http
The content of the message is:
{'singleValueExtendedProperties': [{'id': 'String 0x10F4000B', 'value': 'true'}]}
However, i always get the result that indicates it fails due to a property value error:
{"error": {"code": "ErrorFolderSavePropertyError", "message": "The folder save operation failed due to invalid property values.", "innerError": { "request-id": "GUID", "date": "2019-11-12T19:44:02"}}}
Looking at the rules around MAPI and setting its values, it is trying to set the tag 0x10F4 using type 0x000B which is boolean. However, the extended properties only excepts strings. I am not sure how to set a boolean value into the value field.
the second half for the type goes where the string value is. So to post this, it will become
`{'singleValueExtendedProperties': [{'id': 'boolean 0x10F4', 'value': '**true**'}]}1

pact: how to check a a field in the response that may or may not exist

I have a service that will respond to a request with a json blob, such as this:
{
"field1": 1,
"field2": "2",
"array": [1,2,3]
}
I know that I can test array by using EachLike, like this:
expected = {
"field1": Like(1),
"field2": Like("2"),
"array": EachLike(1)
}
The issue is that "array" is an optional field in the response. It may not exist at all, and if it doesn't, I still need the contract to validate. How do I define that a field in the response body must match a type if it exists, but that it may not exist at all?
From https://github.com/pact-foundation/pact-ruby/wiki/FAQ#why-is-there-no-support-for-specifying-optional-attributes
Why is there no support for specifying optional attributes?
Firstly, it is assumed that you have control over the provider's data (and consumer's data) when doing the verification tests. If you don't, then maybe Pact is not the best tool for your situation.
Secondly, if you think about it, if Pact supports making an assertion that element $.body.name may be present in a response, then you write consumer code that can handle an optional $.body.name, but in fact, the provider gives $.body.firstname, no test will ever fail to tell you that you've made an incorrect assumption. Remember that a provider may return extra data without failing the contract, but it must provide at minimum the data you expect.
I would recommend that you write one interaction where you require that the array is populated, so that you know how to deal with a populated array. Then leave it out of the other interactions altogether, and then it won't matter whether it's populated or not.

Categories

Resources