-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Added support for NGPVAN ActionID OpenID #750
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -98,6 +98,7 @@ Social backends | |
moves | ||
naszaklasa | ||
nationbuilder | ||
ngpvan_actionid | ||
odnoklassnikiru | ||
openstreetmap | ||
orbi | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
NGP VAN ActionID | ||
================ | ||
|
||
`NGP VAN`_'s ActionID_ service provides an OpenID 1.1 endpoint, which provides | ||
first name, last name, email address, and phone number. | ||
|
||
ActionID doesn't require major settings beside being defined on | ||
``AUTHENTICATION_BACKENDS`` | ||
|
||
.. code-block:: python | ||
|
||
SOCIAL_AUTH_AUTHENTICATION_BACKENDS = ( | ||
... | ||
'social.backends.ngpvan.ActionIDOpenID', | ||
... | ||
) | ||
|
||
|
||
If you want to be able to access the "phone" attribute offered by NGP VAN | ||
within ``extra_data`` you can add the following to your settings: | ||
|
||
.. code-block:: python | ||
|
||
SOCIAL_AUTH_ACTIONID_OPENID_AX_EXTRA_DATA = [ | ||
('http://openid.net/schema/contact/phone/business', 'phone') | ||
] | ||
|
||
|
||
NGP VAN offers the ability to have your domain whitelisted, which will disable | ||
the "{domain} is requesting a link to your ActionID" warning when your app | ||
attempts to login using an ActionID account. Contact | ||
`NGP VAN Developer Support`_ for more information | ||
|
||
.. _NGP VAN: http://www.ngpvan.com/ | ||
.. _ActionID: http://developers.ngpvan.com/action-id | ||
.. _NGP VAN Developer Support: http://developers.ngpvan.com/support/contact |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
""" | ||
NGP VAN's `ActionID` Provider | ||
|
||
http://developers.ngpvan.com/action-id | ||
""" | ||
from social.backends.open_id import OpenIdAuth | ||
from openid.extensions import ax | ||
|
||
|
||
class ActionIDOpenID(OpenIdAuth): | ||
""" | ||
NGP VAN's ActionID OpenID 1.1 authentication backend | ||
""" | ||
name = 'actionid-openid' | ||
URL = 'https://accounts.ngpvan.com/Home/Xrds' | ||
USERNAME_KEY = 'email' | ||
|
||
def get_ax_attributes(self): | ||
""" | ||
Return the AX attributes that ActionID responds with, as well as the | ||
user data result that it must map to. | ||
""" | ||
return [ | ||
('http://openid.net/schema/contact/internet/email', 'email'), | ||
('http://openid.net/schema/contact/phone/business', 'phone'), | ||
('http://openid.net/schema/namePerson/first', 'first_name'), | ||
('http://openid.net/schema/namePerson/last', 'last_name'), | ||
('http://openid.net/schema/namePerson', 'fullname'), | ||
] | ||
|
||
def setup_request(self, params=None): | ||
""" | ||
Setup the OpenID request | ||
|
||
Because ActionID does not advertise the availiability of AX attributes | ||
nor use standard attribute aliases, we need to setup the attributes | ||
manually instead of rely on the parent OpenIdAuth.setup_request() | ||
""" | ||
request = self.openid_request(params) | ||
|
||
fetch_request = ax.FetchRequest() | ||
fetch_request.add(ax.AttrInfo( | ||
'http://openid.net/schema/contact/internet/email', | ||
alias='ngpvanemail', | ||
required=True)) | ||
|
||
fetch_request.add(ax.AttrInfo( | ||
'http://openid.net/schema/contact/phone/business', | ||
alias='ngpvanphone', | ||
required=False)) | ||
fetch_request.add(ax.AttrInfo( | ||
'http://openid.net/schema/namePerson/first', | ||
alias='ngpvanfirstname', | ||
required=False)) | ||
fetch_request.add(ax.AttrInfo( | ||
'http://openid.net/schema/namePerson/last', | ||
alias='ngpvanlastname', | ||
required=False)) | ||
request.addExtension(fetch_request) | ||
|
||
return request |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
"""Tests for NGP VAN ActionID Backend""" | ||
import datetime | ||
|
||
from httpretty import HTTPretty | ||
|
||
from social.p3 import urlencode | ||
from social.tests.backends.open_id import OpenIdTest | ||
|
||
|
||
JANRAIN_NONCE = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ') | ||
|
||
|
||
class NGPVANActionIDOpenIDTest(OpenIdTest): | ||
"""Test the NGP VAN ActionID OpenID 1.1 Backend""" | ||
backend_path = 'social.backends.ngpvan.ActionIDOpenID' | ||
expected_username = '[email protected]' | ||
discovery_body = ' '.join( | ||
[ | ||
'<?xml version="1.0" encoding="UTF-8"?>', | ||
'<xrds:XRDS', | ||
'xmlns:xrds="xri://$xrds"', | ||
'xmlns:openid="http://openid.net/xmlns/1.0"', | ||
'xmlns="xri://$xrd*($v*2.0)">', | ||
'<XRD>', | ||
'<Service priority="10">', | ||
'<Type>http://specs.openid.net/auth/2.0/signon</Type>', | ||
'<Type>http://openid.net/extensions/sreg/1.1</Type>', | ||
'<Type>http://axschema.org/contact/email</Type>', | ||
'<URI>https://accounts.ngpvan.com/OpenId/Provider</URI>', | ||
'</Service>', | ||
'<Service priority="20">', | ||
'<Type>http://openid.net/signon/1.0</Type>', | ||
'<Type>http://openid.net/extensions/sreg/1.1</Type>', | ||
'<Type>http://axschema.org/contact/email</Type>', | ||
'<URI>https://accounts.ngpvan.com/OpenId/Provider</URI>', | ||
'</Service>', | ||
'</XRD>', | ||
'</xrds:XRDS>', | ||
]) | ||
server_response = urlencode({ | ||
'openid.claimed_id': 'https://accounts.ngpvan.com/user/abcd123', | ||
'openid.identity': 'https://accounts.ngpvan.com/user/abcd123', | ||
'openid.sig': 'Midw8F/rCDwW7vMz3y+vK6rjz6s=', | ||
'openid.signed': 'claimed_id,identity,assoc_handle,op_endpoint,return_' | ||
'to,response_nonce,ns.alias3,alias3.mode,alias3.type.' | ||
'alias1,alias3.value.alias1,alias3.type.alias2,alias3' | ||
'.value.alias2,alias3.type.alias3,alias3.value.alias3' | ||
',alias3.type.alias4,alias3.value.alias4,alias3.type.' | ||
'alias5,alias3.value.alias5,alias3.type.alias6,alias3' | ||
'.value.alias6,alias3.type.alias7,alias3.value.alias7' | ||
',alias3.type.alias8,alias3.value.alias8,ns.sreg,sreg' | ||
'.fullname', | ||
'openid.assoc_handle': '{635790678917902781}{GdSyFA==}{20}', | ||
'openid.op_endpoint': 'https://accounts.ngpvan.com/OpenId/Provider', | ||
'openid.return_to': 'http://myapp.com/complete/actionid-openid/', | ||
'openid.response_nonce': JANRAIN_NONCE + 'MMgBGEre', | ||
'openid.mode': 'id_res', | ||
'openid.ns': 'http://specs.openid.net/auth/2.0', | ||
'openid.ns.alias3': 'http://openid.net/srv/ax/1.0', | ||
'openid.alias3.mode': 'fetch_response', | ||
'openid.alias3.type.alias1': 'http://openid.net/schema/contact/phone/b' | ||
'usiness', | ||
'openid.alias3.value.alias1': '+12015555555', | ||
'openid.alias3.type.alias2': 'http://openid.net/schema/contact/interne' | ||
't/email', | ||
'openid.alias3.value.alias2': '[email protected]', | ||
'openid.alias3.type.alias3': 'http://openid.net/schema/namePerson/firs' | ||
't', | ||
'openid.alias3.value.alias3': 'John', | ||
'openid.alias3.type.alias4': 'http://openid.net/schema/namePerson/las' | ||
't', | ||
'openid.alias3.value.alias4': 'Smith', | ||
'openid.alias3.type.alias5': 'http://axschema.org/namePerson/first', | ||
'openid.alias3.value.alias5': 'John', | ||
'openid.alias3.type.alias6': 'http://axschema.org/namePerson/last', | ||
'openid.alias3.value.alias6': 'Smith', | ||
'openid.alias3.type.alias7': 'http://axschema.org/namePerson', | ||
'openid.alias3.value.alias7': 'John Smith', | ||
'openid.alias3.type.alias8': 'http://openid.net/schema/namePerson', | ||
'openid.alias3.value.alias8': 'John Smith', | ||
'openid.ns.sreg': 'http://openid.net/extensions/sreg/1.1', | ||
'openid.sreg.fullname': 'John Smith', | ||
}) | ||
|
||
def setUp(self): | ||
"""Setup the test""" | ||
super(NGPVANActionIDOpenIDTest, self).setUp() | ||
|
||
# Mock out the NGP VAN endpoints | ||
HTTPretty.register_uri( | ||
HTTPretty.POST, | ||
'https://accounts.ngpvan.com/Home/Xrds', | ||
status=200, | ||
body=self.discovery_body) | ||
HTTPretty.register_uri( | ||
HTTPretty.GET, | ||
'https://accounts.ngpvan.com/user/abcd123', | ||
status=200, | ||
body=self.discovery_body) | ||
HTTPretty.register_uri( | ||
HTTPretty.GET, | ||
'https://accounts.ngpvan.com/OpenId/Provider', | ||
status=200, | ||
body=self.discovery_body) | ||
|
||
def test_login(self): | ||
"""Test the login flow using python-social-auth's built in test""" | ||
self.do_login() | ||
|
||
def test_partial_pipeline(self): | ||
"""Test the partial flow using python-social-auth's built in test""" | ||
self.do_partial_pipeline() | ||
|
||
def test_get_ax_attributes(self): | ||
"""Test that the AX attributes that NGP VAN responds with are present""" | ||
records = self.backend.get_ax_attributes() | ||
|
||
self.assertEqual( | ||
records, | ||
[ | ||
('http://openid.net/schema/contact/internet/email', 'email'), | ||
('http://openid.net/schema/contact/phone/business', 'phone'), | ||
('http://openid.net/schema/namePerson/first', 'first_name'), | ||
('http://openid.net/schema/namePerson/last', 'last_name'), | ||
('http://openid.net/schema/namePerson', 'fullname'), | ||
] | ||
) | ||
|
||
def test_setup_request(self): | ||
"""Test the setup_request functionality in the NGP VAN backend""" | ||
# We can grab the requested attributes by grabbing the HTML of the | ||
# OpenID auth form and pulling out the hidden fields | ||
_, inputs = self.get_form_data(self.backend.auth_html()) | ||
|
||
# Confirm that the only required attribute is email | ||
self.assertEqual(inputs['openid.ax.required'], 'ngpvanemail') | ||
|
||
# Confirm that the 3 optional attributes are requested "if available" | ||
self.assertIn('ngpvanphone', inputs['openid.ax.if_available']) | ||
self.assertIn('ngpvanfirstname', inputs['openid.ax.if_available']) | ||
self.assertIn('ngpvanlastname', inputs['openid.ax.if_available']) | ||
|
||
# Verify the individual attribute properties | ||
self.assertEqual( | ||
inputs['openid.ax.type.ngpvanemail'], | ||
'http://openid.net/schema/contact/internet/email') | ||
self.assertEqual( | ||
inputs['openid.ax.type.ngpvanfirstname'], | ||
'http://openid.net/schema/namePerson/first') | ||
self.assertEqual( | ||
inputs['openid.ax.type.ngpvanlastname'], | ||
'http://openid.net/schema/namePerson/last') | ||
self.assertEqual( | ||
inputs['openid.ax.type.ngpvanphone'], | ||
'http://openid.net/schema/contact/phone/business') | ||
|
||
def test_user_data(self): | ||
"""Ensure that the correct user data is being passed to create_user""" | ||
self.strategy.set_settings({ | ||
'USER_FIELDS': [ | ||
'email', | ||
'first_name', | ||
'last_name', | ||
'username', | ||
'phone', | ||
'fullname' | ||
] | ||
}) | ||
user = self.do_start() | ||
|
||
self.assertEqual(user.username, u'[email protected]') | ||
self.assertEqual(user.email, u'[email protected]') | ||
self.assertEqual(user.extra_user_fields['phone'], u'+12015555555') | ||
self.assertEqual(user.extra_user_fields['first_name'], u'John') | ||
self.assertEqual(user.extra_user_fields['last_name'], u'Smith') | ||
self.assertEqual(user.extra_user_fields['fullname'], u'John Smith') | ||
|
||
def test_extra_data_phone(self): | ||
"""Confirm that you can get a phone number via the relevant setting""" | ||
self.strategy.set_settings({ | ||
'SOCIAL_AUTH_ACTIONID_OPENID_AX_EXTRA_DATA': [ | ||
('http://openid.net/schema/contact/phone/business', 'phone') | ||
] | ||
}) | ||
user = self.do_start() | ||
self.assertEqual(user.social_user.extra_data['phone'], u'+12015555555') | ||
|
||
def test_association_uid(self): | ||
"""Test that the correct association uid is stored in the database""" | ||
user = self.do_start() | ||
self.assertEqual( | ||
user.social_user.uid, | ||
'https://accounts.ngpvan.com/user/abcd123') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added this here to better mimic what Django allows during the create_user step and thus better test
USER_FIELDS