def response(self): if not hasattr(self, '_response'): if self.data.get('error'): error = self.data.get( 'error_description') or self.data['error'] raise AuthFailed(self, error) state = self.validate_state() client_id, client_secret = self.get_key_and_secret() params = { 'grant_type': 'authorization_code', # request auth code 'code': self.data.get('code', ''), # server response code 'client_id': client_id, 'client_secret': client_secret, 'redirect_uri': self.get_redirect_uri(state) } headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json' } request = Request(self.ACCESS_TOKEN_URL, data=urlencode(params), headers=headers) try: self._response = simplejson.loads(dsa_urlopen(request).read()) except HTTPError, e: if e.code == 400: raise AuthCanceled(self) else: raise except (ValueError, KeyError): raise AuthUnknownError(self)
def auth_complete(self, *args, **kwargs): """Completes loging process, must return user instance""" if self.data.get('error'): error = self.data.get('error_description') or self.data['error'] raise AuthFailed(self, error) client_id, client_secret = self.get_key_and_secret() params = { 'grant_type': 'authorization_code', # request auth code 'code': self.data.get('code', ''), # server response code 'client_id': client_id, 'client_secret': client_secret, 'redirect_uri': self.redirect_uri } headers = {'Content-Type': 'application/x-www-form-urlencoded'} request = Request(self.ACCESS_TOKEN_URL, data=urlencode(params), headers=headers) try: response = simplejson.loads(urlopen(request).read()) except (ValueError, KeyError): raise AuthUnknownError(self) if response.get('error'): error = response.get('error_description') or response.get('error') raise AuthFailed(self, error) else: response.update(self.user_data(response['access_token']) or {}) kwargs.update({ 'auth': self, 'response': response, self.AUTH_BACKEND.name: True }) return authenticate(*args, **kwargs)
def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" url = LIVE_USER_DATA_URL + '?' + urlencode( {'access_token': access_token}) try: return simplejson.load(urlopen(url)) except (ValueError, IOError): raise AuthUnknownError('Error during profile retrieval, ' \ 'please, try again later')
class BaseOAuth2(BaseOAuth): """Base class for OAuth2 providers. OAuth2 draft details at: http://tools.ietf.org/html/draft-ietf-oauth-v2-10 Attributes: @AUTHORIZATION_URL Authorization service url @ACCESS_TOKEN_URL Token URL """ AUTHORIZATION_URL = None ACCESS_TOKEN_URL = None SCOPE_SEPARATOR = ' ' RESPONSE_TYPE = 'code' def auth_url(self): """Return redirect url""" client_id, client_secret = self.get_key_and_secret() args = {'client_id': client_id, 'redirect_uri': self.redirect_uri} scope = self.get_scope() if scope: args['scope'] = self.SCOPE_SEPARATOR.join(self.get_scope()) if self.RESPONSE_TYPE: args['response_type'] = self.RESPONSE_TYPE args.update(self.auth_extra_arguments()) return self.AUTHORIZATION_URL + '?' + urlencode(args) def auth_complete(self, *args, **kwargs): """Completes loging process, must return user instance""" if self.data.get('error'): error = self.data.get('error_description') or self.data['error'] raise AuthFailed(self, error) client_id, client_secret = self.get_key_and_secret() params = { 'grant_type': 'authorization_code', # request auth code 'code': self.data.get('code', ''), # server response code 'client_id': client_id, 'client_secret': client_secret, 'redirect_uri': self.redirect_uri } headers = {'Content-Type': 'application/x-www-form-urlencoded'} request = Request(self.ACCESS_TOKEN_URL, data=urlencode(params), headers=headers) try: response = simplejson.loads(urlopen(request).read()) except HTTPError, e: if e.code == 400: raise AuthCanceled(self) else: raise except (ValueError, KeyError): raise AuthUnknownError(self)
def auth_complete(self, *args, **kwargs): """Complete auth process. Check LinkedIn error response.""" oauth_problem = self.request.GET.get('oauth_problem') if oauth_problem: if oauth_problem == 'user_refused': raise AuthCanceled(self, '') else: raise AuthUnknownError(self, 'LinkedIn error was %s' % oauth_problem) return super(LinkedinAuth, self).auth_complete(*args, **kwargs)
def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" guid = self._get_guid(access_token) url = 'http://social.yahooapis.com/v1/user/%s/profile?format=json' \ % guid request = self.oauth_request(access_token, url) response = self.fetch_response(request) try: return simplejson.loads(response)['profile'] except ValueError: raise AuthUnknownError('Error during profile retrieval, ' \ 'please, try again later')
def _get_guid(self, access_token): """ Beause you have to provide GUID for every API request it's also returned during one of OAuth calls """ url = 'http://social.yahooapis.com/v1/me/guid?format=json' request = self.oauth_request(access_token, url) response = self.fetch_response(request) try: json = simplejson.loads(response) return json['guid']['value'] except ValueError: raise AuthUnknownError('Error during user id retrieval, ' \ 'please, try again later')
def auth_complete(self, *args, **kwargs): """Completes loging process, must return user instance""" access_token = None response = self.response if 'code' in self.data: access_token = self.response['access_token'][0] if 'expires' in self._response: expires = self.response['expires'][0] if 'signed_request' in self.data: if response is not None: access_token = response.get('access_token') or \ response.get('oauth_token') or \ self.data.get('access_token') if 'expires' in self._response: expires = response['expires'] if access_token: data = self.user_data(access_token) if not isinstance(data, dict): # From time to time Facebook responds back a JSON with just # False as value, the reason is still unknown, but since the # data is needed (it contains the user ID used to identify the # account on further logins), this app cannot allow it to # continue with the auth process. raise AuthUnknownError( self, 'An error ocurred while ' 'retrieving users Facebook ' 'data') data['access_token'] = access_token # expires will not be part of response if offline access # premission was requested if expires: data['expires'] = expires kwargs.update({ 'auth': self, 'response': data, self.AUTH_BACKEND.name: True }) return authenticate(*args, **kwargs) else: if self.data.get('error') == 'access_denied': raise AuthCanceled(self) else: raise AuthException(self)
def auth_complete(self, *args, **kwargs): """Complete auth process""" response = self.consumer().complete(dict(self.data.items()), self.request.build_absolute_uri()) if not response: raise AuthException(self, 'OpenID relying party endpoint') elif response.status == SUCCESS: kwargs.update({ 'auth': self, 'response': response, self.AUTH_BACKEND.name: True }) return authenticate(*args, **kwargs) elif response.status == FAILURE: raise AuthFailed(self, response.message) elif response.status == CANCEL: raise AuthCanceled(self) else: raise AuthUnknownError(self, response.status)
def do_auth(self, access_token, expires=None, *args, **kwargs): data = self.user_data(access_token) if not isinstance(data, dict): # From time to time Facebook responds back a JSON with just # False as value, the reason is still unknown, but since the # data is needed (it contains the user ID used to identify the # account on further logins), this app cannot allow it to # continue with the auth process. raise AuthUnknownError( self, 'An error ocurred while ' 'retrieving users Facebook ' 'data') data['access_token'] = access_token if expires: # expires is None on offline access data['expires'] = expires kwargs.update({ 'auth': self, 'response': data, self.AUTH_BACKEND.name: True }) return authenticate(*args, **kwargs)
def auth_complete(self, *args, **kwargs): """Completes loging process, must return user instance""" access_token = None expires = None if 'code' in self.data: state = self.validate_state() url = ACCESS_TOKEN + urlencode( { 'client_id': setting('FACEBOOK_APP_ID'), 'redirect_uri': self.get_redirect_uri(state), 'client_secret': setting('FACEBOOK_API_SECRET'), 'code': self.data['code'] }) try: response = cgi.parse_qs(urlopen(url).read()) except HTTPError: raise AuthFailed(self, 'There was an error authenticating ' \ 'the app') access_token = response['access_token'][0] if 'expires' in response: expires = response['expires'][0] if 'signed_request' in self.data: response = load_signed_request(self.data.get('signed_request')) if response is not None: access_token = response.get('access_token') or \ response.get('oauth_token') or \ self.data.get('access_token') if 'expires' in response: expires = response['expires'] if access_token: data = self.user_data(access_token) if not isinstance(data, dict): # From time to time Facebook responds back a JSON with just # False as value, the reason is still unknown, but since the # data is needed (it contains the user ID used to identify the # account on further logins), this app cannot allow it to # continue with the auth process. raise AuthUnknownError(self, 'An error ocurred while ' \ 'retrieving users Facebook ' \ 'data') data['access_token'] = access_token # expires will not be part of response if offline access # premission was requested if expires: data['expires'] = expires kwargs.update({ 'auth': self, 'response': data, self.AUTH_BACKEND.name: True }) return authenticate(*args, **kwargs) else: if self.data.get('error') == 'access_denied': raise AuthCanceled(self) else: raise AuthException(self)
class BaseOAuth2(BaseOAuth): """Base class for OAuth2 providers. OAuth2 draft details at: http://tools.ietf.org/html/draft-ietf-oauth-v2-10 Attributes: AUTHORIZATION_URL Authorization service url ACCESS_TOKEN_URL Token URL """ AUTHORIZATION_URL = None ACCESS_TOKEN_URL = None SCOPE_SEPARATOR = ' ' RESPONSE_TYPE = 'code' SCOPE_VAR_NAME = None DEFAULT_SCOPE = None REDIRECT_STATE = True def state_token(self): """Generate csrf token to include as state parameter.""" return get_random_string(32) def get_redirect_uri(self, state): """Build redirect_uri with redirect_state parameter.""" uri = self.redirect_uri if self.REDIRECT_STATE: uri = url_add_parameters(uri, {'redirect_state': state}) return uri def auth_url(self): """Return redirect url""" client_id, client_secret = self.get_key_and_secret() state = self.state_token() # Store state in session for further request validation. The state # value is passed as state parameter (as specified in OAuth2 spec), but # also added to redirect_uri, that way we can still verify the request # if the provider doesn't implement the state parameter. self.request.session[self.AUTH_BACKEND.name + '_state'] = state args = { 'client_id': client_id, 'state': state, 'redirect_uri': self.get_redirect_uri(state) } scope = self.get_scope() if scope: args['scope'] = self.SCOPE_SEPARATOR.join(self.get_scope()) if self.RESPONSE_TYPE: args['response_type'] = self.RESPONSE_TYPE args.update(self.auth_extra_arguments()) if self.request.META.get('QUERY_STRING'): query_string = '&' + self.request.META['QUERY_STRING'] else: query_string = '' return self.AUTHORIZATION_URL + '?' + urlencode(args) + query_string def validate_state(self): """Validate state value. Raises exception on error, returns state value if valid.""" state = self.request.session.get(self.AUTH_BACKEND.name + '_state') request_state = self.data.get('state') or \ self.data.get('redirect_state') if not request_state: raise AuthMissingParameter(self, 'state') elif not state: raise AuthStateMissing(self, 'state') elif not constant_time_compare(request_state, state): raise AuthStateForbidden(self) return state def auth_complete(self, *args, **kwargs): """Completes loging process, must return user instance""" if self.data.get('error'): error = self.data.get('error_description') or self.data['error'] raise AuthFailed(self, error) state = self.validate_state() client_id, client_secret = self.get_key_and_secret() params = { 'grant_type': 'authorization_code', # request auth code 'code': self.data.get('code', ''), # server response code 'client_id': client_id, 'client_secret': client_secret, 'redirect_uri': self.get_redirect_uri(state) } headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json' } request = Request(self.ACCESS_TOKEN_URL, data=urlencode(params), headers=headers) try: response = simplejson.loads(dsa_urlopen(request).read()) except HTTPError, e: if e.code == 400: raise AuthCanceled(self) else: raise except (ValueError, KeyError): raise AuthUnknownError(self)