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
Related
In my frontend i'm logging into another app's api in the browser, I'm then redirected back to my app, that hits a View in my backend which gets a code from the other app's api, sends code back in a post request then receives an access token and stores it in a model associated with the current user.
My problem is that after the user gives permission to other app in the browser it redirects back to my backend view without the users token in the header so if i have permissions_classes set it wont allow user to access that view... but if i take the permissions_classes off, the view won't know who the current user is.
View #1 that prepares the other app's API url:
class getAPIAuthURL(APIView):
authentication_class = [authentication.TokenAuthentication]
permission_class = [permissions.IsAuthenticated]
def get(self, request):
scopes = 'scopes'
url = Request('GET', 'https://accounts.api.com/authorize',
params={
'scope': scopes,
'response_type': 'code',
'redirect_uri': REDIRECT_URL,
'client_id': CLIENT_ID
}
).prepare().url
return Response(url, status=status.HTTP_200_OK)
View #2 that gets data and stores it in model (this is the REDIRECT_URL from previous view):
class APICallback(APIView):
authentication_class = [authentication.TokenAuthentication]
permission_class = [permissions.IsAuthenticated]
def api_callback(request, format=None):
code = request.GET.get('code')
if not code:
return Response({'Error': 'Code not found in request'}, status=status.HTTP_400_BAD_REQUEST)
response = post('https://accounts.api.com/api/token', data={
'code': code,
}).json()
print(response)
user = request.user
access_token = response.get('access_token')
token = APITokenModel(user=user, access_token=access_token)
token.save()
return redirect('frontend')
I have other Views that make requests and it has been able to get the token to know who the user is, but when this View is called I get a 401 Unauthorized error.
How do I let Django know the token I'm receiving from the other app's api belongs to the current user?
also... when I take off permissions and authentication class from the View it returns the user as Anonymous User
First, what authentication class are you using? You should know that your TokenAuthentication class uses the Authorization header in your request to authenticate you. If that's not been passed then you should fix that.
It would be worth knowing that you don't send auth tokens as GET and should not be sent as those. Unless of course you want to write an Authentication class of your own.
EDIT
In lieu of our discuss in the comments, try this redirect...
# import the class
from django.http import HttpResponseRedirect
# now redirect
return HttpResponseRedirect(redirect_to="url", headers=dict)
my rest_framework authentication and permission classes
"DEFAULT_PERMISSION_CLASSES": [
"rest_framework.permissions.IsAuthenticated",
"rest_framework.permissions.IsAdminUser",
"rest_framework.permissions.AllowAny",
],
"DEFAULT_AUTHENTICATION_CLASSES": (
"rest_framework_simplejwt.authentication.JWTAuthentication",
"rest_framework.authentication.SessionAuthentication",
"rest_framework.authentication.BasicAuthentication"
),
login view
class UserLoginView(generics.RetrieveAPIView):
"""
View for a user to login through 1FA.
The view provides a post request that accepts a email and password.
Returns a jwt token as a response to authenticated user.
"""
throttle_scope = "login"
permission_classes = (permissions.AllowAny,)
serializer_class = UserLoginSerializer
def post(self, request):
"""
POST request to login a user.
"""
#if session key is not present then create a session for the user
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
if not request.session.session_key:
request.session.save()
return Response("logged in")
In my login view if user credentials are valid i am creating a user session if not created yet. For all other requests i need to ensure that user has a active session i.e. a session id in the cookie and csrf token to secure the application , is there method provided by rest framework to do that or i need to write my own permission classes for the views
Documentation url:
https://www.django-rest-framework.org/api-guide/authentication/#setting-the-authentication-scheme
Add this:
authentication_classes = [SessionAuthentication, BasicAuthentication]
DRF behind the scene uses authentication backend in (authentication_classes list) one after another and try to call a method authenticate with provided credentials
remove this part
if not request.session.session_key:
request.session.save()
SessionAuthentication backend does that for you
I've been trying to create a drf app and wanted to achieve a sign in view that does two things:
set's the cookies automatically
returns the url and the username of the user
the issue is specifically in the validate function inside the serializer code
views.py:
class CookieTokenObtainPairView(TokenObtainPairView):
def finalize_response(self, request, response, *args, **kwargs):
if response.data.get("refresh"):
# the cookie part works well
# the part that doesn't is in the serializer below
user = UserLoginSerializer(data=request.data)
user = user.validate(data=request.data) if user.is_valid()
response.data["user"] = user.data if user.is_valid() else user.errors
return super().finalize_response(request, response, *args, **kwargs)
serializers.py
class UserLoginSerializer(serializers.HyperlinkedModelSerializer):
password = serializers.CharField(style={"input type": "password"}, write_only=True)
#
class Meta:
model = User
fields = (
"id",
"url",
"username",
"password",
)
# read_only_fields = ("id")
def validate(self, data):
data["username"] = self["username"]
data["password"] = self["url"]
return super().validate(data)
so as you can see the validate option is trying to get the username and the url data to return it, but instead it's trying to create a new account. so maybe the validate option was not right. I researched on the drf docs but there seem to be an entirely other function called create. so I don't know how validate is not working. maybe I'm supposed to type in another function
In your validate function, you cannot access self['username'] – you can only access user data through self.instance; but, otherwise, you only can access the instance if you passed it to the serializer in a construct like:
user_serializer = UserLoginSerializer(data=request.data, instance=user_obj)
What do you need is after user login, so I recommend to you this post: Login and Register User Django Rest Framewrok; I am pretty sure you can get what you need there.
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 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.