<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
Recent posts:
- So you want to build a social network?
- Mastering Project Estimation
- Typescript Validators Jamboree
- Revolutionize Animation: Build a Digital Human with Large Language Models
- View more posts...
Back to top