Get user from multiple auth backend - python

Django 2.2, python 3.6
I'm using 2 authentication backends in my application :
GRAPHENE = {
'SCHEMA': 'my_main_app.all_schemas.schema',
'MIDDLEWARE': [
'graphql_jwt.middleware.JSONWebTokenMiddleware',
],
}
AUTHENTICATION_BACKENDS = [
'graphql_jwt.backends.JSONWebTokenBackend',
'django.contrib.auth.backends.ModelBackend',
]
I can get user info from graphql queries method like :
#login_required
def resolve_curr_user(self, info, parent_id):
user = info.context.user
But I cannot get user from a view :
def curr_user(request):
request.user # user is None
I'm passing the correct headers to the view request :
const headers = new Headers();
headers.append('Authorization', `JWT ${auth_token}`);
const init = {
method: 'GET',
headers: headers,
mode: 'cors',
cache: 'default'
};
const response = await fetch(`/curr_user_route`, init);
When authenticating for graphql query, django calls authenticate method from JSONWebTokenBackend class.
How do I tell django to call the same method for regular views ?

Related

CSRF verification failed. Request aborted in Django rest framework sending the request from flutter

I've followed everything mentioned in both documentation of Django rest-framework and Flutter http but still getting the error ..here is my code :
Django
Settings
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication',
]
}
View
#csrf_exempt
#permission_classes(["isAuthenticated"])
#api_view(['POST'])
def chanage_image(request):
data = {}
if request.method == "POST":
token = request.META['HTTP_AUTHORIZATION'][6:]
lang = request.META['HTTP_LANG']
image = request.data['image']
main_user = Token.objects.get(key=token).user
app_user = AppUser.objects.get(main_user=main_user)
format, imgstr = image.split(';base64,')
ext = format.split('/')[-1]
data = ContentFile(base64.b64decode(imgstr), name='temp.' + ext) # You can save this as file instance.
app_user.image = data
app_user.save()
data = {"success": True, "details": AppUserSerializer(
app_user).data, "message": "Image changed" if lang == "en" else "تم تغيير الصورة"}
return Response(data, headers=get_headers())
URLS
path('chanage_image/', chanage_image,name="chanage_image"),
Flutter
Request
Map<String, dynamic> body = {
"image": base64Image,
};
Future<UserModel> changePlayerImage(Map<String, dynamic> body) async {
return await httpClient.post('api/user/change-image',
body: body,
headers: {'referer': 'https://www.l-dawri.com/'}).then((response) {
print(response.body);
return UserModel.fromJson(response.body);
});
}
but still in the end am always getting this error :
CSRF verification failed. Request aborted.
You are seeing this message because this site requires a CSRF cookie when submitting forms.
First you don't sent authorization token into header request while use from drf TokenAuthentication
Also into drf is better you use from class view api(like inheritance from APIView) replace def view's

How do I pass a django auth token to view or serializer?

I am trying to work with a Token provided by 'rest_framework.authentication.TokenAuthentication',
I would like to either work with the token from the view or from the serializer. What method do I use to make a token viewable inside of my application? Here is what i have tried only the current user returns. The 'auth' key returns none.
View
class ReadProductView(generics.ListAPIView):
permission_classes = [IsAuthenticated]
def get(self, request, format=None):
content = {
'auth': str(request.auth),
'user': str(request.user),
}
return Response(content)
Settings...
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication', #Token authentication
],
}
I figured it out. I needed to add a library to my view to get access to the token.
the library:
from rest_framework.authtoken.models import Token
and I grabbed the token with a get method inside my class based view.
def get(self, request, format=None):
content = {
'auth': str(Token.objects.get()),
'user': str(request.user),
}
return Response(content)

Django REST framework TokenAuthentication returns anonymous user

How do I properly implement DRF TokenAuthentication without the request object returning an anonymous user when I try to log in?
according to the docs, when authenticated, the TokenAuthentication object provides the request.user which is the Django user instance and the request.auth which is the token instance. But even after authentication, request.user returns anonymouse user.
What could I be doing wrong?
Client request:
//function to get token
export default function axiosConfig() {
// request header
const headers = {
"Content-Type": "application/json"
}
// Get token from local storage. Token is stored when user registers.
const token = localStorage.getItem("token");
if (token) headers["Authorisation"] = `Token ${token}`;
return headers;
}
Redux action
import axiosConfig from "../../utils/axiosConfig";
const config = axiosConfig
export const login = (email, password) => (dispatch, getState) => {
const body = { email, password };
// Change to absoulte path when deploying to production
axios
.post("http://localhost:8000/api/auth/login", body, config())
.then((res) => {
dispatch({
type: SIGN_IN_SUCCESFUL,
payload: res.data,
});
console.log(res);
})
.catch((err) => {
dispatch({
type: SIGN_IN_FAIL,
payload: err.response,
});
console.log(err.response.data, err.response.status);
});
};
Django
url:
from django.urls import path
from authentication.views import RegisterationView
from authentication.views import LoginView
from authentication.views import LogoutView
urlpatterns = [
path("auth/register", RegisterationView.as_view()),
path("auth/login", LoginView.as_view()),
path("auth/logout/<int:id>", LogoutView.as_view()),
]
Serializer:
The LoginResponseSerializer is used to provide response data to the client
class LoginSerializer(serializers.Serializer):
"""Login serializer"""
username = serializers.CharField()
password = serializers.CharField(required=True)
class LoginResponseSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = [
"id",
"username",
"first_name",
"last_name",
"email",
"is_active",
"is_staff",
]
read_only_fields = ["id", "is_active", "is_staff"]
View:
class LoginView(APIView):
"""Login View"""
permision_classs = [permissions.AllowAny]
def post(self, request):
serializer = LoginSerializer(data=request.data)
if serializer.is_valid():
print(serializer.data) # Data is present
user = authenticate(request, **serializer.data) # Valid credentials. User object is returned.
response_serializer = LoginResponseSerializer(user)
if user is not None and login(request, user):
print(request.user) # User is anonymous
token, created_token = Token.objects.get_or_create(user_id=user.id)
if isinstance(created_token, Token):
token = created_token
return Response(
{
"user": response_serializer.data,
"status": {
"message": "user authenticated",
"code": status.HTTP_200_OK,
},
"token": token.key,
}
)
raise serializers.ValidationError(
"Invalid Username or Password. Please try again"
)
return Response(
{"error": serializer.errors, "status": status.HTTP_403_FORBIDDEN}
)
Since you are using Token authentication, your users will be authenticated with the token in the header, for each request.
Django login() is useful in case of SessionAuthentication. Where user is stored in the session object in django, identified by the session cookie.
In your view, you don't have to call the login method. Just return the token and whatever extra information you want. And make sure you are sending this token in every request to authenticate this user.
EDIT:
And the clarification about the request.user in the documentation of DRF, is about accessing the authenticated user in the other view where you provide token in headers.

