def auth_complete(self, *args, **kwargs): """Completes logging process, must return user instance""" self.process_error(self.data) params = self.auth_complete_params(self.validate_state()) request = Request(self.ACCESS_TOKEN_URL, data=urlencode(params), headers=self.auth_headers()) try: response = json.loads(dsa_urlopen(request).read()) except HTTPError as e: logger.exception( "plugins.auth.error", extra={ "class": type(self), "status_code": e.code, "response": e.read()[:128] }, ) raise AuthUnknownError(self) except (ValueError, KeyError): raise AuthUnknownError(self) self.process_error(response) return self.do_auth(response["access_token"], response=response, *args, **kwargs)
class StackoverflowAuth(BaseOAuth2): """Stackoverflow OAuth2 mechanism""" AUTHORIZATION_URL = STACKOVERFLOW_AUTHORIZATION_URL ACCESS_TOKEN_URL = STACKOVERFLOW_ACCESS_TOKEN_URL AUTH_BACKEND = StackoverflowBackend SETTINGS_KEY_NAME = 'STACKOVERFLOW_CLIENT_ID' SETTINGS_SECRET_NAME = 'STACKOVERFLOW_CLIENT_SECRET' SCOPE_SEPARATOR = ',' # See: https://api.stackexchange.com/docs/authentication#scope SCOPE_VAR_NAME = 'STACKOVERFLOW_EXTENDED_PERMISSIONS' def auth_complete(self, *args, **kwargs): """Completes loging process, must return user instance""" self.process_error(self.data) params = self.auth_complete_params(self.validate_state()) request = Request(self.ACCESS_TOKEN_URL, data=urlencode(params), headers=self.auth_headers()) try: response = dict(parse_qsl(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""" self.process_error(self.data) params = self.auth_complete_params(self.validate_state()) try: response = requests.post(self.ACCESS_TOKEN_URL, data=params, headers=self.auth_headers()) response.raise_for_status() except requests.exceptions.HTTPError as e: if e.code == 400: raise AuthCanceled(self) else: raise else: try: response = response.json() except (ValueError, KeyError): raise AuthUnknownError(self) response.pop('data') self.process_error(response) return self.do_auth(response['access_token'], response=response, *args, **kwargs)
def auth_complete(self, *args, **kwargs): """Complete auth process""" logger.debug('auth_complete ARGS=%s, KWARGS=%s' % (args, kwargs)) response = self.consumer().complete(dict(self.data.items()), self.build_absolute_uri()) logger.debug('response and status %s / %s' % (response, response.status)) if not response: logger.debug('will throw AuthException') 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: logger.debug('will throw AuthFailed(%s)' % response.message) raise AuthFailed(self, response.message) elif response.status == CANCEL: logger.debug('will throw AuthCanceled') raise AuthCanceled(self) else: logger.debug('will throw AuthUnknownError(%s)' % response.status) raise AuthUnknownError(self, response.status)
def process_error(self, data): error = self.request.GET.get('error', '') if error: if error == 'access_denied': raise AuthCanceled(self) else: raise AuthUnknownError(self, 'Jawbone error was %s' % error) return super(JawboneAuth, self).process_error(data)
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(dsa_urlopen(url)) except (ValueError, IOError): raise AuthUnknownError('Error during profile retrieval, ' 'please, try again later')
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): """Complete auth process""" response = self.consumer().complete(dict(self.data.items()), self.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 auth_complete(self, *args, **kwargs): """Completes loging process, must return user instance""" self.process_error(self.data) params = self.auth_complete_params(self.validate_state()) request = Request(self.ACCESS_TOKEN_URL, data=urlencode(params), headers=self.auth_headers()) try: response = json.loads(dsa_urlopen(request).read()) except HTTPError as e: if e.code == 400: raise AuthCanceled(self) else: raise except (ValueError, KeyError): raise AuthUnknownError(self) self.process_error(response) return self.do_auth(response['access_token'], response=response, *args, **kwargs)
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)
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 REFRESH_TOKEN_URL = None RESPONSE_TYPE = 'code' REDIRECT_STATE = True STATE_PARAMETER = True def state_token(self): """Generate csrf token to include as state parameter.""" return get_random_string(32) def get_redirect_uri(self, state=None): """Build redirect_uri with redirect_state parameter.""" uri = self.redirect_uri if self.REDIRECT_STATE and state: uri = url_add_parameters(uri, {'redirect_state': state}) return uri def auth_params(self, state=None): client_id, client_secret = self.get_key_and_secret() params = { 'client_id': client_id, 'redirect_uri': self.get_redirect_uri(state) } if self.STATE_PARAMETER and state: params['state'] = state if self.RESPONSE_TYPE: params['response_type'] = self.RESPONSE_TYPE return params def auth_url(self): """Return redirect url""" if self.STATE_PARAMETER or self.REDIRECT_STATE: # 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. # Reuse token if any. name = self.AUTH_BACKEND.name + '_state' state = self.request.session.get(name) or self.state_token() self.request.session[self.AUTH_BACKEND.name + '_state'] = state else: state = None params = self.auth_params(state) params.update(self.get_scope_argument()) params.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(params) + query_string def validate_state(self): """Validate state value. Raises exception on error, returns state value if valid.""" if not self.STATE_PARAMETER and not self.REDIRECT_STATE: return None state = self.request.session.get(self.AUTH_BACKEND.name + '_state') if 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 process_error(self, data): if data.get('error'): error = self.data.get('error_description') or self.data['error'] raise AuthFailed(self, error) def auth_complete_params(self, state=None): client_id, client_secret = self.get_key_and_secret() return { '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) } @classmethod def auth_headers(cls): return { 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json' } def auth_complete(self, *args, **kwargs): """Completes loging process, must return user instance""" self.process_error(self.data) params = self.auth_complete_params(self.validate_state()) request = Request(self.ACCESS_TOKEN_URL, data=urlencode(params), headers=self.auth_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)