Django OAuth sign up and get a token in the same view - django

I am trying to create a sign up view that can both register the user model according to the POST data, and that includes in the response both:
The created user data (i.e. {'username': 'foo.bar', 'email': 'test#test.io'})
The token data (i.e. {'expires_in': 36000, 'access_token': 'foo', 'scope': 'read write', 'token_type': 'Bearer', 'refresh_token': 'bar'})
So they don't have to login after signing up. I am trying to do so with this views.py:
import json
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
import requests
from rest_framework import permissions, status
from rest_framework.response import Response
from rest_framework.views import APIView
from accounts.serializers import UserSerializer, UserProfileSerializer
class UserProfile(APIView):
permission_classes = (permissions.AllowAny,)
def get(self, request, format=None):
return Response()
def post(self, request, format=None):
user_profile = UserProfileSerializer(data=request.data) # data
if user_profile.is_valid():
user_profile.save() # returns User instance
# ask for a token
r = requests.post('http://myapp.com/auth/token',
data = {'client_id': '',
'client_secret': '',
'grant_type': 'password',
'username': user_profile.user.username,
'password': request.data['password']})
response_dict = json.loads(r.text)
response_dict['user_profile'] = user_profile.data
return Response(response_dict, status=status.HTTP_201_CREATED)
else:
return Response(user_profile._errors, status=status.HTTP_400_BAD_REQUEST)
however I don't know how am I supposed to get my provider's client_id and client_secret (I created my oauth2_provider application).
Am I supposed to get it as in my_app = oauth2_provider.models.Application.objects.all()[0] and then get them as my_app.client_id and my_app.client_secret? Is there a nicer way to do so?
Plus, has this any security flaws?
Thank you!

Related

Django - BaseSerializer.is_valid() takes 1 positional argument but 2 were given

This project was working before, but then Heroku took away free tier so I have been trying to deploy somewhere else, but now all of a sudden I cannot even create a user locally even though I could before... Now when I create a user I get the error mentioned in the title.
serializers folder
common.py file
from xml.dom import ValidationErr
from rest_framework import serializers
from django.contrib.auth import get_user_model, password_validation
from django.contrib.auth.hashers import make_password
User = get_user_model()
class UserSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True)
confirm_password = serializers.CharField(write_only=True)
def validate(self, data):
password = data.pop('password')
confirm_password = data.pop('confirm_password')
if password != confirm_password:
raise ValidationErr({ 'confirm_password': 'Does not match the password'})
password_validation.validate_password(password)
data['password'] = make_password(password)
return data
class Meta:
model = User
fields = ('id', 'username', 'email', 'password', 'confirm_password')
views.py file
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.exceptions import PermissionDenied
from django.contrib.auth import get_user_model
import jwt
User = get_user_model()
from datetime import datetime, timedelta
from jwt_auth.serializers.common import UserSerializer
from django.conf import settings
class RegisterView(APIView):
def post (self, request):
create_user = UserSerializer(data=request.data)
try:
create_user.is_valid(True)
create_user.save()
return Response(create_user.data, status=status.HTTP_201_CREATED)
except Exception as e:
print(str(e))
return Response(e.__dict__ if e.__dict__ else str(e), status=status.HTTP_422_UNPROCESSABLE_ENTITY)
class LoginView(APIView):
def post(self, request):
password = request.data.get('password')
username = request.data.get('username')
try:
user_login = User.objects.get(username=username)
except User.DoesNotExist:
raise PermissionDenied('Credentials are incorrect!')
if not user_login.check_password(password):
raise PermissionDenied('Credentials are incorrect!')
dt = datetime.now() + timedelta(days=7)
token = jwt.encode(
{
'sub': user_login.id,
'exp': int(dt.strftime('%s'))
},
settings.SECRET_KEY,
'HS256'
)
print('TOKEN ----->', token)
return Response({ 'token': token, 'message': f'Welcome back {user_login.username}' })
rest-framework branch "3.9.x" (github)
def is_valid(self, raise_exception=False):
...
rest-framework branch "master" (github)
def is_valid(self, *, raise_exception=False):
...
so "master" now: is_valid() only accepts keyword argument "raise_exception=True":
class RegisterView(APIView):
def post (self, request):
create_user = UserSerializer(data=request.data)
try:
create_user.is_valid(raise_exception=True)
....
Change it into something like this:
def post(self, request):
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
user = serializer.save()
return Response(user.data, status=status.HTTP_201_CREATED)
the rest of your code...

How does django generate unique user token

