A Cheat Sheet for Using your JWT Authentication with Django REST Framework to log in to Firebase - CodiMD
<center> # A Cheat Sheet for Using your JWT Authentication with Django REST Framework to log in to Firebase *Getting DRF, SimpleJWT and Firebase to play nice.* </center> --- [Firebase](https://firebase.google.com/) is a Google platform composed of more than eighteen products that can speed up the development of mobile and web applications. It has some really nice advanced features, such as real-time updates and horizontal scalability for greenfield software projects. However, applications with a considerable user base can end up being expensive to host on Firebase. For cases like this, it’s usually worth migrating to a different solution like Django. For a general strategy for how to do this, have a look at [How to Migrate from Firebase to Django](https://monadical.com/posts/from-firebase-to-django.html). Whether as a first step towards a larger migration, or a component of a hybrid approach (for example, if you want to augment Firebase with data that has to be hosted on your own servers), sometimes it’s useful to migrate the login process while retaining access to Firebase's services This post gives you a quick cheat sheet for **how to use Firebase services with a token emitted from your Django backend. The token will be both valid for Firebase and for your backend.** This way you’ll also be prepared for any future migrations from Firebase. We will set SimpleJWT, DRF and Django to log in to Firebase. ## Installation and Setup For this setup, we are going to use [DRF](https://www.django-rest-framework.org/) with [SimpleJWT](https://github.com/SimpleJWT/django-rest-framework-simplejwt). First, let’s install the packages. ```bash pip install djangorestframework pip install djangorestframework_simplejwt ``` Then let’s configure DRF to use `JWTAuthentication` and require authenticated requests as default. These SimpleJWT settings tell SimpleJWT to use the appropriate algorithm and set some of the claims required by Firebase without needing to install and call [firebase-admin-python](https://github.com/firebase/firebase-admin-python) to create the token. `settings.py` ```python3 # django-rest-framework - https://www.django-rest-framework.org/api-guide/settings/ REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES": ( "rest_framework_simplejwt.authentication.JWTAuthentication", ), "DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.IsAuthenticated",), } # See https://firebase.google.com/docs/auth/admin/create-custom-tokens#create_custom_tokens_using_a_third-party_jwt_library SIMPLE_JWT = { "ALGORITHM": "RS256", "SIGNING_KEY": env.str("FIREBASE_PRIVATE_KEY", multiline=True), "VERIFYING_KEY": env.str("FIREBASE_PUBLIC_KEY", multiline=True), "ISSUER": env.str("FIREBASE_SERVICE_EMAIL"), "USER_ID_CLAIM": "uid", # Firebase allows only max=1h "ACCESS_TOKEN_LIFETIME": timedelta(hours=1), "AUDIENCE": "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit", } ``` Also notice that we loaded the secret and sensitive information (SIGNING_KEY, VERIFYING_KEY, ISSUER) from environment variables for the required [service account](https://firebase.google.com/support/guides/service-accounts). Next we'll need to add URL routes for our token endpoints. `urls.py` ```python3 from django.urls import path from rest_framework_simplejwt.views import TokenRefreshView from .views import FirebaseTokenObtainPairView urlpatterns = [ path('api/token/', FirebaseTokenObtainPairView.as_view(), name='token_obtain_pair'), path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'), # Your other urls …. ] ``` The URL routes need to be connected to views -- let’s add a view to obtain the token pair and a serializer that will add the additional claims required by Firebase. `views.py` ```python3 from django.conf import settings from rest_framework_simplejwt.serializers import TokenObtainPairSerializer from rest_framework_simplejwt.views import TokenObtainPairView class FirebaseTokenObtainPairSerializer(TokenObtainPairSerializer): @classmethod def get_token(cls, user): token = super().get_token(user) # Add custom claims token["sub"] = settings.SIMPLE_JWT.get("ISSUER", "") # When the serializer is called token['exp'] does not reflect the settings.ACCES_TOKEN_LIFETIME # and is set to now + 1day,thus we subtract a day to get iat token["iat"] = token["exp"] - (60 * 60 * 24) # Add additional claims here token["claims"] = {"is_superuser": user.is_superuser, "is_staff": user.is_staff} return token class FirebaseTokenObtainPairView(TokenObtainPairView): serializer_class = FirebaseTokenObtainPairSerializer ``` Here we overrode the view and the serializer to include all of the required fields for Firebase. An alternative approach would be to generate the token with `firebase-admin-python` as follows: ```python3 uid = 'some-uid' additional_claims = { 'premiumAccount': True } custom_token = auth.create_custom_token(uid, additional_claims) ``` When we have the token, we return it at the view. And that's it! We have our token. ## Test Let’s do a quick test with [httpie](https://httpie.org/) and one of our existing Django accounts. ```bash http post localhost:8000/api/token username=test password=test ``` We get a token valid for both our Django backend and Firebase services. Woot. ```bash HTTP/1.1 200 OK allow: POST, OPTIONS content-language: en content-length: 1945 content-type: application/json date: Mon, 19 Oct 2020 21:22:21 GMT server: uvicorn vary: Accept, Accept-Language, Origin x-content-type-options: nosniff x-frame-options: DENY x-xss-protection: 1; mode=block { "access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9…… ", "refresh":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9…… " } ``` ## Credits and Further Reading 1. https://simpleisbetterthancomplex.com/tutorial/2018/12/19/how-to-use-jwt-authentication-with-django-rest-framework.html 2. https://monadical.com/posts/from-firebase-to-django.html 3. https://firebase.google.com/docs/auth/admin/create-custom-tokens 4. https://firebase.google.com/support/guides/service-accounts

Back to top

Some other posts: