Loading…

Multiple Authentication Tokens per User with Django REST Framework

In this post I will show you how to set up token authentication with Django REST Framework (or short DRF), with the special twist that each user can have multiple different tokens at the same time. This is not possible with DRF out of the box and needs some custom code.

But why would you even want multiple tokens per user? Because you really want to use a different token for each client. If every client uses the same token, the token is basically a password. If you want to change the token in one client, you have to change it in every client.

Enough reasoning, let’s write some code. If you look at the definition of the Token model that comes with DRF, you might notice that it has a severe limitation: it has a One-to-One relationship to the user model, so each user can only have one token.

Luckily, it is quite easy to use a custom token model. First, you have to remove rest_framework.authtoken from your INSTALLED_APPS. Then create your own token model as a subclass of DRF’s Token:

from django.conf import settings
from django.db import models
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _

import rest_framework.authtoken.models


@python_2_unicode_compatible
class Token(rest_framework.authtoken.models.Token):
    # key is no longer primary key, but still indexed and unique
    key = models.CharField(_("Key"), max_length=40, db_index=True, unique=True)
    # relation to user is a ForeignKey, so each user can have more than one token
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL, related_name='auth_tokens',
        on_delete=models.CASCADE, verbose_name=_("User")
    )
    name = models.CharField(_("Name"), max_length=64)

    class Meta:
        unique_together = (('user', 'name'),)

Our custom model is hiding some fields inherited from the superclass. Field hiding works only for abstract models, but the Token class is abstract
as long as the rest_framework.authtoken app is not installed. You did remove it from your INSTALLED_APPS, right?

Note that hiding of fields from abstract models is a new feature in Django 1.10. In older versions of Django, field hiding is not permitted. If you are not using Django 1.10 yet, the easiest workaround is probably to just copy the required code from DRF.

You can put our Token model in an existing app or create a new one for it. If you decide to do the latter, don’t forget to add it to INSTALLED_APPS!

How is the new Token model different?

  • The OneToOne relationship to user model has been replaced with a ForeignKey, allowing multiple tokens per user
  • The key is indexed and unique, but no longer a primary key. That means Django will create a numeric primary key by default. Why does that make sense? If you have multiple tokens, you might want to have a way to manage them. That usually means referring to tokens by a URL. As the key is supposed to be secret, it should not appear in URLs, because URLs usually end up in a kind of places, e.g. server logs.
  • Tokens now have a name to be able to differentiate them. The name should be unique per user

Now you have to tell DRF to use our custom token class. For that, simply create a subclass of TokenAuthentication and override the model attribute. You can put that class in any module you want, I prefer creating a file called authentication.py in the same app as the token model.

import rest_framework.authentication

from .models import Token


class TokenAuthentication(rest_framework.authentication.TokenAuthentication):
    model = Token

Use this class in authentication_classes and you are all set! You can now create as many tokens as you want.

>>> user = User.objects.get(username='daniel')
>>> Token.objects.create(user=user, name='Foo')
>>> Token.objects.create(user=user, name='Bar')

Summary

So what did we do today?

  1. We created a custom Token model with a ForeignKey to User instead of a OneToOneField
  2. We created a custom authentication class that uses our Token

What’s next? If your site is already online and has tokens that are in use, you might want to create a data migration to keep your users happy. You also need some kind of management interface to manage the tokens. This could be handled in the admin or as part of some kind of account settings page. For now, both is left as an exercise to the reader.

If you have questions or anything to add, leave a comment below!

4 thoughts on “Multiple Authentication Tokens per User with Django REST Framework

  1. Thank. This is very useful for me. However I need to stick with Django 1.9. What code do I need to copy from DRF to make it work?
    Thanks

  2. Thanks I tried copying and wasnt successful. I also tried to upgrade to 1.11 with more success. However it seems I still have to rewerite the TokenAuthentication class to be able to support multiple tokens per user and take into account the name field. I use djoser project to expose REST Apis for registration/login etc. Thanks

Leave a Reply