Django Restframework _ custom authentication - python

I wrote some code for custom authentication with this structure:
serializers.py
class LoginSerializer(serializers.Serializer):
first_token = serializers.CharField()
phonenumber = serializers.CharField()
token = serializers.CharField(max_length=255, read_only=True)
views .py
class LoginView(APIView):
serializer_class = LoginSerializer
permission_classes = (AllowAny,)
def post(self, request, format=None):
phonenumber = request.data.get('phonenumber', None)
first_token = request.data.get('first_token', None)
try:
x = User.objects.get(phonenumber=phonenumber)
except x.DoesNotExist:
return Response('user does not exists')
if first_token == x.first_token.token:
user = authenticate(phonenumber=phonenumber)
login_user = login(request, user)
user_info = {
'phonenumber': user.phonenumber,
'username': user.username,
'token': user.token,
'is_admin':user.is_admin,
}
return Response(user_info, status=status.HTTP_200_OK)
urls.py
urlpatterns = [
re_path(r'^login/$', views.LoginView.as_view(), name='login'),
]
so, authentication and login is successful and user logs in. but when i try to go another page testframework doesnt store the authentication. I made a custom authentication already .
auth.py
class PhoneAuthentication(authentication.BaseAuthentication):
authentication_header_prefix = 'Token'
def authenticate(self, request):
request.user = None
auth_header = authentication.get_authorization_header(request).split()
auth_header_prefix = self.authentication_header_prefix.lower()
if not auth_header:
return None
if len(auth_header) == 1:
return None
elif len(auth_header) > 2:
return None
prefix = auth_header[0].decode('utf-8')
token = auth_header[1].decode('utf-8')
if prefix.lower() != auth_header_prefix:
return None
return self._authenticate_credentials(request, token)
def _authenticate_credentials(self, request, token):
try:
payload = jwt.decode(token, settings.SECRET_KEY)
except:
raise exceptions.AuthenticationFailed("invalid authentication . could not decode token")
try:
user = User.objects.get(pk=payload['id'])
except User.DoesNotExist:
raise exceptions.AuthenticationFailed('No such user')
return(user, token)

I think the best way to do it is to subscribe the view ObtainAuthToken that is default of DRF.

Related

how to create custom login view with Django rest_auth?

I'm working on creating a custom login API with phon number using django-rest-auth package. I am just using rest_auth.views.LoginView in my code to generate token for token authentication.
this is my serializer:
class LoginUserSerializer(serializers.Serializer):
phone = serializers.CharField()
password = serializers.CharField(
style={'input_type': 'password'}, trim_whitespace=False)
def validate(self, attrs):
phone = attrs.get('phone')
password = attrs.get('password')
if phone and password:
if User.objects.filter(phone=phone).exists():
user = authenticate(request=self.context.get('request'),
phone=phone, password=password)
else:
msg = {'detail': 'Phone number is not registered.',
'register': False}
raise serializers.ValidationError(msg)
if not user:
msg = {
'detail': 'Unable to log in with provided credentials.', 'register': True}
raise serializers.ValidationError(msg, code='authorization')
else:
msg = 'Must include "username" and "password".'
raise serializers.ValidationError(msg, code='authorization')
attrs['user'] = user
return attrs
and this is my view:
from rest_auth.views import LoginView as RestLoginView
class Login(RestLoginView):
permission_classes = (permissions.AllowAny,)
def post(self, request, *args, **kwargs):
serializer = LoginUserSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
login(request, user)
return super().post(request, format=None)
when I run the server I have this page and I don't want to have username and email fields. instead of these, I want phone number. how do I can fix this????
in the settings.py i forgot to add:
REST_AUTH_SERIALIZERS = {
'LOGIN_SERIALIZER': 'Accounts.serializers.LoginUserSerializer',
}
this way it works too..
i use phone_number from my CustomUserModel and otp code to login;
this is my serializer:
class UserLoginSerializer(serializers.Serializer):
phone_number = serializers.CharField(max_length=11, required=True)
password = serializers.CharField(required=False, allow_null=True, write_only=True)
otp = serializers.CharField(required=False, allow_null=True)
def validate(self, attrs):
phone_number = attrs.get('phone_number')
user = User.objects.filter(phone_number=phone_number).exists()
if user:
return attrs
else:
msg = {
'detail': 'User does not exists.', 'register': True}
raise serializers.ValidationError(msg, code='authorization')
class UserLogoutSerializer(serializers.Serializer):
phone_number = serializers.CharField(max_length=11, required=True)
def validate(self, attrs):
phone_number = attrs.get('phone_number')
user = User.objects.filter(phone_number=phone_number).exists()
if user:
return attrs
else:
msg = {
'detail': 'User does not exists.', 'register': True}
raise serializers.ValidationError(msg, code='authorization')
this is my view:
class UserLoginView(APIView):
def post(self, request):
ser_data =UserLoginSerializer(data=request.POST)
if ser_data.is_valid():
data = ser_data.validated_data
user = User.objects.get(phone_number=data['phone_number'])
if user:
if user.check_password(data['password']) or (data['otp'] == user.otp):
login(request, user)
user.otp = None
user.save()
return Response(ser_data.data, status=status.HTTP_200_OK)
return Response({'detail': 'inter password or otp'})
return Response({'detail': 'user does not exists'})
return Response(ser_data.errors, status=status.HTTP_400_BAD_REQUEST)
class UserLogoutView(APIView):
def post(self, request):
ser_data =UserLogoutSerializer(data=request.POST)
if ser_data.is_valid():
data = ser_data.validated_data
user = User.objects.get(phone_number=data['phone_number'])
if user:
logout(request)
return Response(ser_data.data, status=status.HTTP_200_OK)
return Response({'detail': 'user does not exists'})
return Response(ser_data.errors, status=status.HTTP_400_BAD_REQUEST)
if you got csrf_token error , reference to this page:https://stackoverflow.com/a/52782448/18089125

