I am using tastypie to create an api for my Django project. My project requires that the user logins in order to make edits to models view etc. So my first step was to login the user using tastypie by creating the login and logout functions in the UserResource as described here. The response contained a sessionid cookie that i used to logout. My resources use SessionAuthentication and DjangoAuthorization. But when i try to post or put or delete on a model using a tastypie resource url, I get an error a 401 error. Some people suggested that i must use a valid csrf_token. But how can i do that when tastypie login view won't return one.
example code:
class EntryResource(ModelResource):
customer = fields.ForeignKey(CustomerResourse, 'customer', full=True)
creator = fields.ForeignKey(UserResource,'creator',null=True,full=True,blank=True)
class Meta:
queryset = Entry.objects.all()
resource_name = 'entry'
authorization = DjangoAuthorization()
authentication = SessionAuthentication()
my client.py
headers={"Content-Type":"application/json"}
#login. I don't get a csrftoken here, but works fine which is odd because i post data r
resp = requests.post(login_url, data=json.dumps(credentials), headers=headers)
cookies ={'sessionid':resp.cookies['sessionid']}
entry = {
'title':'New Entry',
'snippet':'New Snippet',
'created': datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%S'),
}
entry_json = json.dumps(entry)
entry_add_response = requests.post(entry_api_url,data=entry_json,
headers=headers,cookies=cookies)
#entry_add_response.status_code is 401
#logout works as it should
logout_resp = requests.get(logout_url, cookies=cookies)
what am i doing wrong? Am I not doing the right steps?
all my urls have ?format=json appended to them. Tried without didn't work. I also tried changing from django and session authentication and authorization to basic (Authentication(), Authorization() ). I then get a 500 error. I starting to feel a bit desperate....
Related
I have encountered a weird behavior as it works with one view but does not with another.
Django 3.2 and rest framework 3.13.1 are currently installed within the venv.
When I call the view I get the error message: CSRF validation failed, reason CSRF cookie not set.
This view uses its authentication class that extends TokenAuthentication.
class APIKeyAuthentication(authentication.TokenAuthentication):
logger = logging.getLogger(__name__)
def authenticate(self, request):
# try to get api key via X-API_KEY
api_key = request.META.get("HTTP_X_API_KEY")
# try to get api key via Authorization
if not api_key:
api_key = request.META.get("HTTP_AUTHORIZATION")
if not api_key:
return None
platform_id, api_user = self.validate_api_token(api_key)
return api_user, None
That class is used as default within rest framework:
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": (
"api.authentification.APIKeyAuthentication",
"rest_framework.authentication.BasicAuthentication",
),
"DEFAULT_PERMISSION_CLASSES": [
"rest_framework.permissions.IsAuthenticated",
],
Endpoints are:
path("v1/combinedEntries", VerifyV1OrderView.as_view()),
path("order/verify/", VerifyOrderView.as_view()),
Classes:
class VerifyV1OrderView(GenericAPIView):
serializer_class = CombinedEntryV1Serializer
authentication_classes = (APIKeyAuthentication,)
and
class VerifyOrderView(GenericAPIView):
serializer_class = CombinedEntrySerializer
authentication_classes = (APIKeyAuthentication,)
so I do not understand the error I even removed the session authentication from the config but without any change.
I am using Postman to send the requests but the error occurs also when using curl.
Has someone an idea what the problem could be?
Thanks and regards.
Matt
I am using the Django Restful API Framework together with Simple JWT and have successfully created a URL for receiving and refreshing a user token.
In order to try out the authentication using the token, I have created a view that simply lists all the posts inside the database. I have then assigned the IsAuthenticated class to the view.
As expected, I get an error message saying that the authentication credentials were not provided. I then went ahead and made a simple GET request using Postman, with the authentication token provided in the "Authorization" tab. The type was set to "Bear Token". Unfortunately, I still get the message "Authentication credentials were not provided." with a 403 Forbidden code.
I have also tried to provide the token in the Headers, as well as make CURL requests, everything to no avail.
My view looks like this:
class PostListView(generics.ListAPIView):
permission_classes = (IsAuthenticated,)
queryset = Post.objects.filter()
This is the serializer:
class PostListSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ('__all__')
The settings.py of the Django project:
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],
'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.AllowAny'],
'DEFAULT_AUTHENTICATION_CLASSES:': ('rest_framework_simplejwt.authentication.JWTAuthentication',)
}
CORS_ALLOW_ALL_ORIGINS = True # For testing purposes
I have followed several different tutorials online, read through numerous posts as well as followed the official documentation of Simple JWT.
Well what you are doing is trying to filter the data while your basic purpose is to just list your model. For filtering make sure your go through the documentation DRF Filtering.
Try these changes in your code. I hope it will work for you.
Settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_simplejwt.authentication.JWTAuthentication',
],
}
Views.py
class UserList(generics.ListAPIView):
permission_classes = (IsAuthenticated,)
queryset = Post.objects.all()
serializer_class = PostListSerializer
After this try to hit your API with access token. To learn more about generic views you can go through this link Generic Views in DRF.
Getting a csrf error I cant figure out how to fix, i have rest auth working, user is able to update their details like so:
but with Django Comments i get this csrf error using the same csrf token Error:
I would like to get rid of this error on the /comments/post/ endpoint, such that this endpoint behaves similar to /rest-auth/user/ view which accepts an "Authorization: Token 792b5fb27b4fe805e895c91274f26b6ab13cb654" header field to relevant provide data to the authenticated user.
The following is an exert of the csrf related decotaros on the respective views shown in the screen shots:
From the /comments/post/ endpoint
#csrf_protect
#require_POST
def post_comment(request, next=None, using=None):
# Fill out some initial data fields from an authenticated user, if present
data = request.POST.copy()
if request.user.is_authenticated():
if not data.get('name', ''):
data["name"] = request.user.get_full_name() or request.user.get_username()
if not data.get('email', ''):
data["email"] = request.user.email
From the /rest-auth/user/ endpoint
#api_view(['GET'])
#permission_classes((IsAuthenticated, ))
def get_user(request, **kwargs):
pk = request.data['pk']
user = MyUser.objects.get(pk=pk)
serializers = UsersSerializer(user)
return Response(serializers.data)
You're using the wrong content type. Please change it into application/json and try again.
The decorators for your endpoints are different, thus you need to adjust the headers accordingly.
For your /rest-auth/ view the WWW-Authenticate header is required as mentioned here.
The comments view /comments/ endpoint has the csrf_protect decorators which means that the header must match the csrf-token returned in the cookie,as Fede mentions in your header you only require 'X-CSRFToken' with the matching value from the cookie.
I think you are using django-rest-framework which comes with the csfr token exempt by default, but postman is sending a csfr token that is why you are getting that error.
cleaning the cookies might solve the problem.
I followed this implementation: Building a True OAuth 2.0 API with Django and Tasty Pie to bring OAuth2 Two-legged authentication to my Django app.
It works perfectly and authentication using session keys is terrific, however, I am facing some issues retrieving the user authenticated using OAuth, as request.user always returns an Anonymous user object.
I don't know whether I should include any other method in the api/authentication.py, but right now I'm using this one: https://github.com/amrox/django-tastypie-two-legged-oauth/blob/master/src/authentication.py
I guess that custom authentication is not taking care of returning the current user authenticated at all, but I'm unable to handle it myself.
This is my resource:
class UpdateResource(ModelResource):
class Meta:
resource_name = 'updates'
queryset = Update.objects.all()
allowed_methods = ['get', 'post']
authorization = DjangoAuthorization()
authentication = OAuth20Authentication()
def hydrate(self, bundle, request=None):
print bundle.request.user
Any help would be appreciated,
Thanks.
EDIT: I can retrieve correctly the user in GET requests, but POST request don't seem to go through my custom authentication, so I guess it must have something to do with Django CSRF protection. :(
I have an Adobe Air mobile application that communicates with Django via TastyPie. To use the app people have to register first. Therefore they have to supply their email and password. Afterwards they will be able to "login". I thought it would be the best idea that after entering a successful username/password combination, the api-key will be sent back to the mobile app where it will be cached, so the user is "logged in".
Please tell me if you think there is a better way for registering and "logging in" users.
Inside Django I have a UserRessource class that I use to register new users when sending data via POST:
class UserResource(ModelResource):
class Meta:
allowed_methods = ['get', 'post']
queryset = User.objects.all()
resource_name = 'auth'
authentication = Authentication()
authorization = Authorization()
fields = ['username', 'email']
def obj_create(self, bundle, request=None, **kwargs):
username, email, password = bundle.data['username'], bundle.data['password'], bundle.data['password'],
try:
bundle.obj = User.objects.create_user(username, email, password)
except IntegrityError:
raise BadRequest('That username already exists')
return bundle
That works very well.
But now I'm struggling with the actual login process. In my opinion it would be best to send username and password via GET (and https) to this ressource and if those are valid, return the users api key. But would that be possible? And is it clean? Usually TastyPie would show all users currently in the DB if you send a GET request to that ressource. But I dont need that data, so I could overwrite that somehow. I already checked http://django-tastypie.readthedocs.org/en/v0.9.9/resources.html but I don't get it to work. Is it even possible to overwrite that behaviour?
So the actual questions are Whats the best way to "sign in" a user using ApiKeyAuthentication?
And Is my approach right and clean or do you have a better method? and Do you have any examples for this case?
Thanks alot in advance!
I'm using BasicAuth so it may be slightly different. But my solution is basicaly an empty resource that requires authentication. If the authentication is a success the service returns response code 200 and the authenticated user, I override obj_get_list and stuff the authenticated user in there. If the credentials are wrong the service returns response code 401.
class LoginResource(ModelResource):
class Meta:
allowed_methods = ['get']
resource_name = 'login'
include_resource_uri = False
object_class = User
authentication = BasicAuthentication()
authorization = DjangoAuthorization()
def obj_get_list(self, bundle, **kwargs):
return [bundle.request.user]
Okay I'll try to explain my point of view on the topic:
First the UserResource example on the tastypie page for me has one significant issue:
The User Objects should not be presented at any time to the single User, they should be able to see they're own "profile" or whatever but never browse and see others. Of course that can be done and with UserResource by clearing the main "list view" of that resource and applying the APIKeyAuth to the individual profiles, but still I don't like the idea of UserResource.
Second in the form when you are developing an API(such as tastypie usage) the APIKey is the actual "password" so what should be send on request is not the username and password but the username and APIKey, which is obtained in other manners(normally an e-mail or some kind of website based UI). Than they are recommended to be send via Authorization Header and not in GET parameters.
Third when we are talking about API there is no such thing as sign-in - at least not in the RESTFULL APIs - it is in some sense connectionless, so you actually going to send the Authorization header with each request.
As to the question yes you can overwrite the data. Look at the hydrate/dehydrate cycle in the Tastypie docs to understand how does it renders the content and if you have more question go ahead and ask.