Skip to content

Support non-x-www-form-urlencoded bodies returned from refresh_token_request compliance hook #545

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions requests_oauthlib/compliance_fixes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
from .weibo import weibo_compliance_fix
from .plentymarkets import plentymarkets_compliance_fix
from .ebay import ebay_compliance_fix
from .wix import wix_compliance_fix
36 changes: 36 additions & 0 deletions requests_oauthlib/compliance_fixes/wix.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import json
from oauthlib.common import urldecode

"""
Wix requires the request body for token requests to be sent in JSON format
instead of x-www-form-urlencoded.
"""
def wix_compliance_fix(session):

def _non_compliant_access_token_request_body(
url: str, headers: dict, request_kwargs: dict
):
"""
Move the request body from the `data` kwarg to the `json` kwarg,
and set the `Content-Type` header to `application/json`.
"""
headers["Content-Type"] = "application/json"
request_kwargs["json"] = request_kwargs["data"]
del request_kwargs["data"]
return url, headers, request_kwargs

def _non_compliant_refresh_token_request_body(
token_url: str, headers: dict, body: str
):
"""
Convert the body from a urlencoded string to a JSON string,
and set the `Content-Type` header to `application/json`.
"""
headers["Content-Type"] = "application/json"
body = json.dumps(dict(urldecode(body)))
return token_url, headers, body

session.register_compliance_hook("access_token_request", _non_compliant_access_token_request_body)
session.register_compliance_hook("refresh_token_request", _non_compliant_refresh_token_request_body)

return session
5 changes: 4 additions & 1 deletion requests_oauthlib/oauth2_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -473,9 +473,12 @@ def refresh_token(
log.debug("Invoking refresh_token_request hook %s.", hook)
token_url, headers, body = hook(token_url, headers, body)

if headers['Content-Type'] == "application/x-www-form-urlencoded":
body = dict(urldecode(body))

r = self.post(
token_url,
data=dict(urldecode(body)),
data=body,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To check the content type instead of catching the ValueError, this could just become:

data=body if headers["Content-Type"] == "application/json" else dict(urldecode(body)),

auth=auth,
timeout=timeout,
headers=headers,
Expand Down
44 changes: 44 additions & 0 deletions tests/test_compliance_fixes.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from requests_oauthlib.compliance_fixes import instagram_compliance_fix
from requests_oauthlib.compliance_fixes import plentymarkets_compliance_fix
from requests_oauthlib.compliance_fixes import ebay_compliance_fix
from requests_oauthlib.compliance_fixes.wix import wix_compliance_fix


class FacebookComplianceFixTest(TestCase):
Expand Down Expand Up @@ -385,3 +386,46 @@ def test_refresh_token(self):
"https://example.com/refresh",
)
assert token["token_type"] == "Bearer"


class WixComplianceFixTest(TestCase):

def setUp(self):
wix = OAuth2Session()
self.session = wix_compliance_fix(wix)

def test_access_token_request_sent_as_json(self):
mocker = requests_mock.Mocker()
mocker.post(
"https://www.wixapis.com/oauth/access",
request_headers={"Content-Type": "application/json"},
json={"access_token": "sample_access_token", "refresh_token": "sample_refresh_token"},
additional_matcher=lambda req: req.json() == {'grant_type': 'authorization_code', 'code': 'sample_code'}
)
mocker.start()
self.addCleanup(mocker.stop)

token = self.session.fetch_token(
"https://www.wixapis.com/oauth/access",
code="sample_code"
)

self.assertEqual(token, {"access_token": "sample_access_token", "refresh_token": "sample_refresh_token"})

def test_refresh_token_request_sent_as_json(self):
mocker = requests_mock.Mocker()
mocker.post(
"https://www.wixapis.com/oauth/access",
request_headers={"Content-Type": "application/json"},
json={"access_token": "sample_access_token", "refresh_token": "sample_refresh_token"},
additional_matcher=lambda req: req.json() == {'grant_type': 'refresh_token', 'refresh_token': 'sample_refresh_token'}
)
mocker.start()
self.addCleanup(mocker.stop)

token = self.session.refresh_token(
"https://www.wixapis.com/oauth/access",
refresh_token="sample_refresh_token"
)

self.assertEqual(token, {"access_token": "sample_access_token", "refresh_token": "sample_refresh_token"})