I'm a bit confused about token authentication. After a few question and attempts I've manage to create url gateway for auto-login my users but I'm managing to do that only by using user_id and passing it out in the url for example http://example.com/auth/login/?user_id=12 but I would rather do it with ?token=.
I'm using DRF example on how make custom auth token and return some more data about my user with that so after I curl to the url I'm returning
{"token":"d5d86e55fd5ddd48298b2ac72c3ed96b7e30dd86","user_id":52}
Now the problem that I'm facing is this MyUser maching query does not exists which is normal I didn't have token in my model so I've created one
token = models.CharField(max_length=125, null=True, blank=True)
so I could overcome DoesNotExist error but the error is still there.
I'm using this hack for the gateway login
from django.contrib.auth import authenticate, login
from django.core.urlresolvers import reverse
from django.views.generic import View
from django.http import HttpResponseRedirect
from business_accounts.models.my_user import MyUser
class UrlGatewayLogin(View):
def get(self, request, **kwargs):
page_group = kwargs.get('page_group')
token = request.GET.get('token')
user = MyUser.objects.get(token=token)
user.backend = 'django.contrib.auth.backends.ModelBackend'
login(request, user)
return HttpResponseRedirect(reverse('dashboard', args=(page_group, )))
Is DRF token unique per user, and how can I generate token in django and use it for my gateway?
You need to install Django-Rest-Framework and enable TokenAuthentication in the settings.
First you have to write the a serializer for your User Model, and then create an api view with the serialized data for token authentication.
For example (You can also use username instead of email):
In your users/api/serializers.py file:
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
from rest_framework.compat import authenticate
from ..models import User
class AuthTokenSerializer(serializers.Serializer):
email = serializers.EmailField(label=_("Email Address"))
password = serializers.CharField(
label=_("Password"),
style={'input_type': 'password'},
trim_whitespace=False
)
def validate(self, data):
email = data.get('email')
password = data.get('password')
if email and password:
user = authenticate(email=email, password=password)
if user:
if not user.is_active:
msg = _('User account is deactivated.')
raise serializers.ValidationError(msg)
else:
msg = _('Unable to log in with provided credentials.')
raise serializers.ValidationError(msg)
else:
msg = _('Must include "email" and "password".')
raise serializers.ValidationError(msg)
data['user'] = user
return data
Then in your users/api/views.py file:
from rest_framework.response import Response
from .serializers import AuthTokenSerializer
from ..models import User
class ObtainAuthToken(APIView):
throttle_classes = ()
permission_classes = ()
parser_classes = (parsers.FormParser, parsers.MultiPartParser, parsers.JSONParser,)
renderer_classes = (renderers.JSONRenderer,)
serializer_class = AuthTokenSerializer
def post(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data,
context={'request': request})
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user)
return Response({'token': token.key, 'username':user.username})
Then in settings, make sure you have token authentication turned on as such:
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
)
}
In order to return data about the user with that token, you need to write another serializer on the User model including the fields you want to return, for instance:
class UserDetailSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = [
'username', 'email', 'hobbies', 'language',
'gender', 'birthday'
]
And write another api view such as:
class UserDetailAPIView(RetrieveAPIView):
permission_classes = [IsAuthenticated]
serializer_class = UserDetailSerializer
def get_object(self):
obj = get_object_or_404(User, username=self.request.user.username)
return obj
The key is to write serializers for your model so that the data can be transported on the Internet.

django ApiView login returns 404

I am having problems with my login API view. When trying to login I get a POST 404 but I know the url path is correct because if I put a simple GET request in the view it returns data. There are still some rough parts of my code but I thought this would work.
login.py
from django.contrib.auth import authenticate
from django.shortcuts import get_object_or_404
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from apiV1.v1.accounts.models.user import User
from apiV1.v1.accounts.serializers.user import UserSerializerLogin
# login
class LoginView(APIView):
authentication_classes = ()
permission_classes = ()
#staticmethod
def post(request):
user = get_object_or_404(User, email=request.data.get('email'))
user = authenticate(username=user.email, password=request.data.get('password'))
if user:
serializer = UserSerializerLogin(user)
return Response(serializer.data)
return Response(status=status.HTTP_400_BAD_REQUEST)
serializers.py
class UserSerializerLogin(UserSerializer):
token = serializers.SerializerMethodField()
#staticmethod
def get_token(user):
"""
Get or create token
"""
token, created = Token.objects.get_or_create(user=user)
return token.key
class Meta:
model = User
fields = ('id', 'email', 'first_name', 'last_name', 'profile', 'role', 'token')
POST
{
"email": "admin",
"password": "password"
}