Django API Testing : csrf exempt

I'm getting a "Forbidden (CSRF cookie not set.): /user/admin/sign-up" error whenever I test class based views. When I change those views to functional based views with #csrf_exempt on top of the function declaration, it works.
Postman POST Request:
localhost:8000/admin/sign-up
body : {'email' : 'email#gmail.com', 'password' : 123123}
URL path patterns:
...
path(‘/admin/sign-up’, views.AdminSignUpView),
...
Views.py
#csrf_exempt
def token_verification(request,**kwargs):
if request.method == “POST”:
id = kwargs.get(‘id’)
token = kwargs.get(‘token’)
user = User.objects.get(id = id)
redirect_url = ‘/eval/intro’
is_valid = user_activation_token.check_token(user,token)
if is_valid:
user.is_active = True
user.save()
return HttpResponseRedirect(redirect_url,status = 200)
else:
return HttpResponse(status = 403)
class AdminSignInView(View):
#csrf_exempt
def post(self,request):
data = json.loads(request.body)
try:
if User.objects.filter(name = data[‘email’]).exists():
user = User.objects.get(name=data[‘email’])
if bcrypt.checkpw(data[‘password’].encode(‘utf-8’),user.password.encode(‘utf-8’)):
token = jwt.encode({‘email’:data[‘email’]}, SECRET, algorithm = HASH).decode(‘utf-8’)
return JsonResponse({ ‘token’ : token }, status = 200)
return JsonResponse({ ‘message’ : ‘INVALID_USER’ }, status = 401)
return JsonResponse({ ‘message’ : ‘INVALID_USER’ }, status = 401)
except KeyError:
return JsonResponse({ ‘message’ : ‘INVALID_KEYS’ }, status = 400)
class AdminSignUpView(View):
#csrf_exempt
def post(self,request):
try:
data = json.loads(request.body)
if not User.objects.filter(email = data[‘email’]).exists:
password = bcrypt.hashpw(data[‘password’].encode(‘utf-8’),bcrypt.gensalt())
crypted = password.decode(‘utf-8’)
User.objects.create(
name = data[‘name’],
password = bcrypt,
email = data[‘email’],
auth_id = data[‘auth_id’]
)
return HttpResponse(status = 200)
except KeyError:
return JsonResponse({ ‘message’ : ‘INVALID_KEYS’ },status = 4000)
Models.py
class User(models.Model):
name = models.CharField(max_length = 50)
email = models.EmailField(max_length = 200,unique = True, blank = False)
department = models.ForeignKey('Department', on_delete = models.SET_NULL, null = True)
is_active = models.BooleanField(default=False)
question = models.ManyToManyField('eval.Question',through='UserQuestion')
auth = models.ForeignKey('Auth', on_delete = models.SET_NULL, null = True)
class Meta:
db_table = 'users'
token_verification view, which is written in function based, works fine but the last two raised an error. I think the fact that the decorator only goes on top of the function brings up this error, but I'm not sure why #csrf_exempt is necessary for some views.
I have no clue why I'm getting the csrf issue at this time.
I believe the problem is where you're adding the #csrf_exempt.
Django perform the csrf validation before it reaches post
You should check your Django version flowchart to find which method you should override on your View to add the csrf_exempt decorator.
https://docs.djangoproject.com/en/2.2/ref/class-based-views/base/#django.views.generic.base.View.setup
https://docs.djangoproject.com/en/1.8/ref/class-based-views/base/#django.views.generic.base.View
I believe the code bellow should work for django versions < 2.2
#method_decorator(csrf_exempt)
def dispatch(self, request, *args, **kwargs):
return super(AdminSignInView, self).dispatch(request, *args, **kwargs)

DRF when generating cookie sessionid

