Bob
Bob

Reputation: 8714

Django DRF with oAuth2 using DOT (django-oauth-toolkit)

I am trying to make DRF work with oAuth2 (django-oauth-toolkit).

I was focusing on http://httplambda.com/a-rest-api-with-django-and-oauthw-authentication/

First I followed that instruction, but later, after getting authentication errors, I setup this demo: https://github.com/felix-d/Django-Oauth-Toolkit-Python-Social-Auth-Integration

Result was the same: I couldn't generate access token using this curl:

curl -X POST -d "grant_type=password&username=<user_name>&password=<password>" -u "<client_id>:<client_secret>" http://127.0.0.1:8000/o/token/

I got this error:

{"error": "unsupported_grant_type"}

The oAuth2 application was set with grant_type password. I changed grant_type to "client credentials" and tried this curl:

curl -X POST -d "grant_type=client_credentials" -u "<client_id>:<client_secret>" http://127.0.0.1:8000/o/token/

This worked and I got generated auth token.

After that I tried to get a list of all beers:

curl -H "Authorization: Bearer <auth_token>" http://127.0.0.1:8000/beers/

And I got this response:

{"detail":"You do not have permission to perform this action."}

This is the content of views.py that should show the beers:

from beers.models import Beer
from beers.serializer import BeerSerializer
from rest_framework import generics, permissions

class BeerList(generics.ListCreateAPIView):
    serializer_class = BeerSerializer
    permission_classes = (permissions.IsAuthenticated,)

    def get_queryset(self):
        user = self.request.user
        return Beer.objects.filter(owner=user)

    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)

I am not sure what can be the problem here. First with "unsuported grant type" and later with other curl call. This also happen to me when I did basic tutorial from django-oauth-toolkit. I am using Django 1.8.2 and python3.4

Thanks for all help!

My settings.py looks like this

import os
BASE_DIR = os.path.dirname(os.path.dirname(__file__))

SECRET_KEY = 'hd#x!ysy@y+^*%i+klb)o0by!bh&7nu3uhg+5r0m=$3x$a!j@9'

DEBUG = True

TEMPLATE_DEBUG = True

ALLOWED_HOSTS = []

TEMPLATE_CONTEXT_PROCESSORS = (
    'django.contrib.auth.context_processors.auth',
)

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'oauth2_provider',
    'rest_framework',
    'beers',
)

MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
)

AUTHENTICATION_BACKENDS = (
    'django.contrib.auth.backends.ModelBackend',
)
ROOT_URLCONF = 'beerstash.urls'

WSGI_APPLICATION = 'beerstash.wsgi.application'

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True

STATIC_URL = '/static/'

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'oauth2_provider.ext.rest_framework.OAuth2Authentication',
    )
}

OAUTH2_PROVIDER = {
    # this is the list of available scopes
    'SCOPES': {'read': 'Read scope', 'write': 'Write scope'}
}

Upvotes: 21

Views: 8599

Answers (2)

Yevgeniy Shchemelev
Yevgeniy Shchemelev

Reputation: 3651

I have tried the demo you mentioned and everything was fine.

$ curl -X POST -d "grant_type=password&username=superuser&assword=123qwe" -u"xLJuHBcdgJHNuahvER9pgqSf6vcrlbkhCr75hTCZ:nv9gzOj0BMf2cdxoxsnYZuRYTK5QwpKWiZc7USuJpm11DNtSE9X6Ob9KaVTKaQqeyQZh4KF3oZS4IJ7o9n4amzfqKJnoL7a2tYQiWgtYPSQpY6VKFjEazcqSacqTx9z8" http://127.0.0.1:8000/o/token/
{"access_token": "jlLpKwzReB6maEnjuJrk2HxE4RHbiA", "token_type": "Bearer", "expires_in": 36000, "refresh_token": "DsDWz1LiSZ3bd7NVuLIp7Dkj6pbse1", "scope": "read write groups"}
$ curl -H "Authorization: Bearer jlLpKwzReB6maEnjuJrk2HxE4RHbiA" http://127.0.0.1:8000/beers/
[]

In your case, I think, you have created an application with wrong "Authorization grant type".

Use this application settings:

Name: just a name of your choice
Client Type: confidential
Authorization Grant Type: Resource owner password-based

This https://django-oauth-toolkit.readthedocs.org/en/latest/rest-framework/getting_started.html#step-3-register-an-application helped me a lot.

Here the database file I've created: https://www.dropbox.com/s/pxeyphkiy141i1l/db.sqlite3.tar.gz?dl=0

You can try it yourself. No source code changed at all. Django admin username - superuser, password - 123qwe.

Upvotes: 13

AJ Venturella
AJ Venturella

Reputation: 4922

When you use "client credentials" it doesn't set the user on the generated access token, this is the root of that you do not have permission error you are seeing.

When using the client credentials grant type, you need to set the Rest Framework permission handler to look at tokens as client credentials does not set the user on the generated token. Django OAuth Toolkit provides custom permissions for this purpose:

https://django-oauth-toolkit.readthedocs.org/en/latest/rest-framework/permissions.html

Or if your entire API is subject to the same type of permissions you can just set the permission handler globally in your settings.pyfile, for example:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'oauth2_provider.ext.rest_framework.OAuth2Authentication',
    ),

    'DEFAULT_PERMISSION_CLASSES': (
        'oauth2_provider.ext.rest_framework.TokenHasReadWriteScope',
    )
}

This assumes of course that you grant read write permissions at the time.

More info about scopes here:

https://django-oauth-toolkit.readthedocs.org/en/latest/settings.html

Upvotes: 4

Related Questions