How and where to generate a CSRF token for to make requests to a Django app through a REST API?

I have a Django view login that allows me to get a session for a user using POST data from an Android app. It works because I set the #csrf_exempt decorator, but I'll need the CSRF token for subsequent requests.
How can I get the CSRF token? Should it be generated on my Android app or on the Django app? Then how do I add it to my requests?
from django.contrib import auth
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser
class JSONResponse(HttpResponse):
"""
An HttpResponse that renders its content into JSON.
"""
def __init__(self, data, **kwargs):
content = JSONRenderer().render(data)
kwargs['content_type'] = 'application/json'
super(JSONResponse, self).__init__(content, **kwargs)
#csrf_exempt
def login(request, *args, **kwargs):
# Login
username = request.POST.get('username')
password = request.POST.get('password')
user = auth.authenticate(username=username, password=password)
if user is not None:
if user.is_active:
auth.login(request, user)
return JSONResponse({'success': 1, 'user_id': user.id})
return JSONResponse({'success': 0, 'msg': 'User is not active'})
return JSONResponse({'success': 0, 'msg': 'Wrong username and/or password'})
I had to add the decorator from django.views.decorators.csrf import ensure_csrf_cookie to my function to make it returns a CSRF token.
Django documentation speaks on this here: https://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ajax
An alternative is to use a framework like http://www.django-rest-framework.org/api-guide/authentication

HTTP 500 error on POST

I am following instructions from here How can I login to django using tastypie to create a UserResource that can be used to login to my django user.
However, I am running into HTTP 500 error when I run the code. I tried to debug it myself, but could not figure it out. I am not sure how I can troubleshoot the 500 error. Any thought you can give is appreciated.
Thanks!!
My code is as below:
#####api.py
from registration.views import register
from tastypie.resources import ModelResource
from tastypie.constants import ALL
from django.contrib.auth.models import User
from django.contrib.auth import authenticate, login, logout
from tastypie.http import HttpUnauthorized, HttpForbidden
from django.conf.urls.defaults import url
from tastypie.utils import trailing_slash
class UserResource(ModelResource):
class Meta:
queryset = User.objects.all()
fields = ['first_name', 'last_name', 'email']
allowed_methods = ['get', 'post']
resource_name = 'user'
def prepend_urls(self):
return [
url(r"^(?P<resource_name>%s)/login%s$" %
(self._meta.resource_name, trailing_slash()),
self.wrap_view('login'), name="api_login"),
url(r'^(?P<resource_name>%s)/logout%s$' %
(self._meta.resource_name, trailing_slash()),
self.wrap_view('logout'), name='api_logout'),
]
def login(self, request, **kwargs):
self.method_check(request, allowed=['post'])
print "reached login auth"
data = self.deserialize(request, request.raw_post_data, format=request.META.get('CONTENT_TYPE', 'application/json'))
username = data.get('username', '')
password = data.get('password', '')
print "reached login auth"
user = authenticate(username=username, password=password)
if user:
if user.is_active:
login(request, user)
return self.create_response(request, {
'success': True
})
else:
return self.create_response(request, {
'success': False,
'reason': 'disabled',
}, HttpForbidden )
else:
return self.create_response(request, {
'success': False,
'reason': 'incorrect',
}, HttpUnauthorized )
def logout(self, request, **kwargs):
self.method_check(request, allowed=['get'])
if request.user and request.user.is_authenticated():
logout(request)
return self.create_response(request, { 'success': True })
else:
return self.create_response(request, { 'success': False }, HttpUnauthorized)
########test_login.py
import requests
import json
from urllib2 import urlopen
import datetime
import simplejson
url = 'http://127.0.0.1:8000/api/user/login'
data = {'username' :'sv3#gmail.com', 'password' : 'pass'}
headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
print json.dumps(data)
r = requests.post(url, data=json.dumps(data), headers=headers)
print r
#####urls.py
from userdetails.api import UserResource
user_resource = UserResource()
urlpatterns = patterns('',
......
(r'^api/', include(user_resource.urls)),
)
When deploying via WSGI using print statement can raise IOErrors. Try to comment them out or redirect the output.
In Django, how do I allow print statements to work with Apache WSGI?
http://blog.dscpl.com.au/2009/04/wsgi-and-printing-to-standard-output.html
Append a slash
url = 'http://127.0.0.1:8000/api/user/login/'