Check which applications can be included in their `Google Data Protocol Directory`_
+--------
+LinkedIn
+--------
+LinkedIn setup is similar to any other OAuth service. To request extra fields
+using `LinkedIn fields selectors`_ just define the setting::
+
+ LINKEDIN_EXTRA_FIELD_SELECTORS = [...]
+
+with the needed fields selectors, also define LINKEDIN_EXTRA_DATA properly, that
+way the values will be stored in ``UserSocialAuth.extra_data`` field.
+
+By default ``id``, ``first-name`` and ``last-name`` are requested and stored.
+
+
-------
Testing
-------
.. _django-social-auth: https://convore.com/django-social-auth/
.. _Convore: https://convore.com/
.. _Selenium: http://seleniumhq.org/
+.. _LinkedIn fields selectors: http://developer.linkedin.com/docs/DOC-1014
No extra configurations are needed to make this work.
"""
-from cgi import parse_qs
from xml.etree import ElementTree
from xml.parsers.expat import ExpatError
-from social_auth.backends import ConsumerBasedOAuth, OAuthBackend
+from django.conf import settings
+
+from social_auth.backends import ConsumerBasedOAuth, OAuthBackend, USERNAME
LINKEDIN_SERVER = 'linkedin.com'
LINKEDIN_AUTHORIZATION_URL = 'https://www.%s/uas/oauth/authenticate' % \
LINKEDIN_SERVER
LINKEDIN_CHECK_AUTH = 'https://api.%s/v1/people/~' % LINKEDIN_SERVER
+# Check doc at http://developer.linkedin.com/docs/DOC-1014 about how to use
+# fields selectors to retrieve extra user data
+LINKEDIN_FIELD_SELECTORS = ['id', 'first-name', 'last-name']
class LinkedinBackend(OAuthBackend):
"""Linkedin OAuth authentication backend"""
name = 'linkedin'
+ EXTRA_DATA = [('id', 'id'),
+ ('first-name', 'first_name'),
+ ('last-name', 'last_name')]
def get_user_details(self, response):
"""Return user details from Linkedin account"""
- return {
- 'first_name': response['first-name'],
- 'last_name': response['last-name'],
- 'email': '', # not supplied
- }
+ first_name, last_name = response['first-name'], response['last-name']
+ return {USERNAME: first_name + last_name,
+ 'fullname': first_name + ' ' + last_name,
+ 'first_name': first_name,
+ 'last_name': last_name,
+ 'email': ''}
class LinkedinAuth(ConsumerBasedOAuth):
def user_data(self, access_token):
"""Return user data provided"""
- request = self.oauth_request(access_token, LINKEDIN_CHECK_AUTH)
+ fields_selectors = LINKEDIN_FIELD_SELECTORS + \
+ getattr(settings, 'LINKEDIN_EXTRA_FIELD_SELECTORS',
+ [])
+ url = LINKEDIN_CHECK_AUTH + ':(%s)' % ','.join(fields_selectors)
+ request = self.oauth_request(access_token, url)
raw_xml = self.fetch_response(request)
try:
xml = ElementTree.fromstring(raw_xml)
- data = _xml_to_dict(xml)
- url = data['site-standard-profile-request']['url']
- url = url.replace('&', '&')
- data['id'] = parse_qs(url)['key'][0]
+ data = to_dict(xml)
return data
except (ExpatError, KeyError, IndexError):
return None
return True
-def _xml_to_dict(xml):
- """Convert xml structure to dict"""
- data = {}
- for child in xml.getchildren():
- if child.getchildren():
- data[child.tag] = _xml_to_dict(child)
- else:
- data[child.tag] = child.text
-
- return data
+def to_dict(xml):
+ """Convert XML structure to dict recursively"""
+ return dict((node.tag, to_dict(node) if node.getchildren() else node.text)
+ for node in xml.getchildren())
# Backend definition