I have a situation guys, I'm using these packages in django for rest-api and authentication:
django-rest-framework (REST API provider)
django-allauth (User authentication)
django-rest-framework-jwt (Support JSON-Web-Token)
In django-allauth, I made mandatory to confirm email after sign-up (ACCOUNT_EMAIL_VERIFICATION = 'mandatory'), login with session authentication is fine and follow this option, but jwt generates token and ignore this option.
How can I handle this?
Finally I found the answer.
We need to override ObtainJSONWebToken class:
def post(self, request):
serializer = self.get_serializer(data=request.DATA)
if serializer.is_valid():
user = serializer.object.get('user') or request.user
# check if settings swith is on / then check validity
if settings.ACCOUNT_EMAIL_VERIFICATION == settings.ACCOUNT_EMAIL_VERIFICATION_MANDATORY:
email_address = user.emailaddress_set.get(email=user.email)
if not email_address.verified:
return Response(status=403, data='E-mail is not verified.')
token = serializer.object.get('token')
response_data = jwt_response_payload_handler(token, user, request)
return Response(response_data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Note: Remember, check email validity with setting switch ACCOUNT_EMAIL_VERIFICATION, because we need to keep this dynamic.
Related
I have just started to explore django. I want to know whether JWT should be verified for every view manually in the server side or is there any better way of doing it.
Login View:
class LoginUser(APIView):
permission_classes = ()
def post(self, request, *args, **kwargs):
username = request.data.get('username')
password = request.data.get('password')
user = authenticate(username=username, password=password)
if user:
payload = jwt_payload_handler(user)
return Response({
'response_code':'success',
'response_msg':'Login successfull',
'username':user.username,
'token': jwt.encode(payload, SECRET_KEY)
},status.HTTP_200_OK)
else:
return Response(
{'response_code':'error',
'response_msg':'Invalid credentials'},status.HTTP_400_BAD_REQUEST
)
Menu View (to fetch menu)
def getMenu(request):
menu_list = serializers.serialize("json", Menu.objects.all())
return HttpResponse(menu_list)
settings.py
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
),
}
My Login View returns JWT token in response. Login view works fine, but post login if I want to fetch Food Menu without sending JWT(got in response of login view), i am able to get the menu list, but ideally it should fail, saying Token is missing and should not return the response, but i am getting response.
I am curious to know, if DRF is only checking whether user is Authenticated or not and based on that if it returns response, then what is the point of JWT.
Your menu view looks like a normal django view. You have to use a DRF view
since you've only configured authentication and permission for DRF.
I am using JWT token for login authentication.
When the user logs in, it returns a token.
how can i get the user id of the logged in user along with the token?
I have tried it by using request.user. it it return AnonymousUser.
class LoginUser(viewsets.ViewSet):
serializer_class = AuthTokenSerializer
def create(self, request):
token= ObtainAuthToken().post(request)
username=User.objects.all(self.request.user)
datas={token,username}
return HttpResponse(datas)
i want to return the token as well as the user id.
try request.auth to get the token and request.user.id to get the user id
I am trying to make a rest application to communicate with my android application but it is stopping me the validation of the password.
I use the User model by default of django and I would like to try to make the server validate the password
I found some other interesting answers but the truth is that django is not my strong point (my specialty is android) and they do not explain well how to implement them in my view
restapp/views.py
class postRegister(APIView):
def post(self,request):
data = JSONParser().parse(request)
cencripM=CriptoMovil(KEY_ENC_M)
data['username'] = cencripM.decrypt(data['username'])
data['email'] = cencripM.decrypt(data['email'])
data['password'] = cencripM.decrypt(data['password'])
serializer = RegistSerializer(data=data)
if serializer.is_valid():
serializer.save()
return Response({"message":"save","state":"1"})
return Response({"message":serializer.errors,"state":"2"})
maybe it helps some of the articles that I found but I did not understand how to implement them in the view (I repeat my specialty is android)
many options but I did not know how to implement
interesting but I did not understand how to implement the view
As beginning you don't need to write your customer serializer for validation instead you can follow token base authentication to validate in android as below:
urls.py
from rest_framework.authtoken.views import ObtainAuthToken
urlpatterns +=[
url(r'^api-token-auth/', ObtainAuthToken.as_view(), name='get_auth_token')
]
Now you can post username and password at /api-token-auth/ and if it is valid you will get a token in response and response status will be 200 OK
if you need to customise response then you need to override post method of
ObtainAuthToken as below:
class CustomAuthentication(ObtainAuthToken):
def post(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data, context={'request': request}) # this will use DRF's AuthTokenSerializer and pass your request to it
serializer.is_valid(raise_exception=True) # validate serializer
user = serializer.validated_data['user'] # you will get user instance if it is valid
token, created = Token.objects.get_or_create(user=user) # gives you token for user
response_data = {'token': token.key} # create dict with token key
# you can add any other JSON serializable details you want to add in this dict like username or related role/email
return Response(response_data)
Now in urls.py instead of using ObtainAuthToken.as_view() you need to use
CustomAuthentication.as_view() and
For other setup detail read this thread
I'm developing an API backend using the Django Rest Framework. I had initially developed it using Session Authentication, unaware that it could not be used for sending to a mobile application. I encountered trouble with respect to CSRF protection while trying user login in Postman.
Now, since I have to shift to Token-based Authentication to make it work, how do I go about doing so? I would like to how to implement it quickly. I have browsed through tutorials and answers on stackoverflow, but am unable to implement this in practise
Also, is Token Authentication the most suitable method for authentication? Should I use the default provided DRF module or JWT or some other implementation? Could I use token authentication simply for user login, and session authentication for the other 3 APIs?
class UserLogin(APIView):
queryset = User.objects.all()
serializer_class = UserSerializer
def post(self, request, format='json'):
username = request.POST.get('username')
email = request.POST.get('email')
password = request.POST.get('password')
user = EmailBackend.authenticate(self,username = email, password = password)
if user:
id = user.id
return Response(id, status=status.HTTP_201_CREATED)
else:
return Response("Failure", status=HTTP_400_BAD_REQUEST)
class UserRegistration(APIView):
queryset = User.objects.all()
serializer_class = UserSerializer
def post(self, request, format='json'):
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
user = serializer.save()
if user:
return Response('Success', status=status.HTTP_201_CREATED)
else:
return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)
class RecommendationQuestions(generics.ListCreateAPIView):
def post(self, request, format = 'json'):
"""Save the post data when logging in."""
uid = request.data['user_id']
resp_list = MovieSerializer.provide_movie_choices(uid)
return Response(resp_list, status=status.HTTP_400_BAD_REQUEST)
class RecommendationGenerator(generics.ListCreateAPIView):
queryset = Ratings.objects.all()#.filter(id__in=(1,2))
serializer_class= RatingsSerializer#(queryset,many=True)
def post(self, request, format='json'):
many = isinstance(request.data, list)
serializer = RatingsSerializer(data = request.data, many = many)
x = 0
if serializer.is_valid():
uid = [d['userId'] for d in serializer.data]
resp_list = RatingsSerializer.generate_recommendations(self, uid[0])
return Response(resp_list, status=status.HTTP_201_CREATED)
else:
return Response(serializer.errors,status=status.HTTP_400_BAD_REQUEST)
This is the views.py for the APIs.
Token authentication setup
You enable TokenAuthentication by including
'rest_framework.authtoken'
in INSTALLED_APPS settings (documentation).
You must run migrate after that. After you run migration, you need to create tokens for your users. Here is an example code that does that:
from django.contrib.auth.models import User
from rest_framework.authtoken.models import Token
users = User.objects.all()
for user in users:
token, created = Token.objects.get_or_create(user=user)
You run this only once. Also, you need to create token for every new user. You can automate that with post_save signal:
from django.contrib.auth.models import User
from django.dispatch import receiver
from django.db.models.signals import post_save
from rest_framework.authtoken.models import Token
#receiver(post_save, sender=User)
def create_auth_token(sender, instance=None, created=False, **kwargs):
if created:
Token.objects.create(user=instance)
Additionally, you have to add configure authentication classes by including
'rest_framework.authentication.TokenAuthentication'
in your settings 'DEFAULT_AUTHENTICATION_CLASSES' (documentation)
Last thing you need to do is add url for token authentication to your urls.py:
from rest_framework.authtoken import views as drf_views
urlpatterns += [
path('api-token-auth/', drf_views.obtain_auth_token)
]
Session-based authentication is meant for logging to your API with your browser. Token-based Authentication is stateless, which means that the server doesn't store any state about the client session on the server. Read more about the difference here. If you login via Token-based authentication, you won't have a session and won't be able to access API in any other way but via token.
Authentication example
Below is a sample code for token authentication in Python with the use of requests library.
# Authentication
import requests
r = requests.post(<add your token auth url here>, data={'username': 'my_username', 'password': 'my_password'})
if r.status_code == 200:
response = r.json()
token = response['token']
print(token)
Token must be used for every other API request. It's sent via headers.
# Consume API
import requests
headers = {'Authorization': 'Token {}'.format(<your token here>)}
# Request method is either GET, POST, PUT, PATCH or DELETE
r = requests.request(method=<request method>, url=<url to api>, headers=headers)
# or you can also use
# requests.get(url=<url to api>, headers=headers) or
# requests.post(url=<url to api>, headers=headers, data=<your data>) etc.
I would recommend you to use JWT, it much more safety than what rest_framework.authtoken is provided Such as a pair of token/refresh token to set for your main token small expiration time. That reduces the chance for the token to be stolen or corrupted. Also inside your JWT token, you can store payload which is very useful in many cases.
There is a very good library for DRF which implements all aspects of using JWT with DRF and it's pretty flexible to adapt to your purposes.
http://getblimp.github.io/django-rest-framework-jwt/
Could I use token authentication simply for user login, and session authentication for the other 3 APIs?
Yes, you definitely can. Each instance of APIView has property 'authentication_classes' and you can set SessionAuthentication specifically for APIs you want.
For example:
class RecommendationQuestions(generics.ListCreateAPIView):
authentication_classes = (SessionAuthentication, )
def post(self, request, format = 'json'):
"""Save the post data when logging in."""
uid = request.data['user_id']
resp_list = MovieSerializer.provide_movie_choices(uid)
return Response(resp_list, status=status.HTTP_400_BAD_REQUEST)
Or you can use both
class RecommendationQuestions(generics.ListCreateAPIView):
authentication_classes = (SessionAuthentication, JSONWebTokenAuthentication)
def post(self, request, format = 'json'):
"""Save the post data when logging in."""
uid = request.data['user_id']
resp_list = MovieSerializer.provide_movie_choices(uid)
return Response(resp_list, status=status.HTTP_400_BAD_REQUEST)
I found this snippet of code that helps me authenticate a user and then create a rest_framework token for them. The client I am using is a native android app and I will get the access token from the client side and post it to django in the ObtainAuth class.
Here is the code for the server side.
#psa('social:complete')
def register_by_access_token(request, backend):
backend = request.strategy.backend
# Split by spaces and get the array
auth = get_authorization_header(request).split()
if not auth or auth[0].lower() != b'token':
msg = 'No token header provided.'
return msg
if len(auth) == 1:
msg = 'Invalid token header. No credentials provided.'
return msg
access_token = auth[1]
user = backend.do_auth(access_token)
return user
class ObtainAuthToken(APIView):
model = Token
serializer_class = AuthTokenSerializer
parser_classes = (parsers.FormParser, parsers.MultiPartParser, parsers.JSONParser,)
renderer_classes = (renderers.JSONRenderer,)
def post(self,request):
serializer = self.serializer_class(data= request.DATA)
if backend == 'auth':
if serializer.is_valid:
token, created = Token.objects.get_or_create(user=serializer.object['user'])
if token:
return Response({'token': token.key})
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
else:
user = register_by_access_token(request, backend)
if user and user.is_active:
token, created = Token.objects.get_or_create(user=user)
return Response({'id': user.id, 'email': user.email, 'firstname': user.first_name, 'userRole': 'user', 'token': token.key})
The register_by_access_token method will get the facebook access token and then create a user with the rest_framework.It takes a request and the backend to be used e.g 'facebook'.
If a user logs in with my backend then the backend is 'auth' and it uses the normal process of retrieving the email and password and then giving me a token to use.As detailed here
My question is how do I post the authentication backend be it 'facebook' or 'auth' so that I can receive the token?
What I've tried.
I have tried sending the backend type ('facebook' or 'auth') with the access token but I get an error that the method takes 3 arguments and I've only provided 2.
I've tried making the url take a backend like this:
url(r'^login/(?P<backend>[^/]+)/$',views.ObtainAuthToken.as_view())
then sending the access token to a url like this mysite.com:8000/login/facebook.
None of these work and I don't have much expereience with psa or django to know how to pass this parameter.
How do I send which backend to use so that it can be accepted by the method? If anyone has ever had this use case please help me out.
according to my understanding social login requires a access token , so when you are login with facebook when you call 'mysite.com:8000/login/facebook' it is expecting a access token,
for my project i defined my urls as 'url(r'^login/(?P[^/]+)/$',register_by_access_token,name='register_by_access_token')',with the token i am sending it completes the login, for facebook i send backend as 'facebook' for google i send backend as 'google-oauth2' but both the case i am sending a token given my respective backend, when you are not using a third party backend you wont get the token and the login expects that.
so my suggestion is if you are going with auth use normal login post, not the same URL.