Reputation: 21
Here's what I'm trying to do: I'm building a finance app. There's a web component, which is using Django templates and jQuery for AJAX requests, and a mobile client.
My goal is to use session-based authentication for the AJAX requests coming from the web app, using django-allauth for authentication, and OAUTH2 for the mobile app with django-oauth-toolkit. I'm using django-rest-framework for the endpoints.
The AJAX behaviour was working fine before I added the OAUTH middleware/authentication backend. This code from my view.py
now prompts a 401 unauthorized when accessed via AJAX call, even when the user is authenticated using django-allauth. It worked previously (and still works when accessed via curl with an access token):
@api_view(['GET'])
def portfolio(request):
"""
Get account balances, total portfolio value in CAD, and balances converted to CAD at current market rates.
"""
try:
account = request.user.account
except ObjectDoesNotExist:
return Response(status=Status.HTTP_404_NOT_FOUND)
if request.method == 'GET':
serializer = AccountSerializer(account)
return Response(serializer.data)
Here are the relevant bits of mysettings.py
:
MIDDLEWARE = [
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'oauth2_provider.middleware.OAuth2TokenMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django_otp.middleware.OTPMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
AUTHENTICATION_BACKENDS = (
'allauth.account.auth_backends.AuthenticationBackend',
'django.contrib.auth.backends.ModelBackend',
'oauth2_provider.backends.OAuth2Backend',
)
An older method I wrote that doesn't use django-rest still works fine:
@verified_account_required
def get_rates(request):
if request.method == 'POST':
buy_rates,sell_rates = utility_rates()
if request.POST.get('action') == 'buy':
data = buy_rates
else:
data = sell_rates
return JsonResponse(data)
How can I use Django-rest with both authentication types simultaneously? Or is that not possible?
I am, admittedly, quite new to Django - so there is probably something I am just not understanding correctly here.
Upvotes: 2
Views: 1531
Reputation: 1149
Yes, it is possible...
The solution is in the order of the validators in the authentication_classes, which depending on the order, the request.user was being removed in the dispatch and this meant that the session authentication did not work when the oaut authentication was first; the configuration that I make is the following, in the view:
class UserViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin,
mixins.UpdateModelMixin, mixins.DestroyModelMixin,
viewsets.GenericViewSet):
queryset = User.objects.all()
serializer_class = UserModelSerializer
authentication_classes = [SessionAuthentication, OAuth2Authentication]
permission_classes = [IsAuthenticatedOrTokenHasScope]
the request is being made as follows:
cookies = {
'sessionid': request.session.session_key
}
headers = {
'remote-user': request.user.get_username(),
'authorization': "{} {}".format(
'Bearer', request.user.oauth_access_token)
}
url = 'http://user-ms:8084/api/v1/user/'
request_data = requests.get(url=url, cookies=cookies, headers=headers)
additionally, if you want it to work with the two separate authentication types, session only or oauth only, you must install the rest_framework.authtoken app and migrate
Upvotes: 0
Reputation: 177
I am using class-based views with this set-up, with one difference in my settings:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
),
}
then in the API view, I have the following:
from rest_framework import viewsets, permissions, authentication
from oauth2_provider.contrib.rest_framework import IsAuthenticatedOrTokenHasScope, OAuth2Authentication
from . import models, serializers
class MyViewSet(viewsets.ModelViewSet):
serializer_class = serializers.MySerializer
authentication_classes = [OAuth2Authentication, authentication.SessionAuthentication]
permission_classes = [IsAuthenticatedOrTokenHasScope,]
required_scopes = ['scope',]
It all works fine.
But true, adding 'oauth2_provider.contrib.rest_framework.OAuth2Authentication'
in the DEFAULT_AUTHENTICATION_CLASSES
of the DRF settings did not work. It may be that the OAuth2 authentication backend throws a 401, since it does not find a Token in the AJAX request, and that it is not handled by DRF to give SessionAuthentication a second chance.
I hope this gives you hints to use OAuth2 in your function-based views.
Upvotes: 1