def auth_complete(self, *args, **kwargs): """Completes login process, must return user instance""" self.process_error(self.data) try: response = self.request_access_token( self.ACCESS_TOKEN_URL, data=self.auth_complete_params(self.validate_state()), headers=self.auth_headers(), method=self.ACCESS_TOKEN_METHOD) except HTTPError as err: if err.response.status_code == 400: raise AuthCanceled(self) else: raise except KeyError: raise AuthUnknownError(self) if 'errcode' in response: return HttpResponse(json.dumps(response)) # raise AuthCanceled(self) self.process_error(response) return self.do_auth(response['access_token'], response=response, *args, **kwargs)
def get_apple_jwk(self): """Returns an iterable of apple jwk strings.""" keys = self.get_json(url=self.JWK_URL).get("keys") if not isinstance(keys, list) or not keys: raise AuthCanceled("Invalid jwk response") return (json.dumps(key) for key in keys)
def decode_id_token(self, id_token): '''Decode and validate JWT token from apple and return payload including user data.''' if not id_token: raise AuthCanceled("Missing id_token parameter") kid = jwt.get_unverified_header(id_token).get('kid') public_key = RSAAlgorithm.from_jwk(self.get_apple_jwk(kid)) try: decoded = jwt.decode( id_token, key=public_key, audience=self.setting("CLIENT"), algorithm="RS256", ) except PyJWTError: raise AuthCanceled("Token validation failed") return decoded
def decode_id_token(self, id_token): '''Decode and validate JWT token from apple and return payload including user data.''' if not id_token: raise AuthCanceled('Missing id_token parameter') for jwk_string in self.get_apple_jwk(): public_key = RSAAlgorithm.from_jwk(jwk_string) try: decoded = jwt.decode(id_token, key=public_key, audience=self.setting('CLIENT'), algorithm='RS256') break except PyJWTError: pass else: raise AuthCanceled('Token validation failed') return decoded
def get_apple_jwk(self, kid=None): '''Return requested Apple public key or all available.''' keys = self.get_json(url=self.JWK_URL).get("keys") if not isinstance(keys, list) or not keys: raise AuthCanceled("Invalid jwk response") if kid: return json.dumps([key for key in keys if key['kid'] == kid][0]) else: return (json.dumps(key) for key in keys)
def do_auth(self, access_token, *args, **kwargs): response = kwargs.pop('response', None) or {} jwt_string = response.get(self.TOKEN_KEY) or access_token if not jwt_string: raise AuthCanceled("Missing id_token parameter") decoded_data = self.decode_id_token(jwt_string) return super(AppleIdAuth, self).do_auth(access_token, response=decoded_data, *args, **kwargs)
def auth_complete(self, *args, **kwargs): """Completes loging process, must return user instance""" self.process_error(self.data) appid, secret = self.get_key_and_secret() try: if WEIXIN_WORK_SP: response = self.request_access_token( self.access_token_url(appid, secret), json=self.auth_complete_params(self.validate_state()), headers={ 'Content-Type': 'application/json', 'Accept': 'application/json' }, method=self.ACCESS_TOKEN_METHOD) else: response = self.request_access_token( self.access_token_url(appid, secret), data=self.auth_complete_params(self.validate_state()), headers=self.auth_headers(), method=self.ACCESS_TOKEN_METHOD) except HTTPError as err: if err.response.status_code == 400: raise AuthCanceled(self, response=err.response) else: raise except KeyError: raise AuthUnknownError(self) try: if response['errmsg'] != 'ok': raise AuthCanceled(self) except KeyError: pass # assume response is ok if 'errmsg' key not found self.process_error(response) access_token = response[ 'provider_access_token'] if WEIXIN_WORK_SP else response[ 'access_token'] return self.do_auth(access_token, response=response, *args, **kwargs)
def test_auth_complete_cancelation(self, super_auth_complete, validate_state): user = mommy.make(User) backend = RESTStateBackend() backend.session = {'_user_id': user.id} super_auth_complete.side_effect = AuthCanceled(backend) response = backend.auth_complete() assert response['location'] == reverse('user:redirect_to_app') validate_state.assert_called_once_with() super_auth_complete.assert_called_once_with(user=user) error_obj = AuthError.objects.last() assert error_obj.provider == 'rest-state' assert error_obj.message == 'Authentication process canceled' assert error_obj.user == user
def process_error(self, data): super().process_error(data) if data.get('serviceErrorCode'): raise AuthCanceled(self, data.get('message') or data.get('status'))