diff --git a/README.md b/README.md index 5573ea7..0aa14de 100755 --- a/README.md +++ b/README.md @@ -65,67 +65,11 @@ DRF_FIREBASE_AUTH = { # require that firebase user.email_verified is True 'FIREBASE_AUTH_EMAIL_VERIFICATION': os.getenv('FIREBASE_AUTH_EMAIL_VERIFICATION', False), - # function should accept firebase_admin.auth.UserRecord as argument - # and return str - 'FIREBASE_USERNAME_MAPPING_FUNC': map_firebase_uid_to_username } ``` You can get away with leaving all the settings as default except for `FIREBASE_SERVICE_ACCOUNT_KEY`, which is obviously required. -NOTE: `FIREBASE_USERNAME_MAPPING_FUNC` will replace behaviour in version < 1 as default (formerly provided by logic in `map_firebase_to_username_legacy`, described below). One can simply switch out this function. - -`drf_firebase_auth.utils` contains functions for mapping firebase user info to the Django username field (new in version >= 1). Any custom function can be supplied here, as long as it accepts a `firebase_admin.auth.UserRecord` argument. The supplied functions are common use-cases: - -```python -def map_firebase_to_username_legacy(firebase_user: auth.UserRecord) -> str: - try: - username = '_'.join( - firebase_user.display_name.split(' ') - if firebase_user.display_name - else str(uuid.uuid4()) - ) - return username if len(username) <= 30 else username[:30] - except Exception as e: - raise Exception(e) - - -def map_firebase_display_name_to_username( - firebase_user: auth.UserRecord -) -> str: - try: - return '_'.join(firebase_user.display_name.split(' ')) - except Exception as e: - raise Exception(e) - - -def map_firebase_uid_to_username( - firebase_user: auth.UserRecord -) -> str: - try: - return firebase_user.uid - except Exception as e: - raise Exception(e) - - -def map_firebase_email_to_username( - firebase_user: auth.UserRecord -) -> str: - try: - return get_firebase_user_email(firebase_user) - except Exception as e: - raise Exception(e) - - -def map_uuid_to_username( - _: auth.UserRecord -) -> str: - try: - return str(uuid.uuid4()) - except Exception as e: - raise Exception(e) -``` - Now that you have configured the application, run the migrations so that the Firebase data can be stored. ``` diff --git a/drf_firebase_auth/authentication.py b/drf_firebase_auth/authentication.py index a108244..e9b72cd 100755 --- a/drf_firebase_auth/authentication.py +++ b/drf_firebase_auth/authentication.py @@ -22,7 +22,7 @@ FirebaseUser, FirebaseUserProvider ) -from .utils import get_firebase_user_email +from .utils import get_firebase_user_uid, get_firebase_user_identifier from . import __title__ log = logging.getLogger(__title__) @@ -98,11 +98,12 @@ def _get_or_create_local_user( """ Attempts to return or create a local User from Firebase user data """ - email = get_firebase_user_email(firebase_user) - log.info(f'_get_or_create_local_user - email: {email}') + uid = get_firebase_user_uid(firebase_user) + identifier = get_firebase_user_identifier(firebase_user) + log.info(f'_get_or_create_local_user - email: {identifier}') user = None try: - user = User.objects.get(email=email) + user = User.objects.get(username=uid) log.info( f'_get_or_create_local_user - user.is_active: {user.is_active}' ) @@ -114,19 +115,19 @@ def _get_or_create_local_user( user.save() except User.DoesNotExist as e: log.error( - f'_get_or_create_local_user - User.DoesNotExist: {email}' + f'_get_or_create_local_user - User.DoesNotExist: {identifier}' ) if not api_settings.FIREBASE_CREATE_LOCAL_USER: raise Exception('User is not registered to the application.') username = \ api_settings.FIREBASE_USERNAME_MAPPING_FUNC(firebase_user) log.info( - f'_get_or_create_local_user - username: {username}' + f'_get_or_create_local_user - username: {uid}' ) try: user = User.objects.create_user( - username=username, - email=email + username=uid, + email=identifier ) user.last_login = timezone.now() if ( diff --git a/drf_firebase_auth/utils.py b/drf_firebase_auth/utils.py index fc44418..96b45a0 100644 --- a/drf_firebase_auth/utils.py +++ b/drf_firebase_auth/utils.py @@ -5,34 +5,26 @@ from firebase_admin import auth -def get_firebase_user_email(firebase_user: auth.UserRecord) -> str: +def get_firebase_user_uid(firebase_user: auth.UserRecord) -> str: try: - return ( - firebase_user.email - if firebase_user.email - else firebase_user.provider_data[0].email - ) + if firebase_user.uid: + return firebase_user.uid except Exception as e: raise Exception(e) -def map_firebase_to_username_legacy(firebase_user: auth.UserRecord) -> str: +def get_firebase_user_identifier(firebase_user: auth.UserRecord) -> str: try: - username = '_'.join( - firebase_user.display_name.split(' ') - if firebase_user.display_name - else str(uuid.uuid4()) - ) - return username if len(username) <= 30 else username[:30] - except Exception as e: - raise Exception(e) - - -def map_firebase_display_name_to_username( - firebase_user: auth.UserRecord -) -> str: - try: - return '_'.join(firebase_user.display_name.split(' ')) + if firebase_user.email: + return firebase_user.email + elif firebase_user.provider_data[0].email: + return firebase_user.provider_data[0].email + elif firebase_user.phone_number: + return firebase_user.phone_number + elif firebase_user.provider_data[0].phone_number: + return firebase_user.provider_data[0].phone_number + else: + raise Exception("Identifier not found, this would fail authentication process") except Exception as e: raise Exception(e) @@ -44,21 +36,3 @@ def map_firebase_uid_to_username( return firebase_user.uid except Exception as e: raise Exception(e) - - -def map_firebase_email_to_username( - firebase_user: auth.UserRecord -) -> str: - try: - return get_firebase_user_email(firebase_user) - except Exception as e: - raise Exception(e) - - -def map_uuid_to_username( - _: auth.UserRecord -) -> str: - try: - return str(uuid.uuid4()) - except Exception as e: - raise Exception(e) diff --git a/testapp/api/tests.py b/testapp/api/tests.py index ff56dc9..442a47d 100644 --- a/testapp/api/tests.py +++ b/testapp/api/tests.py @@ -10,7 +10,7 @@ from firebase_admin import auth as firebase_auth from drf_firebase_auth.settings import api_settings from drf_firebase_auth.utils import ( - get_firebase_user_email, + get_firebase_user_identifier, map_firebase_uid_to_username, map_firebase_email_to_username ) @@ -25,7 +25,7 @@ ) class WhoAmITests(APITestCase): - + def setUp(self): self._url = reverse('whoami') self._test_user_email = 'user@example.com' @@ -103,7 +103,7 @@ def test_authenticated_request(self): status.HTTP_403_FORBIDDEN, f'{api_settings.FIREBASE_CREATE_LOCAL_USER}' ) - + with self._MOCK_FIREBASE_CREATE_LOCAL_USER_TRUE: response = self.client.get(self._url) self.assertEqual( @@ -143,11 +143,11 @@ def test_user_creation_uid_as_username(self): ) ) firebase_user = self._get_test_user() - firebase_user_email = get_firebase_user_email(firebase_user) + firebase_user_email = get_firebase_user_identifier(firebase_user) with self._MOCK_FIREBASE_CREATE_LOCAL_USER_FALSE: before_count = User.objects.count() - + response = self.client.get(self._url) self.assertEqual( response.status_code, @@ -161,11 +161,11 @@ def test_user_creation_uid_as_username(self): with self.assertRaises(Exception): _ = User.objects.get(email=firebase_user_email) - + with self._MOCK_FIREBASE_CREATE_LOCAL_USER_TRUE: with self._MOCK_FIREBASE_USERNAME_MAPPING_FUNC_UID: before_count = User.objects.count() - + response = self.client.get(self._url) self.assertEqual( response.status_code, @@ -193,11 +193,11 @@ def test_user_creation_email_as_username(self): ) ) firebase_user = self._get_test_user() - firebase_user_email = get_firebase_user_email(firebase_user) + firebase_user_email = get_firebase_user_identifier(firebase_user) with self._MOCK_FIREBASE_CREATE_LOCAL_USER_FALSE: before_count = User.objects.count() - + response = self.client.get(self._url) self.assertEqual( response.status_code, @@ -211,11 +211,11 @@ def test_user_creation_email_as_username(self): with self.assertRaises(Exception): _ = User.objects.get(email=firebase_user_email) - + with self._MOCK_FIREBASE_CREATE_LOCAL_USER_TRUE: with self._MOCK_FIREBASE_USERNAME_MAPPING_FUNC_EMAIL: before_count = User.objects.count() - + response = self.client.get(self._url) self.assertEqual( response.status_code,