diff --git a/docs/examples/linkedin.rst b/docs/examples/linkedin.rst index 71dd733..a4009e4 100644 --- a/docs/examples/linkedin.rst +++ b/docs/examples/linkedin.rst @@ -9,41 +9,31 @@ command line interactive example below. .. code-block:: pycon - >>> # Imports - >>> import os - >>> from requests_oauthlib import OAuth2Session - - >>> # Set environment variables - >>> os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1' - >>> # Credentials you get from registering a new application >>> client_id = '' >>> client_secret = '' - >>> # LinkedIn OAuth2 requests require scope and redirect_url parameters. - >>> # Ensure these values match the auth values in your LinkedIn App - >>> # (see auth tab on LinkedIn Developer page) - >>> scope = ['r_liteprofile'] - >>> redirect_url = 'http://127.0.0.1' - >>> # OAuth endpoints given in the LinkedIn API documentation - >>> authorization_base_url = 'https://www.linkedin.com/oauth/v2/authorization' - >>> token_url = 'https://www.linkedin.com/oauth/v2/accessToken' + >>> authorization_base_url = 'https://www.linkedin.com/uas/oauth2/authorization' + >>> token_url = 'https://www.linkedin.com/uas/oauth2/accessToken' + + >>> from requests_oauthlib import OAuth2Session + >>> from requests_oauthlib.compliance_fixes import linkedin_compliance_fix - >>> linkedin = OAuth2Session(client_id, redirect_uri='http://127.0.0.1', scope=scope) + >>> linkedin = OAuth2Session(client_id, redirect_uri='http://127.0.0.1') + >>> linkedin = linkedin_compliance_fix(linkedin) >>> # Redirect user to LinkedIn for authorization >>> authorization_url, state = linkedin.authorization_url(authorization_base_url) - >>> print(f"Please go here and authorize: {authorization_url}") + >>> print 'Please go here and authorize,', authorization_url >>> # Get the authorization verifier code from the callback url - >>> redirect_response = input('Paste the full redirect URL here:') + >>> redirect_response = raw_input('Paste the full redirect URL here:') >>> # Fetch the access token >>> linkedin.fetch_token(token_url, client_secret=client_secret, - ... include_client_id=True, ... authorization_response=redirect_response) >>> # Fetch a protected resource, i.e. user profile - >>> r = linkedin.get('https://api.linkedin.com/v2/me') - >>> print(r.content) + >>> r = linkedin.get('https://api.linkedin.com/v1/people/~') + >>> print r.content diff --git a/requests_oauthlib/compliance_fixes/__init__.py b/requests_oauthlib/compliance_fixes/__init__.py index 0e8e3ac..3b77e74 100644 --- a/requests_oauthlib/compliance_fixes/__init__.py +++ b/requests_oauthlib/compliance_fixes/__init__.py @@ -2,6 +2,7 @@ from .facebook import facebook_compliance_fix from .fitbit import fitbit_compliance_fix +from .linkedin import linkedin_compliance_fix from .slack import slack_compliance_fix from .instagram import instagram_compliance_fix from .mailchimp import mailchimp_compliance_fix diff --git a/requests_oauthlib/compliance_fixes/linkedin.py b/requests_oauthlib/compliance_fixes/linkedin.py new file mode 100644 index 0000000..cd5b4ac --- /dev/null +++ b/requests_oauthlib/compliance_fixes/linkedin.py @@ -0,0 +1,21 @@ +from json import loads, dumps + +from oauthlib.common import add_params_to_uri, to_unicode + + +def linkedin_compliance_fix(session): + def _missing_token_type(r): + token = loads(r.text) + token["token_type"] = "Bearer" + r._content = to_unicode(dumps(token)).encode("UTF-8") + return r + + def _non_compliant_param_name(url, headers, data): + token = [("oauth2_access_token", session.access_token)] + url = add_params_to_uri(url, token) + return url, headers, data + + session._client.default_token_placement = "query" + session.register_compliance_hook("access_token_response", _missing_token_type) + session.register_compliance_hook("protected_request", _non_compliant_param_name) + return session diff --git a/tests/test_compliance_fixes.py b/tests/test_compliance_fixes.py index 5c90d52..3e3fff8 100644 --- a/tests/test_compliance_fixes.py +++ b/tests/test_compliance_fixes.py @@ -14,6 +14,7 @@ from requests_oauthlib import OAuth2Session from requests_oauthlib.compliance_fixes import facebook_compliance_fix from requests_oauthlib.compliance_fixes import fitbit_compliance_fix +from requests_oauthlib.compliance_fixes import linkedin_compliance_fix from requests_oauthlib.compliance_fixes import mailchimp_compliance_fix from requests_oauthlib.compliance_fixes import weibo_compliance_fix from requests_oauthlib.compliance_fixes import slack_compliance_fix @@ -99,6 +100,43 @@ def test_refresh_token(self): self.assertEqual(token["refresh_token"], "refresh") +class LinkedInComplianceFixTest(TestCase): + def setUp(self): + mocker = requests_mock.Mocker() + mocker.post( + "https://www.linkedin.com/uas/oauth2/accessToken", + json={"access_token": "linkedin"}, + ) + mocker.post( + "https://api.linkedin.com/v1/people/~/shares", + status_code=201, + json={ + "updateKey": "UPDATE-3346389-595113200", + "updateUrl": "https://www.linkedin.com/updates?discuss=abc&scope=xyz", + }, + ) + mocker.start() + self.addCleanup(mocker.stop) + + linkedin = OAuth2Session("someclientid", redirect_uri="https://i.b") + self.session = linkedin_compliance_fix(linkedin) + + def test_fetch_access_token(self): + token = self.session.fetch_token( + "https://www.linkedin.com/uas/oauth2/accessToken", + client_secret="someclientsecret", + authorization_response="https://i.b/?code=hello", + ) + self.assertEqual(token, {"access_token": "linkedin", "token_type": "Bearer"}) + + def test_protected_request(self): + self.session.token = {"access_token": "dummy-access-token"} + response = self.session.post("https://api.linkedin.com/v1/people/~/shares") + url = response.request.url + query = parse_qs(urlparse(url).query) + self.assertEqual(query["oauth2_access_token"], ["dummy-access-token"]) + + class MailChimpComplianceFixTest(TestCase): def setUp(self): mocker = requests_mock.Mocker()