From 574d99e425caf8c8ec72d84640231191d53835b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Nowacki?= Date: Sat, 25 Mar 2023 15:27:11 +0100 Subject: [PATCH 1/3] first attempt at drf authentication class implementation --- django_firebase_auth/conf.py | 9 ++++++ django_firebase_auth/drf_authentication.py | 33 ++++++++++++++++++++++ django_firebase_auth/v0/__init__.py | 1 + django_firebase_auth/views.py | 13 +++------ setup.py | 1 + 5 files changed, 48 insertions(+), 9 deletions(-) create mode 100644 django_firebase_auth/drf_authentication.py diff --git a/django_firebase_auth/conf.py b/django_firebase_auth/conf.py index d869786..2bdf9c9 100644 --- a/django_firebase_auth/conf.py +++ b/django_firebase_auth/conf.py @@ -1,5 +1,9 @@ +import importlib + from django.conf import settings +from django_firebase_auth.user_getter import AbstractUserGetter + AUTH_BACKEND = settings.DJANGO_FIREBASE_AUTH_AUTH_BACKEND SERVICE_ACCOUNT_FILE = settings.DJANGO_FIREBASE_AUTH_SERVICE_ACCOUNT_FILE WEB_API_KEY = settings.DJANGO_FIREBASE_AUTH_WEB_API_KEY @@ -11,3 +15,8 @@ GET_OR_CREATE_USER_CLASS = getattr(settings, "DJANGO_FIREBASE_AUTH_GET_OR_CREATE_USER_CLASS", 'django_firebase_auth.user_getter:EmailOnlyUserGetter') ADMIN_LOGIN_REDIRECT_URL = "admin:index" + + +GET_OR_CREATE_USER_MODULE, GET_OR_CREATE_USER_CLASS_NAME = GET_OR_CREATE_USER_CLASS.split(':') +GET_OR_CREATE_USER_CLASS = getattr(importlib.import_module(GET_OR_CREATE_USER_MODULE), GET_OR_CREATE_USER_CLASS_NAME) +user_getter: AbstractUserGetter = GET_OR_CREATE_USER_CLASS(CREATE_USER_IF_NOT_EXISTS) diff --git a/django_firebase_auth/drf_authentication.py b/django_firebase_auth/drf_authentication.py new file mode 100644 index 0000000..398a902 --- /dev/null +++ b/django_firebase_auth/drf_authentication.py @@ -0,0 +1,33 @@ +from django.utils.functional import SimpleLazyObject +from rest_framework.authentication import BaseAuthentication +from rest_framework.exceptions import AuthenticationFailed + +from django_firebase_auth.conf import user_getter +from django_firebase_auth.views import verify_firebase_account, AuthError, NoAuthHeader + + +class LazyUser(SimpleLazyObject): + is_authenticated = True + is_anonymous = False + + def __bool__(self): + return True + + +class JWTAuthentication(BaseAuthentication): + """ + Use Django's session framework for authentication. + """ + + def authenticate(self, request): + """ + Returns a `User` if the request session currently has a logged in user. + Otherwise returns `None`. + """ + try: + jwt_payload = verify_firebase_account(request.headers) + except NoAuthHeader: + return None + except AuthError as ex: + raise AuthenticationFailed(code=ex.error_type, detail=ex.error_type) + return LazyUser(lambda: user_getter.get_or_create_user(jwt_payload)), None diff --git a/django_firebase_auth/v0/__init__.py b/django_firebase_auth/v0/__init__.py index 36d7d03..baca02c 100644 --- a/django_firebase_auth/v0/__init__.py +++ b/django_firebase_auth/v0/__init__.py @@ -1 +1,2 @@ from ..user_getter import AbstractUserGetter +from ..drf_authentication import JWTAuthentication diff --git a/django_firebase_auth/views.py b/django_firebase_auth/views.py index f2177d0..2baa360 100644 --- a/django_firebase_auth/views.py +++ b/django_firebase_auth/views.py @@ -17,13 +17,8 @@ from django.views import View from django_firebase_auth.conf import AUTH_BACKEND, SERVICE_ACCOUNT_FILE, WEB_API_KEY, AUTH_DOMAIN, JWT_HEADER_NAME, \ - ALLOW_NOT_CONFIRMED_EMAILS, ENABLE_GOOGLE_LOGIN, ADMIN_LOGIN_REDIRECT_URL, GET_OR_CREATE_USER_CLASS, \ - CREATE_USER_IF_NOT_EXISTS -from django_firebase_auth.user_getter import AbstractUserGetter, UserNotFound - -GET_OR_CREATE_USER_MODULE, GET_OR_CREATE_USER_CLASS_NAME = GET_OR_CREATE_USER_CLASS.split(':') -GET_OR_CREATE_USER_CLASS = getattr(importlib.import_module(GET_OR_CREATE_USER_MODULE), GET_OR_CREATE_USER_CLASS_NAME) -user_getter: AbstractUserGetter = GET_OR_CREATE_USER_CLASS(CREATE_USER_IF_NOT_EXISTS) + ALLOW_NOT_CONFIRMED_EMAILS, ENABLE_GOOGLE_LOGIN, ADMIN_LOGIN_REDIRECT_URL, user_getter +from django_firebase_auth.user_getter import UserNotFound if SERVICE_ACCOUNT_FILE: firebase_credentials = credentials.Certificate(SERVICE_ACCOUNT_FILE) @@ -66,7 +61,7 @@ class EmailNotVerified(AuthError): def authenticate(request: HttpRequest): try: - jwt_payload = _verify_firebase_account(request.headers) + jwt_payload = verify_firebase_account(request.headers) except AuthError as ex: return JsonResponse(ex.make_response_body(), status=401) @@ -84,7 +79,7 @@ def logout(request: HttpRequest): return JsonResponse({"status": "ok"}) -def _verify_firebase_account(headers: HttpHeaders) -> dict: +def verify_firebase_account(headers: HttpHeaders) -> dict: jwt = headers.get(JWT_HEADER_NAME) if jwt is None: raise NoAuthHeader() diff --git a/setup.py b/setup.py index e2cf556..b81314c 100644 --- a/setup.py +++ b/setup.py @@ -14,4 +14,5 @@ include_package_data=True, python_requires='>=3.6', install_requires=["firebase_admin~=5.2.0"], + extras_require={'djangorestframework': ['djangorestframework>=3']}, ) From a8bcada7ab701e20506bdb57dbde1ae2758ee7b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Nowacki?= Date: Sat, 25 Mar 2023 16:22:14 +0100 Subject: [PATCH 2/3] firebase_uid without looking into DB --- django_firebase_auth/drf_authentication.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/django_firebase_auth/drf_authentication.py b/django_firebase_auth/drf_authentication.py index 398a902..8642323 100644 --- a/django_firebase_auth/drf_authentication.py +++ b/django_firebase_auth/drf_authentication.py @@ -10,6 +10,10 @@ class LazyUser(SimpleLazyObject): is_authenticated = True is_anonymous = False + def __init__(self, func, firebase_uid): + self.__dict__['firebase_uid'] = firebase_uid + super().__init__(func) + def __bool__(self): return True @@ -30,4 +34,4 @@ def authenticate(self, request): return None except AuthError as ex: raise AuthenticationFailed(code=ex.error_type, detail=ex.error_type) - return LazyUser(lambda: user_getter.get_or_create_user(jwt_payload)), None + return LazyUser(lambda: user_getter.get_or_create_user(jwt_payload), jwt_payload['uid']), None From 17d40fa84605813692d164453d03c9b13f37bce6 Mon Sep 17 00:00:00 2001 From: Olzhas Arystanov Date: Tue, 14 May 2024 20:29:46 +0500 Subject: [PATCH 3/3] Bump firebase-admin dependency to 6.5.0 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index b81314c..82225c3 100644 --- a/setup.py +++ b/setup.py @@ -13,6 +13,6 @@ package_data={'': ['templates/firebase_authentication/*.html']}, include_package_data=True, python_requires='>=3.6', - install_requires=["firebase_admin~=5.2.0"], + install_requires=["firebase_admin~=6.5.0"], extras_require={'djangorestframework': ['djangorestframework>=3']}, )