I am creating /auth/ page on my Django Rest Framework site. And I can not save session id.
Have i create this cookie or its creating automatically?
My auth view:
class AuthView(APIView):
permission_classes = (permissions.AllowAny,)
renderer_classes = [TemplateHTMLRenderer]
#csrf_exempt
def get(self, request):
return render(request, 'login.html', {})
#csrf_exempt
def post(self, request):
serializer = AuthFormSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
data = serializer.data
username = data.get('username')
password = data.get('password')
if username is None or password is None:
return Response({'error': 'Please provide both username and password'},
status=HTTP_400_BAD_REQUEST, template_name='login.html')
user = authenticate(username=username, password=password)
if not user:
return Response({'error': 'Invalid Credentials'},
status=HTTP_404_NOT_FOUND, template_name='login.html')
self.token, _ = Token.objects.get_or_create(user=user)
user_token = Token.objects.get(user_id = user.pk)
if (datetime.now() - user_token.created.replace(tzinfo=None)).days > 10:
Token.objects.update(user_id=user.pk, key=Token.generate_key(user.pk), created = datetime.now())
context = {'token': self.token.key}
# return Response(, status=HTTP_201_CREATED, template_name='')
return HttpResponse(json.dumps(context), content_type="application/json")
I forgot write login(request, user) , do not repeat my mistakes xD

I created login authentication in Rest Api

I created login authentication in Rest Api it Retrieves token key in postman. Right now I need the username of the person associated with a token key.
how to do that ?
# views.py
class LoginView(APIView):
def post(self, request):
serializer = LoginSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data["user"]
django_login(request, user)
token, created = Token.objects.get_or_create(user=user)
return Response({"token": token.key }, status=200)
# serializers.py
class LoginSerializer(serializers.Serializer):
username = serializers.CharField()
password = serializers.CharField()
def validate(self, data):
username = data.get("username", "")
password = data.get("password", "")
if username and password:
user = authenticate(username=username, password=password)
if user:
if user.is_active:
data["user"] = user
else:
msg = "User is deactivated."
raise exceptions.ValidationError(msg)
else:
msg = "Unable to login with given credentials."
raise exceptions.ValidationError(msg)
else:
msg = "Must provide username and password both."
raise exceptions.ValidationError(msg)
return data
After the login authentication the method(Authentication method) will return the user, so you can fetch the user data like this,
username = user.username
Example:
class LoginView(APIView):
def post(self, request):
serializer = LoginSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data["user"]
django_login(request, user)
token, created = Token.objects.get_or_create(user=user)
return Response({"token": token.key , "username":user.username},status=200)

how to generate rest_framework_jwt token in the login process

I'm trying to implement django-rest-framework-jwt for authentication. I did all what jwt tutorials said (adding the setting, url and curl testing). and I did successfully obtain the token using 127.0.0.1:8000/api/auth/token/ in the browsable api.
but what I don't get is how to generate the token in the login process. My guess is to generate it in the LoginSerializer maybe? (or I misunderstood the concept!)
Here's what I have:
# settings.py
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
}
# project/urls.py
url(r'^api/auth/token/', obtain_jwt_token),
login serializer and view:
class UserLoginSerializer(ModelSerializer):
token = CharField(allow_blank=True, read_only=True)
class Meta:
model = User
fields = ('username', 'password', 'token')
def validate(self, data):
username = data.get('username',None)
password = data['password']
user = authenticate(username=username, password=password)
if not user or not user.is_active:
raise ValidationError("Invalid username/password.")
# generate jwt token ??
# data['token'] = generated jwt token??
return data
class UserLoginAPIView(APIView):
permission_classes = [AllowAny]
serializer_class = UserLoginSerializer
def post(self, request, *args, **kwargs):
data = request.data
serializer = UserLoginSerializer(data=data)
if serializer.is_valid(raise_exception=True):
new_data = serializer.data
return Response(new_data, status=HTTP_200_OK)
return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)
So what do I have to do?
Here is what i did.
from rest_framework_jwt.settings import api_settings
class LoginView(APIView):
'''
Post call for user login.
'''
def post(self, request, format=None):
serializer = LoginSerializer(data=request.data)
if serializer.is_valid():
# Check if user has valid credentials and return user instance else None
user = authenticate(username=serializer.validated_data['username'],
password=serializer.validated_data['password'])
if user is not None:
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
return Response({'msg':'Login successful', 'token': token, 'is_login_success': True}, status=status.HTTP_200_OK)
else:
return Response({'msg': 'Credentials are not valid!'}, status=status.HTTP_400_BAD_REQUEST)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
But you dont have to do that since your 127.0.0.1:8000/api/auth/token/ is doing pretty exactly the same, unless you want to record something before logging in.
If you want more information how obtain_jwt_token view works, its JSONWebTokenSerializer will help you.

Categories