Skip to content

Instantly share code, notes, and snippets.

@lebedov
Last active February 13, 2019 02:46
Show Gist options
  • Save lebedov/8c3f33ebb55a67b732c1 to your computer and use it in GitHub Desktop.
Save lebedov/8c3f33ebb55a67b732c1 to your computer and use it in GitHub Desktop.
Get LinkedIn access tokens without having to open a web browser.
#!/usr/bin/env python
"""
Get LinkedIn OAuth1 access tokens without having to open a web browser.
Notes
-----
Based upon https://developer.linkedin.com/documents/getting-oauth-token-python
Assumes that the application API key, secret key, user name, and password are stored
in an a configuration file formatted as follows:
[Secrets]
API_KEY = WWWWWWWW
SECRET_KEY = XXXXXXXX
NAME = YYYYYYYY
PASSWORD = ZZZZZZZZ
"""
import re
import ConfigParser as cp
import oauth2
import urlparse
import lxml.html
import mechanize
# Read secrets:
cfg_file = 'linkedin_config'
config = cp.ConfigParser()
config.read(cfg_file)
if not config.has_section('Secrets'):
raise RuntimeError('no secrets specified')
secrets = {}
for s in config.items('Secrets'):
secrets[s[0]] = s[1]
# Set up headless browser:
br = mechanize.Browser()
br.set_cookiejar(mechanize.CookieJar())
br.set_handle_redirect(True)
br.set_handle_robots(False)
# Get request token:
consumer = oauth2.Consumer(secrets['api_key'], secrets['secret_key'])
client = oauth2.Client(consumer)
request_token_url = 'https://api.linkedin.com/uas/oauth/requestToken'
response, content = client.request(request_token_url, 'POST')
if response['status'] != '200':
raise Exception('Invalid response')
request_token = dict(urlparse.parse_qsl(content))
print 'request token: ', request_token['oauth_token']
print 'request token secret: ', request_token['oauth_token_secret']
# Use token to redirect to user login:
authorize_url = 'https://api.linkedin.com/uas/oauth/authorize'
redirect_url = '%s?oauth_token=%s' % (authorize_url, request_token['oauth_token'])
# Login with mechanize:
br.open(redirect_url)
br.select_form(nr=0)
br.form['session_key'] = secrets['username']
br.form['session_password'] = secrets['password']
br.submit()
html = br.response().read()
tree = lxml.html.fromstring(html)
oauth_verifier = tree.xpath('.//div[@class="access-code"]')[0].text_content()
# Use PIN to obtain access token:
token = oauth2.Token(request_token['oauth_token'],
request_token['oauth_token_secret'])
token.set_verifier(oauth_verifier)
access_token_url = 'https://api.linkedin.com/uas/oauth/accessToken'
client = oauth2.Client(consumer, token)
response, content = client.request(access_token_url, 'POST')
access_token = dict(urlparse.parse_qsl(content))
print 'access token: ', access_token['oauth_token']
print 'access token secret: ', access_token['oauth_token_secret']
#!/usr/bin/env python
"""
Get LinkedIn OAuth2 access tokens without having to open a web browser.
Notes
-----
Requires `python-linkedin <https://github.com/ozgur/python-linkedin>`_.
Assumes that the application API key and secret key are stored in a
configuration file formatted as follows:
[Secrets]
API_KEY = WWWWWWWW
SECRET_KEY = XXXXXXXX
"""
import re
import ConfigParser as cp
import urlparse
from linkedin import linkedin
import mechanize
from mechanize import _response
from mechanize import _rfc3986
# Read secrets:
cfg_file = 'linkedin_config'
config = cp.ConfigParser()
config.read(cfg_file)
if not config.has_section('Secrets'):
raise RuntimeError('no secrets specified')
secrets = {}
for s in config.items('Secrets'):
secrets[s[0]] = s[1]
class MyRedirectHandler(mechanize.HTTPRedirectHandler):
def http_error_302(self, req, fp, code, msg, headers):
# Code from mechanize._urllib2_fork.HTTPRedirectHandler:
if 'location' in headers:
newurl = headers.getheaders('location')[0]
elif 'uri' in headers:
newurl = headers.getheaders('uri')[0]
else:
return
newurl = _rfc3986.clean_url(newurl, "latin-1")
newurl = _rfc3986.urljoin(req.get_full_url(), newurl)
new = self.redirect_request(req, fp, code, msg, headers, newurl)
if new is None:
return
if hasattr(req, 'redirect_dict'):
visited = new.redirect_dict = req.redirect_dict
if (visited.get(newurl, 0) >= self.max_repeats or
len(visited) >= self.max_redirections):
raise HTTPError(req.get_full_url(), code,
self.inf_msg + msg, headers, fp)
else:
visited = new.redirect_dict = req.redirect_dict = {}
visited[newurl] = visited.get(newurl, 0) + 1
fp.read()
fp.close()
# If the redirected URL doesn't match
new_url = new.get_full_url()
if not re.search('^http(?:s)?\:\/\/.*www\.linkedin\.com', new_url):
return _response.make_response('', headers.items(), new_url, 200, 'OK')
else:
return self.parent.open(new)
http_error_301 = http_error_303 = http_error_307 = http_error_302
http_error_refresh = http_error_302
# Set up headless browser:
br = mechanize.Browser()
br.set_cookiejar(mechanize.CookieJar())
br.handler_classes['_redirect'] = MyRedirectHandler
br.set_handle_redirect(True)
br.set_handle_robots(False)
return_uri = 'http://localhost:9000'
auth = linkedin.LinkedInAuthentication(secrets['api_key'],
secrets['secret_key'],
return_uri,
linkedin.PERMISSIONS.enums.values())
br.open(auth.authorization_url)
br.select_form(nr=0)
br.form['session_key'] = secrets['username']
br.form['session_password'] = secrets['password']
r = br.submit()
auth.authorization_code = urlparse.parse_qs(urlparse.urlsplit(r.geturl()).query)['code']
access_token = auth.get_access_token()
app = linkedin.LinkedInApplication(token=access_token)
@hpcergar
Copy link

Thanks for your gist, it was very helpful :)

Just a fix for the oauth1 code credentials example:
[Secrets]
...
USERNAME = YYYYYYYY

@gkohnen
Copy link

gkohnen commented Jul 11, 2017

Hello!
would it be possible to port these to python 3? It looks like at least the "mechanize" package does not work on python 3.
Thanks ,
Georges

@shanto12
Copy link

Can you write this in python 3 with requests library please

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment