Extending django-oauth-toolkit

NOTE: You can read here the next part of this blog post.

I am building a REST API with Django, recently I had to do some work with Django-rest-framework and Django-oauth-toolkit, this works consist on using authentication tokens to fetch different schemes from the database; but this is not that application where using a single token allows you to fetch X, Y or Z schemes, if you have ever worked with OAuth applications or services you may know that some schemes are attached to different scopes or endpoints, by default every service includes the read and write scopes and in some cases this is enough for your application, but when you have different schemes and rules over your data structure trends to be a little bit complex.

A classic example of this is when using Google Services for authentication, the authorization screen will prompt you with a message asking if you want to give access to the application to read and sometimes write over your personal data.

Google Grant Screen

And the same thing will happen if you’re using for example a third party calendar, instead of using the normal Google calendar, once you signing on the third-party app this will ask you into the authorization screen if you want to grant this application access to read or write data over your calendar.

Google Grant Screen 2

Manos a la obra

Basically what we need to do is extend the default scopes with our custom scopes, but also allow to set different scopes in the application creation process, this will limit the scopes that a token can access.

For extending the the scopes list, you can use the following as described in the documentation settings:

OAUTH2_PROVIDER = {
    'SCOPES': {
        'account:ro': 'Grants the global permission to read any Object',
        'account:rw': 'Grants the global permission to read and write over any Object',
        'organization:ro': 'Grants the ability to read Organization Object data',
        'organization:rw': 'Grants the ability to read and write the Organization Object',
        ...
    }
}

Now we have a new scopes list but if you try to get a new token you'll see that this is requesting all the available scopes:

Scopes

After searching a lot about this topic on Django-oauth-toolkit issues, I found this issue requesting the same thing: "User should be able to grant scope to application"

The first thing that you need to do is create a custom model for storing your OAuth applications, once again we'll use the documentation to do this process, as described on the "Advanced topics" section we need to extend our model from the AbstractApplication model:

from django.db import models
from django.conf import settings
from django.contrib.postgres.fields import ArrayField
from oauth2_provider.models import AbstractApplication

class MyApplication(AbstractApplication):
    logo = models.ImageField()
    agree = models.BooleanField()
    scopes = ArrayField(
        models.CharField(
            max_length=255,
            choices=settings.OAUTH2_PROVIDER['SCOPES'].items(),
        )
    )

Now we need to generate a new migration for this model, I recommend you that this migration only shows the changes on this model, don't include changes from other models or apps and make sure that the migrations from django-oauth-toolkit have not been executed, you can use the following to check that:

python manage.py showmigrations
...
oauth2_provider
 [ ] 0001_initial
 [ ] 0002_auto_20190406_1805
tessa
 [ ] 0009_myapplication
...

After we ensure our migrations are ok, we need to edit the generated migration and add the following:

run_before = [
    ('oauth2_provider', '0001_initial'),
]

Then execute the migrations, if everything is ok, now declare the following in your settings:

OAUTH2_PROVIDER_APPLICATION_MODEL = 'app_name.MyApplication'

If you don't see the error models.E022 which stands for:

<function> contains a lazy reference to <app label>.<model>, but app <app label> isn’t installed or doesn’t provide model <model>.

If you dont see that in the console, this means that we have extended the Application model in the right way.

So far we just only have one part of this process, we now need to update views and some forms in order to allow custom scopes, we'll discuss that in the next part of this blog post.