Hello community, here is the log from the commit of package python-social-auth-core for openSUSE:Factory checked in at 2019-04-03 09:27:57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-social-auth-core (Old) and /work/SRC/openSUSE:Factory/.python-social-auth-core.new.25356 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "python-social-auth-core" Wed Apr 3 09:27:57 2019 rev:4 rq:690607 version:3.1.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-social-auth-core/python-social-auth-core.changes 2018-12-07 14:34:06.395167424 +0100 +++ /work/SRC/openSUSE:Factory/.python-social-auth-core.new.25356/python-social-auth-core.changes 2019-04-03 09:27:58.759814229 +0200 @@ -1,0 +2,21 @@ +Tue Apr 2 10:09:14 UTC 2019 - Tomáš Chvátal <tchvatal@suse.com> + +- Update to 3.1.0: + * Universe Ticketing backend + * Auth0.com authentication backend + * Update Bungie backend dropping any Django reference + * Enable and fix JWT related tests + * Remove PyPy support from Tox + * Drop support for Python 3.4 in Tox + * Allow to override JWT decode options in Open ID Connect base backend + * Pass access token via Authorization header to Google user data url + * Updated user_data method in AzureADOAuth2 to return access_token if id_token is not present in response + * Updated Azure B2C to extract first email from list if it's a list + * Replace deprecated Google+ API usage with https://www.googleapis.com/oauth2/v3/userinfo + * Updated Azure Tenant to fix Nonetype error + * Updated comment denoting incorrect setting name + * Yandex: do not fail when no email is present + * Mediawiki: do not fail when no email is present + * Mediawiki: enhance get_user_details to return more details + +------------------------------------------------------------------- Old: ---- social-auth-core-2.0.0.tar.gz New: ---- social-auth-core-3.1.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-social-auth-core.spec ++++++ --- /var/tmp/diff_new_pack.7hK5X0/_old 2019-04-03 09:27:59.703814674 +0200 +++ /var/tmp/diff_new_pack.7hK5X0/_new 2019-04-03 09:27:59.703814674 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-social-auth-core # -# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany. # Copyright (c) 2017-2018 Matthias Fehring <buschmann23@opensuse.org> # # All modifications and additions to the file contributed by third parties @@ -19,20 +19,21 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-social-auth-core -Version: 2.0.0 +Version: 3.1.0 Release: 0 Summary: Python Social Auth Core License: BSD-3-Clause Group: Development/Languages/Python URL: https://github.com/python-social-auth/social-core Source: https://files.pythonhosted.org/packages/source/s/social-auth-core/social-auth-core-%{version}.tar.gz -BuildRequires: %{python_module PyJWT >= 1.4.0} +BuildRequires: %{python_module PyJWT >= 1.7.1} BuildRequires: %{python_module coverage >= 3.6} BuildRequires: %{python_module cryptography >= 2.1.1} BuildRequires: %{python_module httpretty} BuildRequires: %{python_module mock} BuildRequires: %{python_module nose >= 1.2.1} BuildRequires: %{python_module oauthlib >= 1.0.3} +BuildRequires: %{python_module python-jose >= 3.0.0} BuildRequires: %{python_module rednose >= 0.4.1} BuildRequires: %{python_module requests >= 2.9.1} BuildRequires: %{python_module requests-oauthlib >= 0.6.1} @@ -46,9 +47,10 @@ BuildRequires: python3 >= 3.4.0 BuildRequires: python3-defusedxml >= 0.5.0 BuildRequires: python3-python3-openid >= 3.0.10 -Requires: python-PyJWT >= 1.4.0 +Requires: python-PyJWT >= 1.7.1 Requires: python-cryptography >= 2.1.1 Requires: python-oauthlib >= 1.0.3 +Requires: python-python-jose >= 3.0.0 Requires: python-requests >= 2.9.1 Requires: python-requests-oauthlib >= 0.6.1 Requires: python-six >= 1.10.0 ++++++ social-auth-core-2.0.0.tar.gz -> social-auth-core-3.1.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/social-auth-core-2.0.0/CHANGELOG.md new/social-auth-core-3.1.0/CHANGELOG.md --- old/social-auth-core-2.0.0/CHANGELOG.md 2018-10-28 21:20:14.000000000 +0100 +++ new/social-auth-core-3.1.0/CHANGELOG.md 2019-02-20 14:58:25.000000000 +0100 @@ -5,6 +5,35 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [Unreleased](https://github.com/python-social-auth/social-core/commits/master) + +## [3.1.0](https://github.com/python-social-auth/social-core/releases/tag/3.1.0) - 2019-02-20 + +### Added +- Universe Ticketing backend +- Auth0.com authentication backend + +### Changed +- Update Bungie backend dropping any Django reference +- Enable and fix JWT related tests +- Remove PyPy support from Tox +- Drop support for Python 3.4 in Tox +- Allow to override JWT decode options in Open ID Connect base backend +- Pass access token via Authorization header to Google user data url +- Updated `user_data` method in `AzureADOAuth2` to return `access_token` if + `id_token` is not present in response + +## [3.0.0](https://github.com/python-social-auth/social-core/releases/tag/3.0.0) - 2019-01-14 + +### Changed +- Updated Azure B2C to extract first email from list if it's a list +- Replace deprecated Google+ API usage with https://www.googleapis.com/oauth2/v3/userinfo +- Updated Azure Tenant to fix Nonetype error +- Updated comment denoting incorrect setting name +- Yandex: do not fail when no email is present +- Mediawiki: do not fail when no email is present +- Mediawiki: enhance `get_user_details` to return more details + ## [2.0.0](https://github.com/python-social-auth/social-core/releases/tag/2.0.0) - 2018-10-28 ### Added diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/social-auth-core-2.0.0/PKG-INFO new/social-auth-core-3.1.0/PKG-INFO --- old/social-auth-core-2.0.0/PKG-INFO 2018-10-28 21:34:21.000000000 +0100 +++ new/social-auth-core-3.1.0/PKG-INFO 2019-02-20 14:59:01.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: social-auth-core -Version: 2.0.0 +Version: 3.1.0 Summary: Python social authentication made simple. Home-page: https://github.com/python-social-auth/social-core Author: Matias Aguirre @@ -60,7 +60,9 @@ Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 -Provides-Extra: all Provides-Extra: openidconnect Provides-Extra: saml +Provides-Extra: allpy2 Provides-Extra: azuread +Provides-Extra: allpy3 +Provides-Extra: all diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/social-auth-core-2.0.0/requirements-openidconnect.txt new/social-auth-core-3.1.0/requirements-openidconnect.txt --- old/social-auth-core-2.0.0/requirements-openidconnect.txt 2018-08-30 17:11:15.000000000 +0200 +++ new/social-auth-core-3.1.0/requirements-openidconnect.txt 2019-02-18 21:34:45.000000000 +0100 @@ -1 +1,2 @@ python-jose>=3.0.0 +pyjwt>=1.7.1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/social-auth-core-2.0.0/setup.py new/social-auth-core-3.1.0/setup.py --- old/social-auth-core-2.0.0/setup.py 2018-08-20 15:44:10.000000000 +0200 +++ new/social-auth-core-3.1.0/setup.py 2019-02-18 16:56:13.000000000 +0100 @@ -88,6 +88,8 @@ 'saml': [requirements_saml], 'azuread': [requirements_azuread], 'all': [requirements_all], + 'allpy2': [requirements_all, requirements_py2], + 'allpy3': [requirements_all, requirements_py3], ':python_version < "3.0"': [requirements_py2], ':python_version >= "3.0"': [requirements_py3], }, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/social-auth-core-2.0.0/social_auth_core.egg-info/PKG-INFO new/social-auth-core-3.1.0/social_auth_core.egg-info/PKG-INFO --- old/social-auth-core-2.0.0/social_auth_core.egg-info/PKG-INFO 2018-10-28 21:34:21.000000000 +0100 +++ new/social-auth-core-3.1.0/social_auth_core.egg-info/PKG-INFO 2019-02-20 14:59:00.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: social-auth-core -Version: 2.0.0 +Version: 3.1.0 Summary: Python social authentication made simple. Home-page: https://github.com/python-social-auth/social-core Author: Matias Aguirre @@ -60,7 +60,9 @@ Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 -Provides-Extra: all Provides-Extra: openidconnect Provides-Extra: saml +Provides-Extra: allpy2 Provides-Extra: azuread +Provides-Extra: allpy3 +Provides-Extra: all diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/social-auth-core-2.0.0/social_auth_core.egg-info/SOURCES.txt new/social-auth-core-3.1.0/social_auth_core.egg-info/SOURCES.txt --- old/social-auth-core-2.0.0/social_auth_core.egg-info/SOURCES.txt 2018-10-28 21:34:21.000000000 +0100 +++ new/social-auth-core-3.1.0/social_auth_core.egg-info/SOURCES.txt 2019-02-20 14:59:01.000000000 +0100 @@ -32,6 +32,7 @@ social_core/backends/arcgis.py social_core/backends/asana.py social_core/backends/atlassian.py +social_core/backends/auth0.py social_core/backends/azuread.py social_core/backends/azuread_b2c.py social_core/backends/azuread_tenant.py @@ -167,6 +168,7 @@ social_core/backends/uber.py social_core/backends/ubuntu.py social_core/backends/udata.py +social_core/backends/universe.py social_core/backends/untappd.py social_core/backends/upwork.py social_core/backends/username.py @@ -222,6 +224,7 @@ social_core/tests/backends/test_arcgis.py social_core/tests/backends/test_asana.py social_core/tests/backends/test_atlassian.py +social_core/tests/backends/test_auth0.py social_core/tests/backends/test_azuread.py social_core/tests/backends/test_azuread_b2c.py social_core/tests/backends/test_behance.py @@ -300,6 +303,7 @@ social_core/tests/backends/test_twitter.py social_core/tests/backends/test_uber.py social_core/tests/backends/test_udata.py +social_core/tests/backends/test_universe.py social_core/tests/backends/test_upwork.py social_core/tests/backends/test_username.py social_core/tests/backends/test_utils.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/social-auth-core-2.0.0/social_auth_core.egg-info/requires.txt new/social-auth-core-3.1.0/social_auth_core.egg-info/requires.txt --- old/social-auth-core-2.0.0/social_auth_core.egg-info/requires.txt 2018-10-28 21:34:21.000000000 +0100 +++ new/social-auth-core-3.1.0/social_auth_core.egg-info/requires.txt 2019-02-20 14:59:00.000000000 +0100 @@ -13,14 +13,31 @@ [all] python-jose>=3.0.0 +pyjwt>=1.7.1 python-saml>=2.2.0 cryptography>=2.1.1 +[allpy2] +python-jose>=3.0.0 +pyjwt>=1.7.1 +python-saml>=2.2.0 +cryptography>=2.1.1 +python-openid>=2.2.5 + +[allpy3] +python-jose>=3.0.0 +pyjwt>=1.7.1 +python-saml>=2.2.0 +cryptography>=2.1.1 +defusedxml>=0.5.0rc1 +python3-openid>=3.0.10 + [azuread] cryptography>=2.1.1 [openidconnect] python-jose>=3.0.0 +pyjwt>=1.7.1 [saml] python-saml>=2.2.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/social-auth-core-2.0.0/social_core/__init__.py new/social-auth-core-3.1.0/social_core/__init__.py --- old/social-auth-core-2.0.0/social_core/__init__.py 2018-10-28 21:28:56.000000000 +0100 +++ new/social-auth-core-3.1.0/social_core/__init__.py 2019-02-20 14:57:46.000000000 +0100 @@ -1 +1 @@ -__version__ = '2.0.0' +__version__ = '3.1.0' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/social-auth-core-2.0.0/social_core/backends/auth0.py new/social-auth-core-3.1.0/social_core/backends/auth0.py --- old/social-auth-core-2.0.0/social_core/backends/auth0.py 1970-01-01 01:00:00.000000000 +0100 +++ new/social-auth-core-3.1.0/social_core/backends/auth0.py 2019-02-18 16:45:39.000000000 +0100 @@ -0,0 +1,52 @@ +""" +Auth0 implementation based on: +https://auth0.com/docs/quickstart/webapp/django/01-login +""" +from jose import jwt + +from .oauth import BaseOAuth2 + + +class Auth0OAuth2(BaseOAuth2): + """Auth0 OAuth authentication backend""" + name = 'auth0' + SCOPE_SEPARATOR = ' ' + ACCESS_TOKEN_METHOD = 'POST' + EXTRA_DATA = [ + ('picture', 'picture') + ] + + def api_path(self, path=''): + """Build API path for Auth0 domain""" + return 'https://{domain}/{path}'.format(domain=self.setting('DOMAIN'), + path=path) + + def authorization_url(self): + return self.api_path('authorize') + + def access_token_url(self): + return self.api_path('oauth/token') + + def get_user_id(self, details, response): + """Return current user id.""" + return details['user_id'] + + def get_user_details(self, response): + # Obtain JWT and the keys to validate the signature + id_token = response.get('id_token') + jwks = self.get_json(self.api_path('.well-known/jwks.json')) + issuer = self.api_path() + audience = self.setting('KEY') # CLIENT_ID + payload = jwt.decode(id_token, + jwks, + algorithms=['RS256'], + audience=audience, + issuer=issuer) + fullname, first_name, last_name = self.get_user_names(payload['name']) + return {'username': payload['nickname'], + 'email': payload['email'], + 'fullname': fullname, + 'first_name': first_name, + 'last_name': last_name, + 'picture': payload['picture'], + 'user_id': payload['sub']} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/social-auth-core-2.0.0/social_core/backends/azuread.py new/social-auth-core-3.1.0/social_core/backends/azuread.py --- old/social-auth-core-2.0.0/social_core/backends/azuread.py 2018-08-20 15:44:10.000000000 +0200 +++ new/social-auth-core-3.1.0/social_core/backends/azuread.py 2019-02-01 13:05:57.000000000 +0100 @@ -77,7 +77,11 @@ def user_data(self, access_token, *args, **kwargs): response = kwargs.get('response') - id_token = response.get('id_token') + if response and response.get('id_token'): + id_token = response.get('id_token') + else: + id_token = access_token + try: decoded_id_token = jwt_decode(id_token, verify=False) except (DecodeError, ExpiredSignature) as de: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/social-auth-core-2.0.0/social_core/backends/azuread_b2c.py new/social-auth-core-3.1.0/social_core/backends/azuread_b2c.py --- old/social-auth-core-2.0.0/social_core/backends/azuread_b2c.py 2018-08-20 15:44:10.000000000 +0200 +++ new/social-auth-core-3.1.0/social_core/backends/azuread_b2c.py 2018-10-29 14:05:41.000000000 +0100 @@ -151,7 +151,9 @@ """ details = super(AzureADB2COAuth2, self).get_user_details(response) if not details['email'] and response.get('emails'): - details['email'] = response['emails'][0] + details['email'] = response['emails'] + if isinstance(details.get('email'), (list, tuple)): + details['email'] = details['email'][0] return details def get_public_key(self, kid): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/social-auth-core-2.0.0/social_core/backends/azuread_tenant.py new/social-auth-core-3.1.0/social_core/backends/azuread_tenant.py --- old/social-auth-core-2.0.0/social_core/backends/azuread_tenant.py 2018-08-20 15:44:10.000000000 +0200 +++ new/social-auth-core-3.1.0/social_core/backends/azuread_tenant.py 2019-01-14 16:00:22.000000000 +0100 @@ -88,6 +88,10 @@ return load_pem_x509_certificate(certificate.encode(), default_backend()) + def get_user_id(self, details, response): + """Use subject (sub) claim as unique id.""" + return response.get('sub') + def user_data(self, access_token, *args, **kwargs): response = kwargs.get('response') id_token = response.get('id_token') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/social-auth-core-2.0.0/social_core/backends/bungie.py new/social-auth-core-3.1.0/social_core/backends/bungie.py --- old/social-auth-core-2.0.0/social_core/backends/bungie.py 2018-08-20 15:44:10.000000000 +0200 +++ new/social-auth-core-3.1.0/social_core/backends/bungie.py 2019-02-18 22:28:31.000000000 +0100 @@ -1,12 +1,12 @@ """ Bungie OAuth2 backend """ +import requests + from social_core.backends.oauth import BaseOAuth2 -from django.conf import settings class BungieOAuth2(BaseOAuth2): - name = 'bungie' ID_KEY = 'membership_id' AUTHORIZATION_URL = 'https://www.bungie.net/en/oauth/authorize/' @@ -28,23 +28,18 @@ def auth_headers(self): """Adds X-API-KEY and Origin""" - return {'X-API-KEY': settings.SOCIAL_AUTH_BUNGIE_API_KEY, - 'Content-Type': 'application/x-www-form-urlencoded', - 'Origin': settings.SOCIAL_AUTH_BUNGIE_ORIGIN, - 'Accept': 'application/json' - } + return { + 'X-API-KEY': self.setting('API_KEY'), + 'Content-Type': 'application/x-www-form-urlencoded', + 'Origin': self.setting('ORIGIN'), + 'Accept': 'application/json' + } def make_bungie_request(self, url, access_token, kwargs): """Helper function to get username data keyed off displayName""" - print('ENTERING MAKE BUNGIE REQUEST') headers = self.auth_headers() - print(repr(headers)) - auth_header = {'Authorization': 'Bearer ' + access_token} - headers.update(auth_header) - import requests as python_requests - r = python_requests.get(url, headers=headers) - this_json = r.json() - return this_json + headers['Authorization'] = 'Bearer ' + access_token + return self.get_json(url, headers=headers) def auth_complete(self, *args, **kwargs): """Completes login process, must return user instance""" @@ -58,7 +53,9 @@ method=self.ACCESS_TOKEN_METHOD ) self.process_error(response) - return self.do_auth(response['access_token'], response=response, *args, **kwargs) + return self.do_auth(response['access_token'], + response=response, + *args, **kwargs) def do_auth(self, access_token, *args, **kwargs): """Finish the auth process once the access_token was retrieved""" @@ -74,17 +71,16 @@ """Grab user profile information from Bunige""" membership_id = kwargs['response']['membership_id'] url = 'https://www.bungie.net/Platform/User/GetBungieNetUser/' - this_json = self.make_bungie_request(url, access_token, kwargs) - username = this_json['Response']['user']['displayName'] - return {'username': username, 'uid': membership_id} + response = self.make_bungie_request(url, access_token, kwargs) + username = response['Response']['user']['displayName'] + return {'username': username, + 'uid': membership_id} def get_user_details(self, response, *args, **kwargs): """Return user details from Bungie account""" username = response['username'] - uid = response['uid'] - bnId = response['bnId'] return { 'first_name': username, 'username': username, - 'uid': uid, + 'uid': response['uid'], } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/social-auth-core-2.0.0/social_core/backends/google.py new/social-auth-core-3.1.0/social_core/backends/google.py --- old/social-auth-core-2.0.0/social_core/backends/google.py 2018-08-30 16:55:12.000000000 +0200 +++ new/social-auth-core-3.1.0/social_core/backends/google.py 2019-02-01 13:04:34.000000000 +0100 @@ -12,7 +12,10 @@ def get_user_id(self, details, response): """Use google email as unique id""" if self.setting('USE_UNIQUE_USER_ID', False): - return response['id'] + if 'sub' in response: + return response['sub'] + else: + return response['id'] else: return details['email'] @@ -20,24 +23,14 @@ """Return user details from Google API account""" if 'email' in response: email = response['email'] - elif 'emails' in response: - email = response['emails'][0]['value'] else: email = '' - if isinstance(response.get('name'), dict): - names = response.get('name') or {} - name, given_name, family_name = ( - response.get('displayName', ''), - names.get('givenName', ''), - names.get('familyName', '') - ) - else: - name, given_name, family_name = ( - response.get('name', ''), - response.get('given_name', ''), - response.get('family_name', '') - ) + name, given_name, family_name = ( + response.get('name', ''), + response.get('given_name', ''), + response.get('family_name', ''), + ) fullname, first_name, last_name = self.get_user_names( name, given_name, family_name @@ -53,11 +46,10 @@ def user_data(self, access_token, *args, **kwargs): """Return user data from Google API""" return self.get_json( - 'https://www.googleapis.com/plus/v1/people/me', - params={ - 'access_token': access_token, - 'alt': 'json' - } + 'https://www.googleapis.com/oauth2/v3/userinfo', + headers={ + 'Authorization': 'Bearer %s' % access_token, + }, ) def revoke_token_params(self, token, uid): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/social-auth-core-2.0.0/social_core/backends/line.py new/social-auth-core-3.1.0/social_core/backends/line.py --- old/social-auth-core-2.0.0/social_core/backends/line.py 2018-08-20 15:44:10.000000000 +0200 +++ new/social-auth-core-3.1.0/social_core/backends/line.py 2019-01-14 16:00:22.000000000 +0100 @@ -44,7 +44,6 @@ data.get('statusCode') or \ data.get('error') error_message = data.get('errorMessage') or \ - data.get('statusMessage') or \ data.get('error_description') if error_code is not None or error_message is not None: raise AuthFailed(self, error_message or error_code) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/social-auth-core-2.0.0/social_core/backends/mediawiki.py new/social-auth-core-3.1.0/social_core/backends/mediawiki.py --- old/social-auth-core-2.0.0/social_core/backends/mediawiki.py 2018-08-20 15:44:10.000000000 +0200 +++ new/social-auth-core-3.1.0/social_core/backends/mediawiki.py 2018-11-20 01:35:12.000000000 +0100 @@ -172,7 +172,14 @@ return { 'username': identity['username'], - 'userID': identity['sub'] + 'userID': identity['sub'], + 'email': identity.get('email'), + 'confirmed_email': identity.get('confirmed_email'), + 'editcount': identity.get('editcount'), + 'rights': identity.get('rights'), + 'groups': identity.get('groups'), + 'registered': identity.get('registered'), + 'blocked': identity.get('blocked') } def get_user_id(self, details, response): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/social-auth-core-2.0.0/social_core/backends/open_id_connect.py new/social-auth-core-3.1.0/social_core/backends/open_id_connect.py --- old/social-auth-core-2.0.0/social_core/backends/open_id_connect.py 2018-08-30 17:11:15.000000000 +0200 +++ new/social-auth-core-3.1.0/social_core/backends/open_id_connect.py 2019-02-18 21:17:43.000000000 +0100 @@ -2,10 +2,11 @@ import datetime from calendar import timegm +import six from jose import jwk, jwt from jose.jwt import JWTError, JWTClaimsError, ExpiredSignatureError from jose.utils import base64url_decode -import six + from social_core.backends.oauth import BaseOAuth2 from social_core.utils import cache from social_core.exceptions import AuthTokenError @@ -43,6 +44,7 @@ REVOKE_TOKEN_URL = '' USERINFO_URL = '' JWKS_URI = '' + JWT_DECODE_OPTIONS = dict() def __init__(self, *args, **kwargs): self.id_token = None @@ -147,7 +149,6 @@ decoded_sig = base64url_decode(encoded_sig.encode('utf-8')) if rsakey.verify(message.encode('utf-8'), decoded_sig): return key - return False def validate_and_return_id_token(self, id_token, access_token): """ @@ -157,6 +158,10 @@ client_id, client_secret = self.get_key_and_secret() key = self.find_valid_key(id_token) + + if not key: + raise AuthTokenError(self, 'Signature verification failed') + alg = key['alg'] rsakey = jwk.construct(key) @@ -167,12 +172,13 @@ algorithms=[alg], audience=client_id, issuer=self.id_token_issuer(), - access_token=access_token + access_token=access_token, + options=self.JWT_DECODE_OPTIONS, ) except ExpiredSignatureError: raise AuthTokenError(self, 'Signature has expired') - except JWTClaimsError: - raise AuthTokenError(self, 'Invalid claims') + except JWTClaimsError as error: + raise AuthTokenError(self, str(error)) except JWTError: raise AuthTokenError(self, 'Invalid signature') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/social-auth-core-2.0.0/social_core/backends/universe.py new/social-auth-core-3.1.0/social_core/backends/universe.py --- old/social-auth-core-2.0.0/social_core/backends/universe.py 1970-01-01 01:00:00.000000000 +0100 +++ new/social-auth-core-3.1.0/social_core/backends/universe.py 2019-01-31 14:43:15.000000000 +0100 @@ -0,0 +1,35 @@ +from .oauth import BaseOAuth2 + + +class UniverseOAuth2(BaseOAuth2): + """Universe Ticketing OAuth2 authentication backend""" + name = 'universe' + AUTHORIZATION_URL = 'https://www.universe.com/oauth/authorize' + ACCESS_TOKEN_URL = 'https://www.universe.com/oauth/token' + BASE_API_URL = 'https://www.universe.com/api' + USER_INFO_URL = BASE_API_URL + '/v2/current_user' + ACCESS_TOKEN_METHOD = 'POST' + STATE_PARAMETER = True + REDIRECT_STATE = True + EXTRA_DATA = [ + ('id', 'id'), + ('slug', 'slug'), + ('created_at', 'created_at'), + ('updated_at', 'updated_at'), + ] + + def get_user_id(self, details, response): + return response['current_user'][self.ID_KEY] + + def get_user_details(self, response): + """Return user details from a Universe account""" + # Start with the user data as it was returned + user_details = response['current_user'] + user_details['username'] = user_details['email'] + return user_details + + def user_data(self, access_token, *args, **kwargs): + """Loads user data from service""" + return self.get_json(self.USER_INFO_URL, headers={ + 'Authorization': 'Bearer {}'.format(access_token) + }) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/social-auth-core-2.0.0/social_core/backends/yandex.py new/social-auth-core-3.1.0/social_core/backends/yandex.py --- old/social-auth-core-2.0.0/social_core/backends/yandex.py 2018-08-20 15:44:10.000000000 +0200 +++ new/social-auth-core-3.1.0/social_core/backends/yandex.py 2019-01-14 16:00:22.000000000 +0100 @@ -42,9 +42,12 @@ fullname, first_name, last_name = self.get_user_names( response.get('real_name') or response.get('display_name') or '' ) + email = response.get('default_email') + if not email: + emails = response.get('emails') + email = emails[0] if emails else '' return {'username': response.get('display_name'), - 'email': response.get('default_email') or - response.get('emails', [''])[0], + 'email': email, 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} @@ -66,9 +69,12 @@ fullname, first_name, last_name = self.get_user_names( response.get('real_name') or response.get('display_name') or '' ) + email = response.get('default_email') + if not email: + emails = response.get('emails') + email = emails[0] if emails else '' return {'username': response.get('display_name'), - 'email': response.get('default_email') or - response.get('emails', [''])[0], + 'email': email, 'fullname': fullname, 'first_name': first_name, 'last_name': last_name} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/social-auth-core-2.0.0/social_core/pipeline/user.py new/social-auth-core-3.1.0/social_core/pipeline/user.py --- old/social-auth-core-2.0.0/social_core/pipeline/user.py 2018-08-20 15:44:10.000000000 +0200 +++ new/social-auth-core-3.1.0/social_core/pipeline/user.py 2019-01-14 16:00:22.000000000 +0100 @@ -88,7 +88,7 @@ # Update user model attributes with the new data sent by the current # provider. Update on some attributes is disabled by default, for # example username and id fields. It's also possible to disable update - # on fields defined in SOCIAL_AUTH_PROTECTED_FIELDS. + # on fields defined in SOCIAL_AUTH_PROTECTED_USER_FIELDS. for name, value in details.items(): if value is None or not hasattr(user, name) or name in protected: continue diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/social-auth-core-2.0.0/social_core/tests/backends/open_id_connect.py new/social-auth-core-3.1.0/social_core/tests/backends/open_id_connect.py --- old/social-auth-core-2.0.0/social_core/tests/backends/open_id_connect.py 2018-08-20 15:44:10.000000000 +0200 +++ new/social-auth-core-3.1.0/social_core/tests/backends/open_id_connect.py 2019-02-18 22:21:22.000000000 +0100 @@ -1,20 +1,14 @@ # -*- coding: utf-8 -*- -from calendar import timegm - import os import sys import json import datetime import unittest2 +import base64 +from calendar import timegm -try: - from jwkest.jwk import RSAKey, KEYS - from jwkest.jws import JWS - from jwkest.jwt import b64encode_item - NO_JWKEST = False -except ImportError: - NO_JWKEST = True - +from jose import jwt +from jose.jwk import RSAKey from httpretty import HTTPretty sys.path.insert(0, '..') @@ -22,6 +16,31 @@ from ...exceptions import AuthTokenError +TEST_ROOT = os.path.dirname(os.path.dirname(__file__)) + +JWK_KEY = { + 'kty': 'RSA', + 'd': 'ZmswNokEvBcxW_Kvcy8mWUQOQCBdGbnM0xR7nhvGHC-Q24z3XAQWlMWbsmGc_R1o' \ + '_F3zK7DBlc3BokdRaO1KJirNmnHCw5TlnBlJrXiWpFBtVglUg98-4sRRO0VWnGXK' \ + 'JPOkBQ6b_DYRO3b0o8CSpWowpiV6HB71cjXTqKPZf-aXU9WjCCAtxVjfIxgQFu5I' \ + '-G1Qah8mZeY8HK_y99L4f0siZcbUoaIcfeWBhxi14ODyuSAHt0sNEkhiIVBZE7QZ' \ + 'm-SEP1ryT9VAaljbwHHPmg7NC26vtLZhvaBGbTTJnEH0ZubbN2PMzsfeNyoCIHy4' \ + '4QDSpQDCHfgcGOlHY_t5gQ', + 'e': 'AQAB', + 'use': 'sig', + 'kid': 'testkey', + 'alg': 'RS256', + 'n': 'pUfcJ8WFrVue98Ygzb6KEQXHBzi8HavCu8VENB2As943--bHPcQ-nScXnrRFAUg8' \ + 'H5ZltuOcHWvsGw_AQifSLmOCSWJAPkdNb0w0QzY7Re8NrPjCsP58Tytp5LicF0Ao' \ + 'Ag28UK3JioY9hXHGvdZsWR1Rp3I-Z3nRBP6HyO18pEgcZ91c9aAzsqu80An9X4DA' \ + 'b1lExtZorvcd5yTBzZgr-MUeytVRni2lDNEpa6OFuopHXmg27Hn3oWAaQlbymd4g' \ + 'ifc01oahcwl3ze2tMK6gJxa_TdCf1y99Yq6oilmVvZJ8kwWWnbPE-oDmOVPVnEyT' \ + 'vYVCvN4rBT1DQ-x0F1mo2Q', +} + +JWK_PUBLIC_KEY = {key: value for key, value in JWK_KEY.items() if key != 'd'} + + class OpenIdConnectTestMixin(object): """ Mixin to test OpenID Connect consumers. Inheriting classes should also @@ -35,8 +54,9 @@ def setUp(self): super(OpenIdConnectTestMixin, self).setUp() - test_root = os.path.dirname(os.path.dirname(__file__)) - self.key = RSAKey(kid='testkey').load(os.path.join(test_root, 'testkey.pem')) + self.key = JWK_KEY.copy() + self.public_key = JWK_PUBLIC_KEY.copy() + HTTPretty.register_uri(HTTPretty.GET, self.backend.OIDC_ENDPOINT + '/.well-known/openid-configuration', status=200, @@ -45,14 +65,12 @@ oidc_config = json.loads(self.openid_config_body) def jwks(_request, _uri, headers): - ks = KEYS() - ks.add(self.key.serialize()) - return 200, headers, ks.dump_jwks() + return 200, headers, json.dumps({'keys': [self.key]}) HTTPretty.register_uri(HTTPretty.GET, oidc_config.get('jwks_uri'), status=200, - body=jwks) + body=json.dumps({'keys': [self.public_key]})) def extra_settings(self): settings = super(OpenIdConnectTestMixin, self).extra_settings() @@ -64,15 +82,6 @@ }) return settings - def access_token_body(self, request, _url, headers): - """ - Get the nonce from the request parameters, add it to the id_token, and - return the complete response. - """ - nonce = self.backend.data['nonce'].encode('utf-8') - body = self.prepare_access_token_body(nonce=nonce) - return 200, headers, body - def get_id_token(self, client_key=None, expiration_datetime=None, issue_datetime=None, nonce=None, issuer=None): """ @@ -110,14 +119,26 @@ nonce = nonce or 'a-nonce' issuer = issuer or self.issuer id_token = self.get_id_token( - client_key, timegm(expiration_datetime.utctimetuple()), - timegm(issue_datetime.utctimetuple()), nonce, issuer) + client_key, + timegm(expiration_datetime.utctimetuple()), + timegm(issue_datetime.utctimetuple()), + nonce, + issuer + ) + + body['id_token'] = jwt.encode( + id_token, + key=dict(self.key, + iat=timegm(issue_datetime.utctimetuple()), + nonce=nonce), + algorithm='RS256', + access_token='foobar' + ) - body['id_token'] = JWS(id_token, jwk=self.key, alg='RS256').sign_compact() if tamper_message: header, msg, sig = body['id_token'].split('.') id_token['sub'] = '1235' - msg = b64encode_item(id_token).decode('utf-8') + msg = base64.encodestring(json.dumps(id_token).encode()).decode() body['id_token'] = '.'.join([header, msg, sig]) return json.dumps(body) @@ -129,38 +150,32 @@ with self.assertRaisesRegexp(AuthTokenError, expected_message): self.do_login() - @unittest2.skipIf(NO_JWKEST, 'No Jwkest installed') def test_invalid_signature(self): self.authtoken_raised( 'Token error: Signature verification failed', tamper_message=True ) - @unittest2.skipIf(NO_JWKEST, 'No Jwkest installed') def test_expired_signature(self): expiration_datetime = datetime.datetime.utcnow() - \ datetime.timedelta(seconds=30) self.authtoken_raised('Token error: Signature has expired', expiration_datetime=expiration_datetime) - @unittest2.skipIf(NO_JWKEST, 'No Jwkest installed') def test_invalid_issuer(self): self.authtoken_raised('Token error: Invalid issuer', issuer='someone-else') - @unittest2.skipIf(NO_JWKEST, 'No Jwkest installed') def test_invalid_audience(self): self.authtoken_raised('Token error: Invalid audience', client_key='someone-else') - @unittest2.skipIf(NO_JWKEST, 'No Jwkest installed') def test_invalid_issue_time(self): expiration_datetime = datetime.datetime.utcnow() - \ datetime.timedelta(hours=1) self.authtoken_raised('Token error: Incorrect id_token: iat', issue_datetime=expiration_datetime) - @unittest2.skipIf(NO_JWKEST, 'No Jwkest installed') def test_invalid_nonce(self): self.authtoken_raised( 'Token error: Incorrect id_token: nonce', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/social-auth-core-2.0.0/social_core/tests/backends/test_auth0.py new/social-auth-core-3.1.0/social_core/tests/backends/test_auth0.py --- old/social-auth-core-2.0.0/social_core/tests/backends/test_auth0.py 1970-01-01 01:00:00.000000000 +0100 +++ new/social-auth-core-3.1.0/social_core/tests/backends/test_auth0.py 2019-02-18 21:17:52.000000000 +0100 @@ -0,0 +1,68 @@ + +import json + +from jose import jwt +from httpretty import HTTPretty + +from .oauth import OAuth2Test + +JWK_KEY = { + 'kty': 'RSA', + 'd': 'ZmswNokEvBcxW_Kvcy8mWUQOQCBdGbnM0xR7nhvGHC-Q24z3XAQWlMWbsmGc_R1o' \ + '_F3zK7DBlc3BokdRaO1KJirNmnHCw5TlnBlJrXiWpFBtVglUg98-4sRRO0VWnGXK' \ + 'JPOkBQ6b_DYRO3b0o8CSpWowpiV6HB71cjXTqKPZf-aXU9WjCCAtxVjfIxgQFu5I' \ + '-G1Qah8mZeY8HK_y99L4f0siZcbUoaIcfeWBhxi14ODyuSAHt0sNEkhiIVBZE7QZ' \ + 'm-SEP1ryT9VAaljbwHHPmg7NC26vtLZhvaBGbTTJnEH0ZubbN2PMzsfeNyoCIHy4' \ + '4QDSpQDCHfgcGOlHY_t5gQ', + 'e': 'AQAB', + 'use': 'sig', + 'kid': 'foobar', + 'alg': 'RS256', + 'n': 'pUfcJ8WFrVue98Ygzb6KEQXHBzi8HavCu8VENB2As943--bHPcQ-nScXnrRFAUg8' \ + 'H5ZltuOcHWvsGw_AQifSLmOCSWJAPkdNb0w0QzY7Re8NrPjCsP58Tytp5LicF0Ao' \ + 'Ag28UK3JioY9hXHGvdZsWR1Rp3I-Z3nRBP6HyO18pEgcZ91c9aAzsqu80An9X4DA' \ + 'b1lExtZorvcd5yTBzZgr-MUeytVRni2lDNEpa6OFuopHXmg27Hn3oWAaQlbymd4g' \ + 'ifc01oahcwl3ze2tMK6gJxa_TdCf1y99Yq6oilmVvZJ8kwWWnbPE-oDmOVPVnEyT' \ + 'vYVCvN4rBT1DQ-x0F1mo2Q' +} + +JWK_PUBLIC_KEY = {key: value for key, value in JWK_KEY.items() if key != 'd'} + +DOMAIN = 'foobar.auth0.com' + + +class Auth0OAuth2Test(OAuth2Test): + backend_path = 'social_core.backends.auth0.Auth0OAuth2' + access_token_body = json.dumps({ + 'access_token': 'foobar', + 'token_type': 'bearer', + 'expires_in': 86400, + 'id_token': jwt.encode({ + 'nickname': 'foobar', + 'email': 'foobar@auth0.com', + 'name': 'John Doe', + 'picture': 'http://example.com/image.png', + 'sub': '123456', + 'iss': 'https://{}/'.format(DOMAIN), + }, JWK_KEY, algorithm='RS256') + }) + expected_username = 'foobar' + jwks_url = 'https://foobar.auth0.com/.well-known/jwks.json' + + def extra_settings(self): + settings = super(Auth0OAuth2Test, self).extra_settings() + settings['SOCIAL_AUTH_' + self.name + '_DOMAIN'] = DOMAIN + return settings + + def auth_handlers(self, start_url): + HTTPretty.register_uri(HTTPretty.GET, + self.jwks_url, + body=json.dumps({'keys': [JWK_PUBLIC_KEY]}), + content_type='application/json') + return super(Auth0OAuth2Test, self).auth_handlers(start_url) + + def test_login(self): + self.do_login() + + def test_partial_pipeline(self): + self.do_partial_pipeline() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/social-auth-core-2.0.0/social_core/tests/backends/test_azuread_b2c.py new/social-auth-core-3.1.0/social_core/tests/backends/test_azuread_b2c.py --- old/social-auth-core-2.0.0/social_core/tests/backends/test_azuread_b2c.py 2018-08-20 15:44:10.000000000 +0200 +++ new/social-auth-core-3.1.0/social_core/tests/backends/test_azuread_b2c.py 2019-02-18 22:22:16.000000000 +0100 @@ -23,26 +23,16 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ - - import json import jwt from time import time -from unittest import SkipTest from httpretty import HTTPretty +from jwt.algorithms import RSAAlgorithm from .oauth import OAuth2Test -try: - from jwt.algorithms import RSAAlgorithm -except ImportError: - # Usually in Python 3.3 - raise SkipTest('RSA support is missing in jwt/cryptography packages') - - - # Dummy private and private keys: RSA_PUBLIC_JWT_KEY = { # https://github.com/jpadilla/pyjwt/blob/06f461a/tests/keys/jwk_rsa_pub.json diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/social-auth-core-2.0.0/social_core/tests/backends/test_elixir.py new/social-auth-core-3.1.0/social_core/tests/backends/test_elixir.py --- old/social-auth-core-2.0.0/social_core/tests/backends/test_elixir.py 2018-10-28 20:22:07.000000000 +0100 +++ new/social-auth-core-3.1.0/social_core/tests/backends/test_elixir.py 2019-02-18 22:33:20.000000000 +0100 @@ -1,10 +1,9 @@ import unittest2 from .oauth import OAuth1Test, OAuth2Test -from .open_id_connect import OpenIdConnectTestMixin, NO_JWKEST +from .open_id_connect import OpenIdConnectTestMixin -@unittest2.skipIf(NO_JWKEST, 'No Jwkest installed') class ElixirOpenIdConnectTest(OpenIdConnectTestMixin, OAuth2Test): backend_path = 'social_core.backends.elixir.ElixirOpenIdConnect' issuer = 'https://login.elixir-czech.org/oidc/' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/social-auth-core-2.0.0/social_core/tests/backends/test_globus.py new/social-auth-core-3.1.0/social_core/tests/backends/test_globus.py --- old/social-auth-core-2.0.0/social_core/tests/backends/test_globus.py 2018-08-20 15:44:10.000000000 +0200 +++ new/social-auth-core-3.1.0/social_core/tests/backends/test_globus.py 2019-02-18 20:40:38.000000000 +0100 @@ -1,51 +1,49 @@ +import json import unittest2 from .oauth import OAuth1Test, OAuth2Test -from .open_id_connect import OpenIdConnectTestMixin, NO_JWKEST +from .open_id_connect import OpenIdConnectTestMixin -@unittest2.skipIf(NO_JWKEST, 'No Jwkest installed') class GlobusOpenIdConnectTest(OpenIdConnectTestMixin, OAuth2Test): backend_path = \ 'social_core.backends.globus.GlobusOpenIdConnect' issuer = 'https://auth.globus.org' - openid_config_body = ''.join([ - '{' - ' "issuer": "https://auth.globus.org",' - ' "authorization_endpoint": "https://auth.globus.org/v2/oauth2/authorize",' - ' "userinfo_endpoint": "https://auth.globus.org/v2/oauth2/userinfo",' - ' "token_endpoint": "https://auth.globus.org/v2/oauth2/token",' - ' "revocation_endpoint": "https://auth.globus.org/v2/oauth2/token/revoke",' - ' "jwks_uri": "https://auth.globus.org/jwk.json",' - ' "response_types_supported": [' - ' "code",' - ' "token",' - ' "token id_token",' - ' "id_token"' - ' ],' - ' "id_token_signing_alg_values_supported": [' - ' "RS512"' - ' ],' - ' "scopes_supported": [' - ' "openid",' - ' "email",' - ' "profile"' - ' ],' - ' "token_endpoint_auth_methods_supported": [' - ' "client_secret_basic"' - ' ],' - ' "claims_supported": [' - ' "at_hash",' - ' "aud",' - ' "email",' - ' "exp",' - ' "name",' - ' "nonce",' - ' "preferred_username",' - ' "iat",' - ' "iss",' - ' "sub"' - ' ],' - ' "subject_types_supported" : ["public"]' - '}' - ]) + openid_config_body = json.dumps({ + 'issuer': 'https://auth.globus.org', + 'authorization_endpoint': 'https://auth.globus.org/v2/oauth2/authorize', + 'userinfo_endpoint': 'https://auth.globus.org/v2/oauth2/userinfo', + 'token_endpoint': 'https://auth.globus.org/v2/oauth2/token', + 'revocation_endpoint': 'https://auth.globus.org/v2/oauth2/token/revoke', + 'jwks_uri': 'https://auth.globus.org/jwk.json', + 'response_types_supported': [ + 'code', + 'token', + 'token id_token', + 'id_token' + ], + 'id_token_signing_alg_values_supported': [ + 'RS512' + ], + 'scopes_supported': [ + 'openid', + 'email', + 'profile' + ], + 'token_endpoint_auth_methods_supported': [ + 'client_secret_basic' + ], + 'claims_supported': [ + 'at_hash', + 'aud', + 'email', + 'exp', + 'name', + 'nonce', + 'preferred_username', + 'iat', + 'iss', + 'sub' + ], + 'subject_types_supported' : ['public'] + }) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/social-auth-core-2.0.0/social_core/tests/backends/test_google.py new/social-auth-core-3.1.0/social_core/tests/backends/test_google.py --- old/social-auth-core-2.0.0/social_core/tests/backends/test_google.py 2018-08-20 15:44:10.000000000 +0200 +++ new/social-auth-core-3.1.0/social_core/tests/backends/test_google.py 2019-02-18 20:43:20.000000000 +0100 @@ -11,73 +11,40 @@ from ..models import User from .oauth import OAuth1Test, OAuth2Test from .open_id import OpenIdTest -from .open_id_connect import OpenIdConnectTestMixin, NO_JWKEST +from .open_id_connect import OpenIdConnectTestMixin class GoogleOAuth2Test(OAuth2Test): backend_path = 'social_core.backends.google.GoogleOAuth2' - user_data_url = 'https://www.googleapis.com/plus/v1/people/me' + user_data_url = 'https://www.googleapis.com/oauth2/v3/userinfo' expected_username = 'foo' access_token_body = json.dumps({ 'access_token': 'foobar', 'token_type': 'bearer' }) user_data_body = json.dumps({ - 'aboutMe': 'About me text', - 'cover': { - 'coverInfo': { - 'leftImageOffset': 0, - 'topImageOffset': 0 - }, - 'coverPhoto': { - 'height': 629, - 'url': 'https://lh5.googleusercontent.com/-ui-GqpNh5Ms/' - 'AAAAAAAAAAI/AAAAAAAAAZw/a7puhHMO_fg/photo.jpg', - 'width': 940 - }, - 'layout': 'banner' - }, - 'displayName': 'Foo Bar', - 'emails': [{ - 'type': 'account', - 'value': 'foo@bar.com' - }], - 'etag': '"e-tag string"', - 'gender': 'male', - 'id': '101010101010101010101', - 'image': { - 'url': 'https://lh5.googleusercontent.com/-ui-GqpNh5Ms/' + 'profile': 'https://plus.google.com/101010101010101010101', + 'family_name': 'Bar', + 'sub': '101010101010101010101', + 'picture': 'https://lh5.googleusercontent.com/-ui-GqpNh5Ms/' 'AAAAAAAAAAI/AAAAAAAAAZw/a7puhHMO_fg/photo.jpg', - }, - 'isPlusUser': True, - 'kind': 'plus#person', - 'language': 'en', - 'name': { - 'familyName': 'Bar', - 'givenName': 'Foo' - }, - 'objectType': 'person', - 'occupation': 'Software developer', - 'organizations': [{ - 'name': 'Org name', - 'primary': True, - 'type': 'school' - }], - 'placesLived': [{ - 'primary': True, - 'value': 'Anyplace' - }], - 'url': 'https://plus.google.com/101010101010101010101', - 'urls': [{ - 'label': 'http://foobar.com', - 'type': 'otherProfile', - 'value': 'http://foobar.com', - }], - 'verified': False + 'locale': 'en', + 'email_verified': True, + 'given_name': 'Foo', + 'email': 'foo@bar.com', + 'name': 'Foo Bar', }) def test_login(self): self.do_login() + last_request = HTTPretty.last_request + self.assertEqual(last_request.method, 'GET') + self.assertTrue(self.user_data_url.endswith(last_request.path)) + self.assertEqual( + last_request.headers['Authorization'], + 'Bearer foobar', + ) + self.assertEqual(last_request.querystring, {}) def test_partial_pipeline(self): self.do_partial_pipeline() @@ -236,59 +203,56 @@ do_disconnect(self.backend, user) -@unittest2.skipIf(NO_JWKEST, 'No Jwkest installed') -class GoogleOpenIdConnectTest(OpenIdConnectTestMixin, GoogleOAuth2Test): +class GoogleOpenIdConnectTest(OpenIdConnectTestMixin, OAuth2Test): backend_path = \ 'social_core.backends.google_openidconnect.GoogleOpenIdConnect' user_data_url = \ 'https://www.googleapis.com/plus/v1/people/me/openIdConnect' issuer = 'accounts.google.com' - openid_config_body = ''.join([ - '{', - ' "issuer": "https://accounts.google.com",', - ' "authorization_endpoint": "https://accounts.google.com/o/oauth2/v2/auth",', - ' "token_endpoint": "https://www.googleapis.com/oauth2/v4/token",', - ' "userinfo_endpoint": "https://www.googleapis.com/oauth2/v3/userinfo",', - ' "revocation_endpoint": "https://accounts.google.com/o/oauth2/revoke",', - ' "jwks_uri": "https://www.googleapis.com/oauth2/v3/certs",', - ' "response_types_supported": [', - ' "code",', - ' "token",', - ' "id_token",', - ' "code token",', - ' "code id_token",', - ' "token id_token",', - ' "code token id_token",', - ' "none"', - ' ],', - ' "subject_types_supported": [', - ' "public"', - ' ],', - ' "id_token_signing_alg_values_supported": [', - ' "RS256"', - ' ],', - ' "scopes_supported": [', - ' "openid",', - ' "email",', - ' "profile"', - ' ],', - ' "token_endpoint_auth_methods_supported": [', - ' "client_secret_post",', - ' "client_secret_basic"', - ' ],', - ' "claims_supported": [', - ' "aud",', - ' "email",', - ' "email_verified",', - ' "exp",', - ' "family_name",', - ' "given_name",', - ' "iat",', - ' "iss",', - ' "locale",', - ' "name",', - ' "picture",', - ' "sub"', - ' ]', - '}' - ]) + openid_config_body = json.dumps({ + 'issuer': 'https://accounts.google.com', + 'authorization_endpoint': 'https://accounts.google.com/o/oauth2/v2/auth', + 'token_endpoint': 'https://www.googleapis.com/oauth2/v4/token', + 'userinfo_endpoint': 'https://www.googleapis.com/oauth2/v3/userinfo', + 'revocation_endpoint': 'https://accounts.google.com/o/oauth2/revoke', + 'jwks_uri': 'https://www.googleapis.com/oauth2/v3/certs', + 'response_types_supported': [ + 'code', + 'token', + 'id_token', + 'code token', + 'code id_token', + 'token id_token', + 'code token id_token', + 'none', + ], + 'subject_types_supported': [ + 'public', + ], + 'id_token_signing_alg_values_supported': [ + 'RS256', + ], + 'scopes_supported': [ + 'openid', + 'email', + 'profile', + ], + 'token_endpoint_auth_methods_supported': [ + 'client_secret_post', + 'client_secret_basic', + ], + 'claims_supported': [ + 'aud', + 'email', + 'email_verified', + 'exp', + 'family_name', + 'given_name', + 'iat', + 'iss', + 'locale', + 'name', + 'picture', + 'sub', + ], + }) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/social-auth-core-2.0.0/social_core/tests/backends/test_keycloak.py new/social-auth-core-3.1.0/social_core/tests/backends/test_keycloak.py --- old/social-auth-core-2.0.0/social_core/tests/backends/test_keycloak.py 2018-08-20 15:44:10.000000000 +0200 +++ new/social-auth-core-3.1.0/social_core/tests/backends/test_keycloak.py 2019-02-18 20:45:58.000000000 +0100 @@ -6,12 +6,6 @@ from .oauth import OAuth2Test -try: - from jwt.algorithms import RSAAlgorithm -except ImportError: - # Usually in Python 3.3 - raise unittest.SkipTest('RSA support is missing in jwt/cryptography packages') - _PRIVATE_KEY_HEADERLESS = ''' MIIEowIBAAKCAQEAvyo2hx1L3ALHeUd/6xk/lIhTyZ/HJZ+Sss/ge6T6gPdES4Dw diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/social-auth-core-2.0.0/social_core/tests/backends/test_universe.py new/social-auth-core-3.1.0/social_core/tests/backends/test_universe.py --- old/social-auth-core-2.0.0/social_core/tests/backends/test_universe.py 1970-01-01 01:00:00.000000000 +0100 +++ new/social-auth-core-3.1.0/social_core/tests/backends/test_universe.py 2019-01-31 14:43:15.000000000 +0100 @@ -0,0 +1,32 @@ +import json + +from .oauth import OAuth2Test + + +class UniverseAuth2Test(OAuth2Test): + backend_path = 'social_core.backends.universe.UniverseOAuth2' + user_data_url = 'https://www.universe.com/api/v2/current_user' + expected_username = 'scott+awesome@universe.com' + access_token_body = json.dumps({ + 'access_token': 'foobar', + 'token_type': 'bearer' + }) + user_data_body = json.dumps( + { + 'current_user': { + 'id': '123456', + 'slug': 'scott-vitale', + 'first_name': 'Scott', + 'last_name': 'Vitale', + 'created_at': '2019-01-08T15:49:42.514Z', + 'updated_at': '2019-01-17T19:41:39.711Z', + 'email': 'scott+awesome@universe.com', + } + } + ) + + def test_login(self): + self.do_login() + + def test_partial_pipeline(self): + self.do_partial_pipeline() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/social-auth-core-2.0.0/social_core/tests/backends/test_yandex.py new/social-auth-core-3.1.0/social_core/tests/backends/test_yandex.py --- old/social-auth-core-2.0.0/social_core/tests/backends/test_yandex.py 2018-08-20 15:44:10.000000000 +0200 +++ new/social-auth-core-3.1.0/social_core/tests/backends/test_yandex.py 2019-01-14 16:00:22.000000000 +0100 @@ -25,3 +25,31 @@ def test_partial_pipeline(self): self.do_partial_pipeline() + + +class YandexOAuth2TestEmptyEmail(OAuth2Test): + """ + When user log in to yandex service with social network account (e.g. + vk.com), they `default_email` could be empty. + """ + backend_path = 'social_core.backends.yandex.YandexOAuth2' + user_data_url = 'https://login.yandex.ru/info' + expected_username = 'foobar' + access_token_body = json.dumps({ + 'access_token': 'foobar', + 'token_type': 'bearer' + }) + user_data_body = json.dumps({ + 'display_name': 'foobar', + 'real_name': 'Foo Bar', + 'sex': None, + 'id': '101010101', + 'default_email': '', + 'emails': [] + }) + + def test_login(self): + self.do_login() + + def test_partial_pipeline(self): + self.do_partial_pipeline()