def _facebook_authenticate(self, signed_request): try: [encoded_signature, payload] = signed_request.split('.') data = json.loads(_base64_url_decode(payload).decode('utf-8')) actual_signature = _base64_url_decode(encoded_signature) except ValueError as error: flask.abort(422, error) for required_field in ('algorithm', 'user_id'): if not data.get(required_field): flask.abort( 422, 'Le champs %s est requis : %s' % (required_field, data)) if data['algorithm'].lower() != 'hmac-sha256': flask.abort( 422, 'Algorithme d\'encryption inconnu "%s"' % data['algorithm']) expected_signature = hmac.new(_FACEBOOK_SECRET, payload.encode('utf-8'), hashlib.sha256).digest() if expected_signature != actual_signature: flask.abort(403, 'Mauvaise signature') response = user_pb2.AuthResponse() user_dict = self._db.user.find_one({'facebookId': data['user_id']}) user_id = str(user_dict['_id']) if user_dict else '' if proto.parse_from_mongo(user_dict, response.authenticated_user): response.authenticated_user.user_id = user_id else: response.authenticated_user.facebook_id = data['user_id'] self._save_new_user(response.authenticated_user) response.is_new_user = True return response
def _google_authenticate(self, token_id): try: id_info = client.verify_id_token(token_id, GOOGLE_SSO_CLIENT_ID) except crypt.AppIdentityError as error: flask.abort(401, "Mauvais jeton d'authentification : %s" % error) if id_info.get('iss') not in _GOOGLE_SSO_ISSUERS: flask.abort( 401, "Fournisseur d'authentification invalide : %s." % id_info.get('iss', '<none>')) response = user_pb2.AuthResponse() user_dict = self._db.user.find_one({'googleId': id_info['sub']}) user_id = str(user_dict['_id']) if user_dict else '' if proto.parse_from_mongo(user_dict, response.authenticated_user): response.authenticated_user.user_id = user_id else: self._assert_user_not_existing(id_info['email']) response.authenticated_user.profile.email = id_info['email'] response.authenticated_user.profile.picture_url = id_info.get( 'picture', '') response.authenticated_user.google_id = id_info['sub'] self._save_new_user(response.authenticated_user) response.is_new_user = True return response
def _linked_in_authenticate(self, code): token_data = _get_oauth2_access_token( 'https://www.linkedin.com/oauth/v2/accessToken', code=code, client_id=_LINKED_IN_CLIENT_ID, client_secret=_LINKED_IN_CLIENT_SECRET, auth_name='LinkedIn Auth', ) authorization_header = '{} {}'.format( token_data.get('token_type', 'Bearer'), token_data.get('access_token', '')) user_info_response = requests.get( 'https://api.linkedin.com/v1/people/~:' '(id,location,first-name,last-name,email-address)?format=json', headers={'Authorization': authorization_header}) if user_info_response.status_code < 200 or user_info_response.status_code >= 400: logging.warning('LinkedIn Auth fails (%d): "%s"', user_info_response.status_code, user_info_response.text) flask.abort(403, user_info_response.text) user_info = user_info_response.json() response = user_pb2.AuthResponse() # TODO(cyrille): Factorize with other 3rd party auth. user_dict = self._user_db.user.find_one( {'linkedInId': user_info['id']}) user_id = str(user_dict.pop('_id')) if user_dict else '' if proto.parse_from_mongo(user_dict, response.authenticated_user): response.authenticated_user.user_id = user_id self._handle_returning_user(response) else: email = user_info.get('emailAddress') if email: self._assert_user_not_existing(email) response.authenticated_user.profile.email = email user = response.authenticated_user user.linked_in_id = user_info['id'] # TODO(pascal): Handle the case where one of the name is missing. user.profile.name = user_info.get('firstName', '') user.profile.last_name = user_info.get('lastName', '') self._save_new_user(user) response.is_new_user = True response.auth_token = create_token(response.authenticated_user.user_id, 'auth') return response
def _google_authenticate(self, token_id): id_info = decode_google_id_token(token_id) response = user_pb2.AuthResponse() user_dict = self._db.user.find_one({'googleId': id_info['sub']}) user_id = str(user_dict['_id']) if user_dict else '' if proto.parse_from_mongo(user_dict, response.authenticated_user): response.authenticated_user.user_id = user_id else: self._assert_user_not_existing(id_info['email']) response.authenticated_user.profile.email = id_info['email'] response.authenticated_user.profile.picture_url = id_info.get('picture', '') response.authenticated_user.google_id = id_info['sub'] self._save_new_user(response.authenticated_user) response.is_new_user = True # TODO(benoit): Check token provide enough guarantee for this use case. response.auth_token = create_token(response.authenticated_user.user_id, 'auth') return response
def _token_authenticate(self, auth_request): now = int(time.time()) response = user_pb2.AuthResponse() response.hash_salt = _timestamped_hash(now, auth_request.email) try: user_id = objectid.ObjectId(auth_request.user_id) except objectid.InvalidId: flask.abort( 400, 'L\'identifiant utilisateur "{}" n\'a pas le bon format.'. format(auth_request.user_id)) user_dict = self._user_db.user.find_one({'_id': user_id}) if not user_dict: flask.abort(404, 'Utilisateur inconnu.') try: if not _assert_valid_salt(auth_request.auth_token, str(user_id), now, validity_seconds=datetime.timedelta( days=5).total_seconds()): flask.abort(403, "Token d'authentification périmé") except ValueError as error: flask.abort( 403, "Le sel n'a pas été généré par ce serveur : {}.".format(error)) if not proto.parse_from_mongo(user_dict, response.authenticated_user): flask.abort( 500, 'Les données utilisateur sont corrompues dans la base de données.' ) response.auth_token = create_token(str(user_id), 'auth') response.authenticated_user.user_id = str(user_id) self._handle_returning_user(response) return response
def _password_authenticate(self, auth_request): now = int(time.time()) response = user_pb2.AuthResponse() response.hash_salt = _timestamped_hash(now, auth_request.email) user_dict = self._user_db.user.find_one( {'profile.email': auth_request.email}) if not user_dict: return self._password_register(auth_request, response) user_id = str(user_dict['_id']) response.auth_token = create_token(user_id, 'auth') user_auth_dict = self._user_db.user_auth.find_one( {'_id': user_dict['_id']}) if not user_auth_dict: if user_dict.get('googleId'): auth_method = ' (Google)' elif user_dict.get('facebookId'): auth_method = ' (Facebook)' else: auth_method = '' flask.abort( 403, "L'utilisateur existe mais utilise un autre moyen de connexion{}." .format(auth_method)) if not auth_request.hashed_password: # User exists but did not sent a passwordt: probably just getting some fresh salt. return response if auth_request.auth_token: self._reset_password(auth_request, user_id, user_auth_dict) user_dict['userId'] = user_id if not proto.parse_from_mongo(user_dict, response.authenticated_user): flask.abort( 500, 'Les données utilisateur sont corrompues dans la base de données.' ) return response if not auth_request.hash_salt: # User exists but has not sent salt: probably just # getting some fresh salt. return response # Check that salt is valid. salt = auth_request.hash_salt try: if not _assert_valid_salt(salt, auth_request.email, now): return response except ValueError as error: flask.abort( 403, "Le sel n'a pas été généré par ce serveur : {}.".format(error)) stored_hashed_password = user_auth_dict.get('hashedPassword') hashed_password = hashlib.sha1() hashed_password.update(salt.encode('ascii')) hashed_password.update(stored_hashed_password.encode('ascii')) request_hashed_password = binascii.unhexlify( auth_request.hashed_password) if request_hashed_password != hashed_password.digest(): flask.abort(403, 'Mot de passe erroné.') if not proto.parse_from_mongo(user_dict, response.authenticated_user): flask.abort( 500, 'Les données utilisateur sont corrompues dans la base de données.' ) response.authenticated_user.user_id = user_id self._handle_returning_user(response) return response
def _pe_connect_authenticate(self, code, nonce): token_data = _get_oauth2_access_token( 'https://authentification-candidat.pole-emploi.fr/connexion/oauth2/access_token?' 'realm=/individu', code=code, client_id=_EMPLOI_STORE_CLIENT_ID, client_secret=_EMPLOI_STORE_CLIENT_SECRET, auth_name='PE Connect', ) if token_data.get('nonce') != nonce: flask.abort(403, 'Mauvais paramètre nonce') authorization_header = '{} {}'.format( token_data.get('token_type', 'Bearer'), token_data.get('access_token', '')) scopes = token_data.get('scope', '').split(' ') user_info_response = requests.get( 'https://api.emploi-store.fr/partenaire/peconnect-individu/v1/userinfo', headers={'Authorization': authorization_header}) if user_info_response.status_code < 200 or user_info_response.status_code >= 400: logging.warning('PE Connect fails (%d): "%s"', user_info_response.status_code, user_info_response.text) flask.abort(403, user_info_response.text) user_info = user_info_response.json() city = None if 'coordonnees' in scopes: coordinates_response = requests.get( 'https://api.emploi-store.fr/partenaire/peconnect-coordonnees/v1/coordonnees', headers={ 'Authorization': authorization_header, 'pe-nom-application': 'Bob Emploi', }) if coordinates_response.status_code >= 200 and coordinates_response.status_code < 400: coordinates = coordinates_response.json() city = geo.get_city_proto(coordinates.get('codeINSEE')) job = None if 'competences' in scopes: competences_response = requests.get( 'https://api.emploi-store.fr/partenaire/peconnect-competences/v1/competences', headers={'Authorization': authorization_header}, ) if competences_response.status_code >= 200 and competences_response.status_code < 400: competences = competences_response.json() job_id, rome_id = next( ((c.get('codeAppellation'), c.get('codeRome')) for c in competences), (None, None)) job = jobs.get_job_proto(self._db, job_id, rome_id) response = user_pb2.AuthResponse() user_dict = self._user_db.user.find_one( {'peConnectId': user_info['sub']}) user_id = str(user_dict.pop('_id')) if user_dict else '' if proto.parse_from_mongo(user_dict, response.authenticated_user): response.authenticated_user.user_id = user_id self._handle_returning_user(response) else: email = user_info.get('email') if email: self._assert_user_not_existing(email) response.authenticated_user.profile.email = email user = response.authenticated_user user.pe_connect_id = user_info['sub'] # TODO(pascal): Handle the case where one of the name is missing. user.profile.name = french.cleanup_firstname( user_info.get('given_name', '')) user.profile.last_name = french.cleanup_firstname( user_info.get('family_name', '')) user.profile.gender = \ _PE_CONNECT_GENDER.get(user_info.get('gender', ''), user_pb2.UNKNOWN_GENDER) if city or job: user.projects.add( is_incomplete=True, mobility=geo_pb2.Location(city=city) if city else None, target_job=job) self._save_new_user(user) response.is_new_user = True response.auth_token = create_token(response.authenticated_user.user_id, 'auth') return response