Tracking API calls by User ID in Django
Tracking API calls by anonymous usage is only part of the story when it comes to leveraging API analytics and metrics. A lot more value can come from knowing exactly what users are using specific endpoints and how they are using them.
In order to track API calls and attribute them to a user, the API calls must be authenticated. For this particular example, I’m going to use a JSON Web Token, also referred to as a “JWT”. The JWT should be attached to the request. The specific field we will use to track the user is the JWT’s id field.
The code I am using is built from an official Django tutorial. It also includes the integration configuration I went over in the previous Django integration tutorial. All of the steps in this tutorial are performed against that codebase.
To get user tracking working I will:
- Configure my Moesif middleware in my Django API server to extract the user ID coming from the JWT
- Configure my endpoints to accept a JWT within the request
- Generate a JWT
- Attach a JWT to my request, and send it to the API
A few things to mention in this guide, before we start.
I will be generating a JWT from an endpoint within the REST service I have built. From a security perspective, it is not meant to be production-ready but merely a demonstration of how to get things started.
Our example assumes that your server and API are already integrated with Moesif. If you still need to integrate your API with Moesif, please take a look at our Django integration guide before proceeding if you want to follow along.
With that said, let’s jump into our example!
Step 1: Getting Started
You’ll notice that the User ID field is blank. This means that these events are not tagged to a specific user. Therefore, we cannot leverage user-specific metrics.
<img class="lazyload blur-up" data-src="/docs/images/guides/800/metrics-screen-without-user-id.png" alt="Moesif Metrics screen requests without a userID">
If you don’t see any traffic coming into your Moesif instance, ensure that you have set up the integration correctly.
Step 2: Adding User Tracking to Our Moesif Middleware
In order to add user tracking to our configuration, we need to supply an identifyUser
parameter to the Moesif middleware configuration. In our settings.py file, our Moesif configuration will now look like this:
# settings.py
def identifyUser(req, res):
if req.user and req.user.is_authenticated:
return req.user.username
else:
return None
MOESIF_MIDDLEWARE = {
'APPLICATION_ID': 'MOESIF APPLICATION_ID',
'IDENTIFY_USER': identifyUser # Optional hook to link API calls to users
}
This identifyUser function will parse out the field from the request you want to use to identify the user. The decoded JWT payload is available on the request via the user property.
NOTE: you don’t have to do follow this exactly. You could pull the unique identifier from any part of the request, including the header, if needed. I am following this convention as it has become a standard way of passing a user’s ID through a JWT. The only thing necessary is to have the users ID be a unique and unchanging value to ensure accurate metrics.
With the code we supplied, Moesif will now be able to attach a user ID to an API request when the middleware intercepts the request.
We now need to configure our endpoints to use the JWT. For that, we will use a package which will give us the JWT functionality we need. You can use another if you’d prefer but I will use the Django REST Framework Simple JWT package.
Step 3: Adding in the djangorestframework-simplejwt
Dependency
To add in the djangorestframework-simplejwt
dependency, run the following pip install command in your terminal:
pip install djangorestframework-simplejwt
NOTE: Make sure to use the correct pip command in the correct directory. If you’re using a new version of Python, you may want to use pip2, pip3, or pipenv instead of the above pip command.
Once this command completes, the djangorestframework-simplejwt
package will be added to your project and ready to use. Now we can begin to plug it into our code.
Step 4: Implementing djangorestframework-simplejwt
In our settings.py file, we will add the following DEFAULT_AUTHENTICATION_CLASSES entry to our REST_FRAMEWORK list, like so:
# settings.py
REST_FRAMEWORK = {
...
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
),
...
}
Now, in our urls.py file, we will import the package we will use for the JWT auth:
# urls.py
from rest_framework_simplejwt.views import TokenObtainPairView
We will also add in a path for /api/token to our urlpatterns list so we can obtain a JWT. That route will look like this:
# urls.py
urlpatterns = [
…
…
path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
]
Now to ensure our endpoint will enforce the incoming request to have a valid JWT attached to it, we need to go into our views.py and ensure that we are enforcing authentication for our views. Permission_classes should equal permissions.IsAuthenticated. The code will now look like this:
# views.py
class UserViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = User.objects.all().order_by('-date_joined')
serializer_class = UserSerializer
permission_classes = [permissions.IsAuthenticated]
Our next steps are to generate our JWT to attach to our request.
Step 5: Creating the JWT
In this example, I will be generating the JWT using the /api/token endpoint that we created before in our urls.py file.
In this case, I have a user I’ve created using the following command:
python manage.py createsuperuser --email admin@example.com --username admin
I will use this users credentials to generate the JWT to give me access to the /users endpoint.
After starting my server, I will then navigate to the /api/token endpoint (my url for this is http://localhost:8000/api/token/)
<img class="lazyload blur-up" data-src="/docs/images/guides/800/create_token_screen.png" alt="example text">
From here, I will add in my username (“admin”) and the password I input for this user. I’ll then click POST and be presented with a token to use.
<img class="lazyload blur-up" data-src="/docs/images/guides/800/jwt_generated.png" alt="example text">
Now, I’ll attach the JWT to the request in Postman.
Step 6: Sending a Request Through Postman
After opening up Postman, we will plug in the endpoint URL for our GET request. We will also add a Bearer Token with a value of the generated access JWT from the previous step. Here’s an example below:
<img class="lazyload blur-up" data-src="/docs/images/guides/800/postman-request.png" alt="example text">
Now, send off the request. At this point, the request will be received by the server. Once at the server, the following will occur: the JWT field you want to use as an ID for the call will be parsed out using the Moesif middleware’s identifyUser function and attached to the metric in Moesif.
Step 7: Verifying User Tracking
In Moesif, our request should be showing in the Events dashboard. Now we can see that a userID has been bound to the request.
You’ll see that the id passed in our JWT has now become the userID attached to the request we sent.
<img class="lazyload blur-up" data-src="/docs/images/guides/800/metrics-screen-with-user-id.png" alt="example text">
At this point, we are now able to start leveraging user-specific metrics instead of having all of our data be anonymous. This can help to power many of the more advanced and valuable features within Moesif.