I want to create users using django built-in "User" Model and with CreateAPIVIew mixin
here is the code :
class Register(generics.CreateAPIView):
permission_classes=[permissions.AllowAny]
def post(self,request,*args,**kwargs):
username= request.POST.get('username')
email=request.POST.get('email')
password=request.POST.get('password')
# first_name=request.POST.get('first_name')
# last_name=request.POST.get('last_name')
user=User.objects.create_user(username,email,password)
# user.first_name='mintu'
user.save()
but when I tried to submit a HTTP post request using Postman
I have been fired up with an error
called
{
"detail": "Invalid username/password."
}
so to make sure if I my database is creating users perfectly fine
I have decided to create a user manually through python shell with this command
user=User.objects.create_user('mounikesh','thota#gmail.com','test1234')
Successfully a user has been created
so I came to a conclusion that my database is fine and its accepting the request now the issue is only with drf's CreateAPIView
and now I tried to figure if the error is caused because of the data that I've been sending through postman
for that I've directly injected the username,email&password to the server
class Register(generics.CreateAPIView):
def post(self,request,*args,**kwargs):
permission_classes=[permissions.AllowAny]
#
# username= request.POST.get('username')
# email=request.POST.get('email')
# password=request.POST.get('password')
user=User.objects.create_user('ok','ok#gmail.com','test1234')
# user=User.objects.create_user(username,email,password)
user.save()
# token=Token.objects.create(user=user)
return Response("user has been created")
tried to send all my required credentials to create a User expecting my User created response instead got a Error Reponse
tried a lot of searching it in google and all other forums
But it's of no use
Any kind of help is appreciated
Related
I am using DRF with auth toolkit and it is working fine. However, I want to have a second login api so a user can log in using username and pin number. It is cos we have a USSD application and it is easier to give them a pin based login system.
Currently, I have the following URL that, when called, generates token:
url(r'^signin/', include('oauth2_provider.urls', namespace='oauth2_provider')),
For the ussd app, I want something like that but the auth2 should check pin field, defined in a separate model defined as follows:
class Members(models.Model):
pin=models.IntegerField()
user=models.ForeignKey(User)
I am a little lost where to start.
Using this answer as a base to answer this question, and Django's documentation.
I would say you'd want to create a custom authentication backend, and you'd want a custom user model with two passwords, or using a one-to-one relationship to add the additional password field, something like so:
from django.contrib.auth.models import AbstractBaseUser
class UserExtension(AbstractBaseUser):
user = models.OneToOneField(User)
...
Inheriting from the AbstractBaseUser should add a password field like the user model, (although I haven't tried this). If you prefer the custom user approach, I actually have a github repo that has a custom user app, so if you'd like to get any ideas of how to achieve this check it out.
Or have a look through the documentation.
Either way, once you've got your two passwords, you need to decide which one to use as the pin. If you're using oauth for the pin field and the web applicaiton with the password, I would probably use the standard user password for the pin login, as that way you don't need to change the oauth package to work with your new password. Then for your web application build a custom login. To do this create a custom authentication backend along the lines of:
from django.contrib.auth.models import User
from django.contrib.auth.hashers import check_password
class AuthBackend(object):
supports_object_permissions = True
supports_anonymous_user = False
supports_inactive_user = False
def get_user(self, user_id):
return User.objects.filter(pk=user_id).first()
def authenticate(self, username, password):
user = User.objects.filter(username=username).first()
if not user:
return None
# this is checking the password provided against the secondary password field
return user if check_password(password, user.userextension.password) else None
Then you need to add this authentication backend to your settings:
AUTHENTICATION_BACKENDS = ('myapp.backends.AuthBackend',)
Then create the web application login (as per the stackoverflow answer above):
from django.contrib.auth import authenticate, login
def my_login_view(request):
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
# Redirect to a success page.
else:
# Return a 'disabled account' error message
...
else:
# Return an 'invalid login' error message.
...
You should now have a custom authentication login for the web application using your password2 field, and you can use the oauth authentication to work with the standard Django password in which you're going to store the pin. Which I think is what you're trying to do?
NOTE: All of the above I haven't tested, so this may not work perfectly, but it should hopefully be able to at least point you in the right direction and give you a few ideas. If I'm understanding your problem correctly, this is the sort of approach that I would take to tackle the problem.
I want to login a user using the python-social-auth functionality for Google Plus signin in Django. When logging in from my website, everything works fine and the correct details are added to the database.
However, I want to authenticate from my Android application as well. The user logs in in the application, which then sends the access token to the django API, which handles the login process in the following code, adapted from the documentation:
#csrf_exempt
#serengeti_api_request
#psa('social:complete')
def login_social_token(request, backend):
# Ensure the token has been specified.
token = request.META.get('HTTP_ACCESSTOKEN')
if token is None:
raise SerengetiApiRequestException('Access token is missing!')
# Login the user for this session
user = request.backend.do_auth(token)
if user is None:
raise SerengetiApiRequestException('Could not authenticate user!')
login(request, user)
# Store the email address if one has been specified (e.g. Twitter)
email = request.META.get('HTTP_EMAIL')
if email is not None:
user.email = email
user.save()
# Prepare the parameters to be returned
response = dict({
'id': user.id,
'first_name': user.first_name,
'last_name': user.last_name,
'api_key': request.session.session_key,
})
# Return a 200 status code to signal success.
return HttpResponse(json.dumps(response, indent=4), status=200)
When logging in from the website, the social_auth_usersocialauth table contains:
id | provider | uid | extra_data
==========================================
10 | google-oauth2 | <myemail> | {"token_type": "Bearer", "access_token": "<token>", "expires": 3600}
However, when logging in from the application using the above function, the operation completes ok, but the entry in the table looks like this:
id | provider | uid | extra_data
=========================================
10 | google-oauth2 | <empty> | {"access_token": "", "expires": null}
Also, the auth_user table contains a username like eeed494412obfuscated48bc47dd9b instead of the Google Plus username and the email field is empty.
What am I doing wrong and how can I obtain the same functionality as I get on the website?
I would like to mention that I have implemented Facebook and Twitter authentication from the Android application, which call the above-mentioned function and store the correct details, only Google Plus is causing problems.
Just wanted to share an alternative way of doing this. This example is quite primitive and doesn't cover all cases (e.g. failed authentication). However, it should give enough insight into how OAuth2 authentication can be done.
Obtain CLIENT ID
Obtain a CLIENT ID from OAuth2 service provider (e.g. Google) and configure redirect URLs.
I assume you have already done this.
Create a login / registration link
You need to generate a login / registration link in your view. It should be something like this:
https://accounts.google.com/o/oauth2/auth?response_type=code&client_id={{CLIENT_ID}}&redirect_uri={{REDIRECT_URL}}&scope=email
Replace {{CLIENT_ID}} and {{REDIRECT_URL}} with the details you obtained in the previous step.
Create a new view
In urls.py add something like:
url(r'^oauth2/google/$', views.oauth2_google),
In your views.py create a method:
def oauth2_google(request):
# Get the code after a successful signing
# Note: this does not cover the case when authentication fails
CODE = request.GET['code']
CLIENT_ID = 'xxxxx.apps.googleusercontent.com' # Edit this
CLIENT_SECRET = 'xxxxx' # Edit this
REDIRECT_URL = 'http://localhost:8000/oauth2/google' # Edit this
if CODE is not None:
payload = {
'grant_type': 'authorization_code',
'code': CODE,
'redirect_uri': REDIRECT_URL,
'client_id': CLIENT_ID,
'client_secret': CLIENT_SECRET
}
token_details_request = requests.post('https://accounts.google.com/o/oauth2/token', data=payload)
token_details = token_details_request.json()
id_token = token_details['id_token']
access_token = token_details['access_token']
# Retrieve the unique identifier for the social media account
decoded = jwt.decode(id_token, verify=False)
oauth_identifier = decoded['sub']
# Retrieve other account details
account_details_request = requests.get('https://www.googleapis.com/plus/v1/people/me?access_token=' + access_token)
account_details = account_details_request.json()
avatar = account_details['image']['url']
# Check if the user already has an account with us
try:
profile = Profile.objects.get(oauth_identifier=oauth_identifier)
profile.avatar = avatar
profile.save()
user = profile.user
except Profile.DoesNotExist:
user = User.objects.create_user()
user.save()
profile = Profile(user=user, oauth_identifier=oauth_identifier, avatar=avatar)
profile.save()
user.backend = 'django.contrib.auth.backends.ModelBackend'
login(request, user)
return redirect('/')
You might need the following imports:
from django.shortcuts import redirect
import jwt # PyJWT==0.4.1
import requests # requests==2.5.0
import json
I have a project (not running actually) with google oauth2 authentication. I leave here my config file so it may be useful to you (I was only using oauth2 so some things may vary):
AUTHENTICATION_BACKENDS = (
'social.backends.google.GoogleOAuth2', # /google-oauth2
'django.contrib.auth.backends.ModelBackend',
)
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = 'your google oauth 2 key'
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = 'your secret google oauth 2 key'
SOCIAL_AUTH_PIPELINE = (
'social.pipeline.social_auth.social_details',
'social.pipeline.social_auth.social_uid',
'social.pipeline.social_auth.auth_allowed',
'social.pipeline.social_auth.associate_by_email',
'social.pipeline.social_auth.social_user',
'social.pipeline.user.get_username',
'social.pipeline.user.create_user',
'social.pipeline.social_auth.associate_user',
'social.pipeline.social_auth.load_extra_data',
'social.pipeline.user.user_details'
)
I attach the view also (note that I'm using django rest framework).
class ObtainAuthToken(APIView):
permission_classes = (permissions.AllowAny,)
serializer_class = AuthTokenSerializer
model = Token
# Accept backend as a parameter and 'auth' for a login / pass
def post(self, request, backend):
if backend == 'auth': # For admin purposes
serializer = self.serializer_class(data=request.DATA)
if serializer.is_valid():
token, created = Token.objects.get_or_create(user=serializer.object['user'])
return Response({'token': token.key})
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
else:
# Here we call PSA to authenticate like we would if we used PSA on server side.
user = register_by_access_token(request, backend)
# If user is active we get or create the REST token and send it back with user data
if user and user.is_active:
token, created = Token.objects.get_or_create(user=user)
return Response({'id': user.id, 'name': user.username, 'token': token.key})
else:
return Response("Bad Credentials, check the Access Token and/or the UID", status=403)
#strategy('social:complete')
def register_by_access_token(request, backend):
# This view expects an access_token GET parameter
token = request.GET.get('access_token')
backend = request.strategy.backend
user = backend.do_auth(access_token=token, backend=backend)
if user:
# login(request, user) #Only useful for web..
return user
else:
return None
and in the urls.py:
urlpatterns = patterns(
'',
url(r'^login/(?P<backend>[\w-]+)$', ObtainAuthToken.as_view(), ),
)
Sorry for attaching all this code and not providing a specific answer but more data is needed because the error can come from many sources (bad api keys, bad settings configuration, pipeline..). I hope the code helps.
I finally figured it out myself. According to this article in the Android's Google Plus documentation, I also need to request the plus.profile.emails.read scope when making the request in the Android app. Once I added this, the python-social-auth code managed to store the email properly in the uid fields. This allows it to recognize the same user whether logging in from the website or the app, which is what I needed. Here's the scopes string I use:
String scopes = "oauth2:" + Plus.SCOPE_PLUS_LOGIN + " https://www.googleapis.com/auth/plus.profile.emails.read";
However, the extra_data field still contains the values I mentioned above. I believe this is due to needing to request offline access as well, which would allow Google Plus to pass the missing fields back to python-django-auth. More details can be found here.
I've been running into the same problem. The reason why the extra_fields on your google user isn't being set is because python-social-auth calls the google server to set those things, but if you're calling Google with just an access_token, it won't be enough to get Google to return the refresh_token and all those other auth related fields. You can hack it by setting them manually, but then you'd end up using the same access and refresh tokens as the client. Google recommends that you use the client to generate a new authorization token with whatever scopes you need, and then send that auth token to the server, which then will turn it into an access and refresh token. See here for the details (it's a bit of an involved read): https://developers.google.com/identity/protocols/CrossClientAuth
If you're really committed to doing this in the scope of what python-social-auth does, I'd recommend making a custom auth backend, call it GoogleOAuth2AuthorizationCodeAuth (see here for details).
The lazier and probably easy-to-break and gross way is to post the access_token to my server to sign in as a google user (which you're doing properly, it seems), and then later, get another authorization token from the client in order to post to a separate endpoint, which I'll then handle turning into another Credentials model object that's connected to a user profile.
In DjangoRestFramework:
class GoogleAuthorizationCodeView(APIView):
def post(self, request, format=None):
credentials = flow.step2_exchange(code)
saved_creds = GoogleCredentials.objects.create(credentials=credentials)
return Response(status=status.HTTP_201_CREATED)
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)
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.
I'm trying to set up a custom backend that queries another database, for which I have created a model in the system. It uses its own rules (email instead of username, and a differently salted/hashed password) so I can't use built in authentication. I've set up a custom authentication backend like so:
class BlahBlahBackend:
def check_password():
# check password code here
return true
def authenticate(self, email=None, password=None):
import myapp.models.loginmodel
try:
person = myapp.models.loginmodel.People.objects.get(email=email)
if check_password(password, person.password):
try:
user = User.objects.get(email=email)
except User.DoesNotExist:
username=person.first_name + person.last_name
name_count = User.objects.filter(username__startswith = username).count()
if name_count:
username = '%s%s'%(username, name_count + 1)
user = User.objects.create_user(username,email)
else:
user = User.objects.create_user(username,email)
except People.DoesNotExist:
return None
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
I've added BlahBlahBackend as an authentication backend:
AUTHENTICATION_BACKENDS = ('django.contrib.auth.backends.ModelBackend',
'socialauth.auth_backends.OpenIdBackend',
'socialauth.auth_backends.TwitterBackend',
'socialauth.auth_backends.FacebookBackend',
'socialauth.auth_backends.BlahBlahBackend',
)
As you can see, I'm also using some pre-existing auth backends that are also in socialauth.
I have a submission form that points to the following view:
def blahblah_login_complete(request):
email = request.POST.get('email')
password = request.POST.get('password')
user = authenticate(email,password)
# if user is authenticated then login user
if user:
login(request, user)
else:
return HttpResponseRedirect(reverse('socialauth_login_page'))
However, when I try to login in this way, it seems like one or more of the other backends are acting as if I'm trying to log in using their method.
I read that backends are cached and so ran
Session.objects.all().delete()
to clear out the backends cache.
My main questions are:
Does the order in which items are listed in AUTHENTICATION_BACKENDS
How does the system decide/know which Backend to use? This was never made clear by any of the documentation, and I find it a bit confusing.
Is there any way to force the use of a specific authorization based on the request. In other words, if someone submits a form, is there a way to force them to use the form-login-based authentication as opposed to the login via openid or Twitter?
Update:
It works! This is very cool, thanks. I guess it just seemed like the django doc was saying "You don't have to do anything else, it just sort of works like magic" and it turns out this is absolutely the case. So long as the backend is there and the credentials are set up correctly, the authentication will work. As it turns out the real problem was a misconfiguration in the urls.py file that wasn't sending the post from the login form to the correct handler, which is why it kept trying to use another authentication method.
You're supposed to use keyword arguments to django.contrib.auth.authenticate() The names should match the names of the arguments in your backend's authenticate method. The default backend handles the names 'username' & 'password'.
Your backend can use a different name for the keyword arguments e.g.: blahblah_email and blahblah_password, and then call authenticate(blahblah_email=..., blahblah_password=...).
It's clearly described here -
django tries each backend in order
defined, if first fails to
authenticate it goes to second etc.
I believe you can load backend class dynamically and authenticate
directly through it. Look at django authenticate() function sources on how to do that.
I guess django-cas will be a good reference for you :)
And yes, the order of AUTHENTICATION_BACKENDS matters.
Django loops over the backends list and stop at the first backend that has a authenticate method accepting the credential parameters you passed to it.