Skip to content
Merged
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
24 changes: 22 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ authentication = linkedin.LinkedInAuthentication(API_KEY, API_SECRET, RETURN_URL
# It can be used to track your user state or something else (it's up to you)
# Be aware that this value is sent to OAuth server AS IS - make sure to encode or hash it
#authorization.state = 'your_encoded_message'
print authentication.authorization_url # open this url on your browser
print(authentication.authorization_url) # open this url on your browser
application = linkedin.LinkedInApplication(authentication)
```
When you grant access to the application, you will be redirected to the return url with the following query strings appended to your **RETURN_URL**:
Expand All @@ -95,7 +95,27 @@ This means that the value of the **authorization_code** is **AQTXrv3Pe1iWS0EQvLg

```python
authentication.authorization_code = 'AQTXrv3Pe1iWS0EQvLg0NJA8ju_XuiadXACqHennhWih7iRyDSzAm5jaf3R7I8'
authentication.get_access_token()
token = authentication.get_access_token()
```

you can retrieve the access token though the namedtuple returned by the get_access_token method

```python
print(token.access_token)
print(token.expires_in)
```

optionally, LinkedIn supports programmatic refresh tokens for all approved Marketing Developer Platform (MDP) partners, so
you can obtain a new access token passing a refresh token to the refresh_access_token method, more info [here](https://docs.microsoft.com/en-us/linkedin/shared/authentication/programmatic-refresh-tokens)

```python
refresh_token = token.refresh_token # the token previously obtained via Oauth
authentication = linkedin.LinkedInAuthentication(API_KEY, API_SECRET)
new_token = authentication.refresh_access_token(refresh_token)
print(new_token.access_token)
print(new_token.expires_in)
print(new_token.refresh_token)
print(new_token.refresh_token_expires_in)
```

After you get the access token, you are now permitted to make API calls on behalf of the user who granted access to you app. In addition to that, in order to prevent from going through the OAuth flow for every consecutive request,
Expand Down
101 changes: 73 additions & 28 deletions linkedin_v2/linkedin.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import requests
from requests_oauthlib import OAuth1

from .models import AccessToken
from .models import *
from .utils import enum, to_utf8, raise_for_error, StringIO
import json
import urllib
Expand Down Expand Up @@ -75,6 +75,9 @@ def __init__(self, consumer_key, consumer_secret, user_token, user_secret,
self.permissions = permissions


REDIRECT_URI_ERROR_MESSAGE = 'You must init LinkedInAuthentication with a redirect_url'
AUTHORIZATION_CODE_ERROR_MESSAGE = 'You must first get the authorization code'

class LinkedInAuthentication(object):
"""
Implements a standard OAuth 2.0 flow that involves redirection for users to
Expand All @@ -83,7 +86,7 @@ class LinkedInAuthentication(object):
AUTHORIZATION_URL = 'https://www.linkedin.com/uas/oauth2/authorization'
ACCESS_TOKEN_URL = 'https://www.linkedin.com/uas/oauth2/accessToken'

def __init__(self, key, secret, redirect_uri, permissions=None):
def __init__(self, key, secret, redirect_uri=None, permissions=None):
self.key = key
self.secret = secret
self.redirect_uri = redirect_uri
Expand All @@ -95,6 +98,7 @@ def __init__(self, key, secret, redirect_uri, permissions=None):

@property
def authorization_url(self):
assert self.redirect_uri, REDIRECT_URI_ERROR_MESSAGE
qd = {'response_type': 'code',
'client_id': self.key,
'scope': (' '.join(self.permissions)).strip(),
Expand All @@ -115,19 +119,52 @@ def _make_new_state(self):
self.secret).encode("utf8")
).hexdigest()

def get_access_token(self, timeout=60):
assert self.authorization_code, 'You must first get the authorization code'
@staticmethod
def _get_token_from_response(response):
response = response.json()
access_token = response.get(ACCESS_TOKEN_KEY)
expires_in = response.get(EXPIRES_IN_KEY)
refresh_token = response.get(REFRESH_TOKEN_KEY)
refresh_token_expires_in = response.get(REFRESH_TOKEN_EXPIRES_IN_KEY)

return AccessToken(
access_token,
expires_in,
refresh_token or None,
refresh_token_expires_in or None
)

def get_access_token(self, timeout=TIMEOUT):
assert self.authorization_code, AUTHORIZATION_CODE_ERROR_MESSAGE
assert self.redirect_uri, REDIRECT_URI_ERROR_MESSAGE
qd = {'grant_type': 'authorization_code',
'code': self.authorization_code,
'redirect_uri': self.redirect_uri,
'client_id': self.key,
'client_secret': self.secret}
response = requests.post(
self.ACCESS_TOKEN_URL, data=qd, timeout=timeout)
response = requests.post(self.ACCESS_TOKEN_URL, data=qd, timeout=timeout)
raise_for_error(response)
response = response.json()
self.token = AccessToken(
response['access_token'], response['expires_in'])

self.token = self._get_token_from_response(response)
return self.token

def refresh_access_token(self, refresh_token, timeout=TIMEOUT):
"""
Exchanges a Refresh Token for a New Access Token
:param refresh_token: str
:param timeout: int
:return: AccessToken
"""
qd = {
'grant_type': 'refresh_token',
REFRESH_TOKEN_KEY: refresh_token,
'client_id': self.key,
'client_secret': self.secret
}
response = requests.post(self.ACCESS_TOKEN_URL, data=qd, timeout=timeout)
raise_for_error(response)

self.token = self._get_token_from_response(response)
return self.token


Expand All @@ -153,10 +190,10 @@ def __init__(self, authentication=None, token=None):
self.authentication = authentication
if not self.authentication:
self.authentication = LinkedInAuthentication('', '', '')
self.authentication.token = AccessToken(token, None)
self.authentication.token = AccessToken(token, None, None, None)

def make_request(self, method, url, data=None, params=None, headers=None,
timeout=60):
timeout=TIMEOUT):
if headers is None:
headers = {'x-li-format': 'json',
'Content-Type': 'application/json'}
Expand Down Expand Up @@ -231,41 +268,49 @@ def search_profile(self, params):
raise_for_error(response)
return response.json()

def post_share(self, post_type='person', company_id=None, comment=None, title=None, description=None,
submitted_url=None, submitted_image_url=None,
visibility_code='anyone'):
post_owner = ''
def post_share(self, post_type='person', company_id=None, title=None, description=None,
submitted_url=None, submitted_image_url=None):

if post_type == 'organization':
post_owner = "urn:li:organization:%s" % company_id
else:
post_owner = "urn:li:person:%s" % self.get_profile()['id']

post = {
"owner": post_owner,
"text": {
"text": description
"text": ""
},
"subject": title,
"subject": "",
"distribution": {
"linkedInDistributionTarget": {}
},
"content": {
"contentEntities": [
{
"entityLocation": "",
"thumbnails": []
}
],
"contentEntities": [],
"title": ""
}
}
if comment is not None:
post['comment'] = comment

content_entity = {
"entityLocation": "",
"thumbnails": []
}

if title is not None:
post['content']['title'] = title
post['subject'] = title

if description is not None:
post['text']['text'] = description

if submitted_url is not None:
post['content']['submitted-url'] = submitted_url
content_entity['entityLocation'] = submitted_url

if submitted_image_url is not None:
post['content']['contentEntities']['thumbnails'][0]['resolvedUrl'] = submitted_image_url
content_entity['thumbnails'] = [{"imageSpecificContent": {}, "resolvedUrl": submitted_image_url}]

post['content']['contentEntities'] = [content_entity]

response = self.make_request(
'POST', ENDPOINTS.SHARE, data=json.dumps(post))
return response.json()
Expand Down Expand Up @@ -318,7 +363,7 @@ def get_post_comments(self, selectors, params=None, **kwargs):
print(url)
response = self.make_request(
'GET', url, params=params)
# raise_for_error(response)
raise_for_error(response)
return response.json()

def get_group(self, group_id, params=None, headers=None):
Expand Down
Loading