"CSRF token missing" with PUT/DELETE meethod rest-framework

I'm using using django rest framework browsable api with ModelViewSet to do CRUD actions and want to use permissions.IsAuthenticatedOrReadOnly, but when I'm logged and try to DELETE or PUT I get
"detail": "CSRF Failed: CSRF token missing or incorrect."
My view looks like this
class objViewSet(viewsets.ModelViewSet):
queryset = obj.objects.all()
serializer_class = objSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
Settings.py
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.AllowAny',
),
Serializer is just
class ObjSerializer(serializers.ModelSerializer):
class Meta:
model = Obj
Although when I delete permission_classes (so the default allowAny triggers) I can it works just fine.
What I want
To be able to PUT/DELETE only when I'm authenticated. I don't know how to send CSRF token, when all happens automatically (modalviewset does the whole work)
In your REST_FRAMEWORK settings you haven't mentioned the Authentication scheme so DRF uses the default authentication scheme which is SessionAuthentication. This scheme makes it mandatory for you to put csrf token with your requests. You can overcome this by:
To make this setting for the whole project in settings.py add
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
)}
To make this setting in specific view do the following in your view
class objViewSet(viewsets.ModelViewSet):
queryset = obj.objects.all()
serializer_class = objSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
authentication_classes = (BasicAuthentication,)
source: http://www.django-rest-framework.org/api-guide/authentication/#sessionauthentication
BTW, csrf token is saved as a cookie called 'csrftoken'. You can retrieve it from HTTP Response and attach it to your request header with the key 'X-CSRFToken'. You can see some details of this on: https://docs.djangoproject.com/en/dev/ref/csrf/#ajax
You might have used SessionAuthentication and session auth checks for the csrf_token always and you can avoid the checks by exempting but Lets not do that.
I think you can keep both Authentication classes. or just TokenAuthentication. ie,
'DEFAULT_AUTHENTICATION_CLASSES': (
# 'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
# 'oauth2_provider.ext.rest_framework.OAuth2Authentication',
# 'rest_framework_social_oauth2.authentication.SocialAuthentication',
),
And If you are not going for TokenAuth and just the session Auth. you can always pass csrf_token via X-CSRFToken header. Or you just can go for csrf_except things. which will avoid the csrf missing issues.
This should work. Also refer the links below.
ref: https://stackoverflow.com/a/26639895/3520792
ref: https://stackoverflow.com/a/30875830/3520792
You shoud add CSRF token to your request. If you do it with JSON request you should add this in your JS code.
It adds CSRF token from user cookies.
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
You can remove CSRF check on individual urls. Try this on your urls.py:
url(r'^/my_url_to_view/', csrf_exempt(views.my_view_function), name='my_name'),

How do I disable OPTIONS method on Django Rest Framework globally?

I want to disable OPTIONS method on my API built with Django Rest Framework (DRF) globally (on all the API endpoints)
currently an OPTIONS call returns,
{
"parses": [
"application/json",
"application/x-www-form-urlencoded",
"multipart/form-data"
],
"renders": [
"application/json"
],
"name": "Login Oauth2",
"description": ""
}
which is something that I don't want someone to peek into. I want to return a blank character as github does on its API or something else.
I tried
#api_view(['POST'])
def my_method(request):
if request.method == 'OPTIONS':
return Response()
on an function based view, which returns an but inspecting the headers show,
Allow →POST, OPTIONS, OPTIONS
which has a repeated OPTIONS.
How do I achieve it? Thanks.
In your settings, add something like:
REST_FRAMEWORK = {
'DEFAULT_METADATA_CLASS': None,
}
if DEBUG:
# the default value
REST_FRAMEWORK['DEFAULT_METADATA_CLASS']: 'rest_framework.metadata.SimpleMetadata'
You can implement your own metadata class as well. Setting this to None will make it return HTTP 405 on OPTIONS requests.
Just implement a custom permission class.
your_app/permissions.py (permissions file for your app)
from rest_framework import permissions
class DisableOptionsPermission(permissions.BasePermission):
"""
Global permission to disallow all requests for method OPTIONS.
"""
def has_permission(self, request, view):
if request.method == 'OPTIONS':
return False
return True
Also set this as the default permission globally, using the DEFAULT_PERMISSION_CLASSES setting.
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'your_app_name.permissions.DisableOptionsPermission',
)
}
This will disallow all incoming requests for OPTIONS method.

Categories

Resources