I am working on a project in angularjs and django 1.6
login(request, user)
print(request.user, request.user.id)
The login is done using standard login from django.contrib.auth in the LoginResource. Inside that it successfully prints user and its id.
Then after logging in i tried another api request from client side.Say there is a MessageResource model. And it has archive function.
def archive(self, request, **kwargs):
self.method_check(request, allowed=['post'])
data = self.deserialize(request, request.body,format=request.META.get('CONTENT_TYPE', 'application/json'))
arch_data = data.get('arch_list', '')
print(request.user, request.user.id)
So i tried to track the user inside this function.But
the last print line prints
AnonymousUser None
Means the user data is not stored in it. Can someone tell me what i'm doing wrong ? How can i get the login user from request?
Cause one - authentication that doesn't attach user to request
It depends of authorization in particular resource.
Let say if you are using ApiKeyAuthorization the user is fetched from credentials and assigned to the request during execution of authentication's is_authenticated method here: https://github.com/toastdriven/django-tastypie/blob/master/tastypie/authentication.py#L206
But if you are using standard Authentication, the method is_authenticated doesn't check or assign anything: https://github.com/toastdriven/django-tastypie/blob/master/tastypie/authentication.py#L47. So event if there are credentials in request they wont be checked or assigned.
Cause two - using resource extended methods doesn't provide authentication etc.
Yes in this case you have to check on your own: allowed http methods authorization, authentication, serialize request.body etc.
If your authentication assign user to request in is_authenticated method like ApiKeyAuthentication does for instance. You just add one line:
def archive(self, request, **kwargs):
self.method_check(request, allowed=['post'])
self.is_authenticated(request)
data = self.deserialize(request, request.body,format=request.META.get('CONTENT_TYPE', 'application/json'))
arch_data = data.get('arch_list', '')
print(request.user, request.user.id)
Related
I'm really stuck at this problem for a couple of days now.
While I understand, what's happening here, I don't really know the best workaround/solution for this.
Problem:
I'm trying to create a user login endpoint using Django and DRF in general.
My login API needs to support a login via password as well as login via OTP.
My LoginView looks like:
def post(self, request, **kwargs):
"""
post
Method to handle user login
:param request:
:param args:
:param kwargs:
:return:
"""
request_data = request.data
login_using_password = request_data.get('login-with-password') is True
login_using_otp = request_data.get('login-with-otp') is True
if request_data is not None:
if all((login_using_password, login_using_otp)):
raise accounts_exceptions.InvalidLoginRequestError()
if login_using_password:
return Response(self._login_with_password(request))
elif login_using_otp:
return Response(self._login_with_otp(request))
raise accounts_exceptions.InvalidLoginRequestError()
return Response(self._login_with_password(request))
Also my _login_with_password looks like:
def _login_with_password(self, request, **kwargs):
"""
_login_with_password
A utility method to handle login with password
:param request:
:return:
"""
return getattr(ObtainJSONWebToken.as_view()(request=request._request, ), 'data')
When I try to login, Django complains saying RawPostDataException You cannot access body after reading from request's data stream
I'm using JWT to authenticate requests. ObtainJSONWebToken is a view provided by DRF-JWT to obtain access tokens to authenticate requests.
What is the workaround/solution for this?
Is there a better way to support such a login requirement?
Thanks in advance!
Resolved this.
There's no concrete way to solve the problem above.
Django disallows access to request.data multiple times.
It could be done only once for the entire request lifetime.
So, this left me with two solutions:
Move my request payload to query params.
Move my request payload to url context.
I ended up using a mix and match of both.
So, basically I used request.query_params and self.context to fetch data from the request and changed my URL and request structure accordingly.
The django-rest-auth documentation discusses Facebook integration, which I am not interested in-- my concern is providing social login via Google. I have tried this for quite some time now and I'm wondering if anyone else has any documentation on how they did this...even just a rough sketch would be helpful. So far, I have not found any results for this search. I am almost there, but cannot get it to work with the Django rest framework (DRF) browsable API.
Here is what I have so far:
I started from the demo project supplied on the django-rest-auth github page and modified the social login template HTML page to only require the 'code' input, not both 'code' AND 'access_token'. When I supply a valid code (obtained by a separate request to google's auth endpoint), this works fine; the browsable API renders the usual web page with the 'key' (my application's API token for the user) in the response. Checking the django admin, everything worked- the user is logged in, email is authenticated, etc. Good so far.
The issue is that starting point of supplying the 'code'- and how I get that code from google in the first place. When I previously (successfully) used the allauth package, I could simply click on a link, which would "invisibly" perform the whole OAuth2 flow (i.e. request the code, use that code to get the access token, and use the access token to get user's google account info).
To recreate that seamless flow (i.e. NOT starting out with the code), I figured I could interrupt the OAuth2 flow and "intercept" the code returned from google, and then POST that code to the rest-auth social login API. To that end, I created a custom allauth.socialaccount.providers.oauth2.views.OAuth2CallbackView by overriding the dispatch method:
class CustomOAuth2CallbackView(OAuth2CallbackView):
def dispatch(self, request):
# gets the code correctly:
code = request.GET['code']
# rp is of type requests.methods.Response
rp = requests.post(<REST-AUTH API ENDPOINT>, data = {'code':code})
return rp
Usually, this method is called when google sends a GET request to the callback uri you initially supply to google's auth endpoint. With this override, I am able to successfully parse the code returned from google in that callback. The POST request works and has the user's key in the resp._content field. However, it ultimately fails to produce the intended view in the DRF browsable API.
Based on diving down in the callstack, I find that rest_framework.views.APIView.dispatch returns an object of type rest_framework.response.Response. However, when the requests.post method used above completes, it returns an instance of type requests.models.Response. As a result, it does not have the proper attributes and fails in the django middleware. For example, it has no acceptable_renderer attribute and no 'get' method (which is used in django.middleware.clickjacking.py). I could, conceivably, add these requirements to the requests.models.Response (rp) instance, but then this hack becomes even more of a kludge.
Thanks for any help you can provide!
https://github.com/st4lk/django-rest-social-auth
class SocialLoginSignup(SocialSessionAuthView):
"""
Mobile user social signup and login api view
args:
provider: name of the social network
access_token: auth token got from the social sites
"""
serializer_class = SocialSignUpSerializer
authentication_classes = (TokenAuthentication,)
def post(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
provider_name = serializer.validated_data['provider']
decorate_request(request, provider_name) # assign the provider class object in request
authed_user = request.user if not request.user.is_anonymous() else None
token = serializer.validated_data['access_token']
if self.oauth_v1() and request.backend.OAUTH_TOKEN_PARAMETER_NAME not in serializer.validated_data:
request_token = parse_qs(request.backend.set_unauthorized_token())
return Response(request_token)
try:
# authentication function get call from assign class object in request
user = request.backend.do_auth(token, user=authed_user)
except social_exceptions.AuthException as e:
raise exceptions.ParseError({'error':str(e)})
except social_exceptions.AuthTokenError as e:
raise exceptions.ParseError({'error': str(e)})
except social_exceptions.AuthAlreadyAssociated as e:
raise exceptions.ParseError({'error': str(e)})
except social_exceptions.AuthFailed as e:
raise exceptions.ParseError({'error':str(e)})
except social_exceptions.AuthUnknownError as e:
raise exceptions.ParseError({'error': str(e)})
except social_exceptions.WrongBackend as e:
raise exceptions.ParseError({'error':str(e)})
except Exception as e:
raise exceptions.ParseError({'error': social_message.INVALID_AUTH_TOKEN})
token, created = Token.objects.get_or_create(user=user)
return Response({'auth_token':token.key})
I am creating a mobile app where I need to use authentication. How can I achieve the following:
I need to create a user. After creating the user it needs to send Api_client and a secret as a response to the user.
I have a function to perform verification. After creating the user it needs to call the function for mobile verification.
Importantly, how can I stop a user who uses a for loop and starts adding users?
I tried this:
models.signals.post_save.connect(create_api_key, sender=User)
That created an API key but is not sending it as a response when creating the user is successful.
Here's what I understand from your question :
You want any user of your mobile app to register himself,
anonymously, as a user to your Django application.
This request must trigger a Tastypie api_key creation, and then return it.
You want to prevent this request from being spammed.
I don't understand this :
"I have a function for mobile without verification. After creating the user it needs to call the function for mobile verification."
To answer the points I get :
See this SO question regarding user registration with Tastypie How to create or register User using django-tastypie API programmatically?, notably this part :
def obj_create(self, bundle, request=None, **kwargs):
username, password = bundle.data['username'], bundle.data['password']
try:
bundle.obj = User.objects.create_user(username, '', password)
except IntegrityError:
raise BadRequest('That username already exists')
return bundle
For a complete walkthrough, check this article : http://psjinx.com/programming/2013/06/07/so-you-want-to-create-users-using-djangotastypie/
You're on the right track regarding the api_key creation, except you have to tell the api to actually send it back. You can use the regular way (it requires another request, though) :
i.e make it accessible from UserResource, as described in the article linked above, specifically :
def dehydrate(self, bundle):
bundle.data['key'] = bundle.obj.api_key.key
try:
# Don't return `raw_password` in response.
del bundle.data["raw_password"]
except KeyError:
pass
return bundle
If you want to send it right after a User's registration, don't forget to set "always_return_data" to True and add the api_key to the response.
Spam / loop registration :
You should look into your server's capabilities regarding this matter. For example, assuming you're using Nginx : http://wiki.nginx.org/NginxHttpLimitReqModule
Another option might be to use this : http://django-ratelimit-backend.readthedocs.org/en/latest/
Hope this helps !
Regards,
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.
I am relatively new with Django and it's ecosystem. I am writing REST api for our mobile client using django-tastypie. I have gone through almost all the examples on the web about how to use tastypie for creating REST interfaces. but none of them are specific to POSTing the data from client and how would you authorize a client.
I used the from tastypie.authentication.BasicAuthentication as show in the example. It opens a pop up asking username and password and works fine on the browser. But I am not sure, if it will do the same thing on mobile (to be specific, native IOS app). I am not quite getting when a user will make a request to login how this popup will be shown there on his/her mobile device if he or she is not using the browser but the native app.
I am totally lost on this, I would really appreciate your help.
You can check out source and use for example ApiKeyAuthentication.
You just have to POST username and api key to authentificate user.
It looks like usable for ios app.
Here is the part of the checking code.
def is_authenticated(self, request, **kwargs):
"""
Finds the user and checks their API key.
Should return either ``True`` if allowed, ``False`` if not or an
``HttpResponse`` if you need something custom.
"""
from django.contrib.auth.models import User
username = request.GET.get('username') or request.POST.get('username')
api_key = request.GET.get('api_key') or request.POST.get('api_key')
if not username or not api_key:
return self._unauthorized()
try:
user = User.objects.get(username=username)
except (User.DoesNotExist, User.MultipleObjectsReturned):
return self._unauthorized()
request.user = user
return self.get_key(user, api_key)
https://github.com/toastdriven/django-tastypie/blob/master/tastypie/authentication.py#L128
https://github.com/toastdriven/django-tastypie/blob/master/tastypie/authorization.py#L42
Thanks for the help.
I used similar approach mentioned by #Iurii. Here is my solution.
I wrote a class for handling the authentication and override is_authenticated method. and then I can use this class in Meta definition of tastypie resource classes.
from tastypie.authentication import BasicAuthentication
from tastypie.resources import Resource, ModelResource
# class for handling authentication
class MyAuthentication(BasicAuthentication):
def is_authenticated(self, request, **kwargs):
# put here the logic to check username and password from request object
# if the user is authenticated then return True otherwise return False
# tastypie resource class
class MyResource(ModelResource):
class Meta:
authentication = MyAuthentication()
this will ensure a request to access the resource will go through your authentication code.