We use kerberos for our authentication. Using
request.META['REMOTE_USER']
I am able to get the authenticated user in my views.py however when I move this to a custom middleware class inside the
def process_request(self, request):
I keep getting the following error
user = request.META['REMOTE_USER']
KeyError: 'REMOTE_USER'
How do I read the authenticated user from my middleware?
I think the problem is your middleware is executing first than the middleware that fill out that info. Try to change the order of your middleware in the settings.py file.
Related
I have a backend API, it's in django and deployed on Google Endpoint.
I have a post request that insert data to my DB.
I created a script to use this endpoint but I got this error:
{"detail":"CSRF Failed: Referer checking failed - no Referer."}
Regarding over posts I added the crsf_exempt decorator to my class but it did not change.
I try to add the decorator two ways:
class AddUser(APIView):
""" Create user and company from csv """
#method_decorator(csrf_exempt)
def post(self, request):
#method_decorator(csrf_exempt, name='dispatch')
class AddUser(APIView):
""" Create user and company from csv """
def post(self, request):
But both failed.
This is how I contact my endpoint:
resp = requests.request(
method, url,
headers={'Authorization': 'Bearer {}'.format(
open_id_connect_token)}, **kwargs)
Any ideas ?
Thanks
EDIT
So I tried to add authentication classes to my views but it appears to be a bad idea. This is being real trouble for me.
I tried to get the csrftoken doing like this:
client = requests.session()
# Retrieve the CSRF token first
client.get(url) # sets cookie
print(client.cookies)
if 'csrftoken' in client.cookies:
# Django 1.6 and up
csrftoken = client.cookies['csrftoken']
else:
# older versions
csrftoken = client.cookies
Thing is, I am using IAP to protect my API and I do not have any csrftoken cookie but I do have a something looking like this:
<RequestsCookieJar[<Cookie GCP_IAP_XSRF_NONCE_Q0sNuY-M83380ypJogZscg=1
for ...
How can I use this to make post request to my API ?
So this happened to me because I did not set any authentication_classes to my generic view.
When this option is not set Django automatically use the SessionBackend, which need the csrf token.
I fixed it by adding this to my view: authentication_classes = [ModelBackend, GoogleOAuth2]
#Kimor - Can you try doing this in your urls.py
from django.views.decorators.csrf import csrf_exempt
url('^test/$', csrf_exempt(views.TestView.as_view())),
The get and post methods defined on the APIView class just tell DRF how the actual view should behave, but the view method that the Django router expects is not actually instantiated until you call TestView.as_view().
source
Django REST Framework CSRF Failed: CSRF cookie not set
So after working on this project for a while this is what I learned regarding the CSRF using Django.
First of all, if you are using django templates, or in any cases where your back-end and front-end are running behind the same server the most common practice is to use session for authentication.
This is activated by default by DRF.
This means that in your DRF configuration if you do not explicitly set the DEFAULT_AUTHENTICATION_CLASSES option default authentication will be set to Session + BasicAuth.
In this configuration you'll need to manage the CSRF token as described in the documentation (https://docs.djangoproject.com/en/4.0/ref/csrf/).
If your back-end and front-end are separated as in my case, using CSRF is not the only solution or even the recommended one.
As in my case I use JWT behind IAP (Identity Aware Proxy, provided by google). I had to write my own authentication classes and then use it in my DRF conf:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'main.authentication_backend.custom_auth.IAPAuthentication'],
...
}
Here is explain how to write your own authentication class: https://www.django-rest-framework.org/api-guide/authentication/#custom-authentication.
I`m using the django remote user middleware, and an own middleware mysupermiddleware:
MIDDLEWARE = [
...,
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.RemoteUserMiddleware',
'my_project.middleware.mysupermiddleware',
]
When a remote user authenticates, it is available in the view. request.user shows the username.
But in my middleware, it is not available
def mysupermiddleware(get_response):
def middleware(request):
print (request.user)
request.user returns AnonymousUser
When I print request.user in my view, it returns the remote authenticated user.
When I authenticate with ModelBackend instead RemoteUserBackend, the user is available in middleware and view.
Is this intended? How do I make the remote user available in the middleware? Django version is 3.1.1
EDIT:
Found the reason: It is related to rest framework authentication: At this stackoverflow post the user #imposeren states, that
if you are using something other than rest_framework.authentication.SessionAuthentication as an authentication_class, then request.user is set outside of middlewares (somewhere during View.dispatch)
Does somebody have an idea, on how to use user in middleware regardless?
We are using django-rest-framework with django-rest-framework-jwt for authentication and it works everywhere except the django admin page at ip:port/admin/. That still wants username and password.
Is there a setting or way to bypass that so it recognizes the JWT?
Is the /admin/ page always required to use name/password? I think the built in token auth works with it.
jwt is the only auth set in the settings.py file. Session authentication is not in there anymore.
The issue is that Django isn't aware of djangorestframework-jwt, but only djangorestframework, itself. The solution that worked for me was to create a simple middleware that leveraged the auth of djangorestframework-jwt
In settings.py:
MIDDLEWARE = [
# others
'myapp.middleware.jwt_auth_middleware',
]
Then in my myapp/middleware.py
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from django.contrib.auth.models import AnonymousUser
from rest_framework import exceptions
def jwt_auth_middleware(get_response):
"""Sets the user object from a JWT header"""
def middleware(request):
try:
authenticated = JSONWebTokenAuthentication().authenticate(request)
if authenticated:
request.user = authenticated[0]
else:
request.user = AnonymousUser
except exceptions.AuthenticationFailed as err:
print(err)
request.user = AnonymousUser
response = get_response(request)
return response
return middleware
Important Note:
This is a naive approach that you shouldn't run in production so I only enable this middleware if DEBUG. If running in production, you should probably cache and lazily evaluate the user as done by the builtin django.contrib.auth module.
The problem can be not in the authentication method you use. If you customize User model, it can happen that create_superuser method doesn't update is_active flag in user instance details to True. This case django authentication backend (if you use ModelBackend) can recognize that user is not active and do not allow to authenticate. Simple check - just see what value has is_active field of the superuser you create. If it False, update it manually to True, and try to login. If it is the reason of your problem you need to override create_superuser and create_user method of UserManager class.
I have a Django REST backend, and it has a /users endpoint where I can add new users through POST method from frontend.
/users endpoint url:
http://192.168.201.211:8024/users/
In this endpoint I can view all users information and add new user, so I must avoid others entry it except Administrator. I create a superuser admin with password admin123 by python manage.py createsuperuser.
My question is, If I want to do a HTTP POST from frontend(I use Angular) I have to pass the Administrator's user name and password, admin and admin123, along with POST head information. So I let others know the user name and password who check the source code of frontend.
Is there any other way to do this Authentication without exposing Administrator's user name and password to others?
You need to create an API that handles the user creation. This is why we create backends. The user will send the API their credentials and the API will add the user to the database using the admin credentials and post request. The API's code will not be viewable. Depending on your needs, auth0 can be a good solution and save you time on user registration and login. If you make your own sign up and login be sure to hash passwords and make sure they are sent over SSL. A service like auth0 will handle all this for you if you want to focus on other parts of your project.
token auth is may what you need,i use token auth for DRF as backend and angular as frontend
Finally, I find a method to solve this problem.
Here has a very elegant way to do this, rewrite get_queryset function in my UserViewSet:
class UserViewSet(viewsets.ModelViewSet):
# permission_classes = (permissions.IsAdminUser, )
permission_classes = (permissions.AllowAny, ) # <-- change 1
# queryset = User.objects.all() # <-- change 2
serializer_class = UserSerializer
def get_queryset(self):
queryset = User.objects.filter(id=self.request.user.id)
if self.request.user.is_superuser:
queryset = User.objects.all()
return queryset
In change 1, permissions allowed anyone to access, so a new user can do a POST without any authentication.
In change 2, I only return all users when the user is superuser, just like rewrote get_queryset done.
Also need to change urls.py file to add base_name for this url like this:
router.register(r'users', UserViewSet, base_name='user')
ref, https://stackoverflow.com/a/22767325/2803344
I'm using django-rest-framework and implement very simple post CRUD API.
But the problem is UpdateView and DeleteView occurs csrf error
"detail": "CSRF Failed: CSRF token missing or incorrect."
Strange thing is CreateView doens't require csrf and works very well.
Here is my view and serializer
views.py
class PostEditAPIView(RetrieveUpdateAPIView):
"""
http://example.com/posts/1/edit
"""
queryset = Post.objects.all()
serializer_class = PostUpdateSerializer
lookup_url_kwarg = 'post_id'
serializer.py
class PostUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = [
"title",
"content",
]
I think this is enough for source code.
After clicked PUT button,
How can I deal with csrf in API?
I didn't touch any SETTINGS about Rest-framework
One of the solutions I like to use is to forcefully remove any and all kinds of CSRF checks by instructing Django to do so in the middlewares level.
To do this, create a new middleware class which contains the code to disable CSRF checks, and add the middle ware to your existing list of middlewares to run. Make sure you add your custom middleware AFTER the default authentication middleware being used by Django (Order of middlewares in settings matters).
class CSRFDisablerMiddleware(object):
def process_request(self, request):
setattr(request, '_dont_enforce_csrf_checks', True)
Beware I safely use this method since my app is protected using other means of authentication. I am not sure about how you would go about solving this problem if your project needs CSRF authentication in certain areas and not in others. The method described above will disable CSRF authentication throughout your entire Django project.