The code I'm showing you below its what works for me right now. its not the most secure but does the job but i want to do it using POST method. any ideas how to change it?
I have a serializer.py class
class userLoginSerializer(serializers.ModelSerializer):
class Meta:
model = users
fields = ('nick', 'pass_field')
#api_view(['GET'])
def user_login(request,nick,pass_field):
but when i sent the 2 values nick and passfield it says that the nick already exist and returns 404 because it passes it to serializers.errors. I just need to pass the code using POST and validating if it exist and return a success JSON. The code below works but its not the best implementation.
if request.method == 'GET':
try:
users.objects.get(nick=nick,pass_field=pass_field)
json = {}
json['message'] = 'success'
return Response(json, status=status.HTTP_201_CREATED)
except users.DoesNotExist:
json = {}
json['message'] = 'error'
return Response(json, status=status.HTTP_400_BAD_REQUEST)
The models is users or User? Why don't you use the Django User model?
The class User has already a check_password method and store it with a hash algoritm: https://docs.djangoproject.com/en/dev/ref/contrib/auth/#methods
Never store a password in plain text, it's very insecure.
Using Django User model (or a class that inherits from it) you can simply check if it's valid this way:
try:
user = User.objects.get(username=nick)
if user.check_password(pass_field):
#TODO: Valid password, insert your code here
else:
#TODO: Password not valid, handle it here
pass
except User.DoesNotExist:
#TODO: Your error handler goes here
pass
Another thing you can do is inherits from ApiView and implement your code in post method: http://www.django-rest-framework.org/api-guide/views
I implemented a method for Sign In with JWT and what it does is:
Fetches the email and password that is send with the request and
converts it into a string variable
I check if the email already
exists in the custom user model i made.
If the user already
exists, i convert the object model to dictionary so that i can get
its particular password.
In that i match the password
corresponding to user model and the password that is send with the
post request.
if the email exists in the user model and the password corresponding to that user model matches the password that is sent with the post request i use the pyJWT to make the JWT with my custom data and return the response.
In all other cases the email and password don't match and i return "No Match"
Suppose the request is {"email":"xyz#gmail.com", "password":"12345" }
#api_view(['POST'])
def signin(request):
email = list(request.data.values())[0] #gets email value from post request {"email":"xyz#gmail.com", "password":"123"} -> this xyz#gmail.com
password = list(request.data.values())[1] #gets password value from post request {"email":"xyz#gmail.com", "password":"123"} -> this 123
usr = User.objects.filter(email=email).exists() #checks if email exists
if usr:
dictionary = User.objects.filter(email=email).values()[0] #converts object to dictionary for accessing data like dictionary["password"] dictionary["first_name"] etc
if usr and dictionary["password"] == password: #check if email and its corresponing password stored matches the password that is sent
branch = dictionary["branch"]
id = dictionary["id"]
encoded_jwt = jwt.encode({'email': email,}, 'secret', algorithm='HS256')
return Response({'token':encoded_jwt,'email':email,'branch':branch,'id':id})
else:
return Response({'No Match'})
return Response({'No Match'})
Related
I'm developing a website using DjangoRest and Flutter and I want to add password reset using email.
I know that django.contrib.auth has views that help with password reset (PasswordResetView, PasswordResetDoneView, etc). But as long as I see, they return HTML files as a response when you call them in Postman.
Is there any way to use the same easy-to-use views but instead of getting HTML files, get an HTTP response so it can be called by the Flutter app?
This can be handled in basically Two Steps:
1. One is to send anything like OTP or Reset Link view email
2. The second is to verify whether the OTP/link either is valid or not with a new password.
This can be achieved via simple function-based API views:
I can demonstrate the simplest form using OTP for basic understanding, as u said you are using flutter at frontend and that will be easier to manage otp instead of link
Step 1: Add a top Field into User Model.
Let's say we have field otp in the user model. Later we use it for verification purposes.
class CustomerUser(models.Model):
#...
otp = models.CharField(
max_length=6, null=True, blank=True)
# Method to Put a Random OTP in the CustomerUser table.
def save(self, *args, **kwargs):
number_list = [x for x in range(10)] # Use of list comprehension
code_items_for_otp = []
for i in range(6):
num = random.choice(number_list)
code_items_for_otp.append(num)
code_string = "".join(str(item)
for item in code_items_for_otp) # list comprehension again
# A six digit random number from the list will be saved in top field
self.otp = code_string
super().save(*args, **kwargs)
Step:2: Function to send Email with OTP on User Request
#api_view(['POST'])
def reset_request(request):
data = request.data
email = data['email']
user = CustomUser.objects.get(email=email)
if CustomUser.objects.filter(email=email).exists():
# send email with otp
send_mail(
'Subject here',
f'Here is the message with {user.otp}.',
'from#example.com',
[user.email],
fail_silently=False,
)
message = {
'detail': 'Success Message'}
return Response(message, status=status.HTTP_200_OK)
else:
message = {
'detail': 'Some Error Message'}
return Response(message, status=status.HTTP_400_BAD_REQUEST)
Last Step: Verify OTP And reset Password
#api_view(['PUT'])
def reset_password(request):
"""reset_password with email, OTP and new password"""
data = request.data
user = CustomUser.objects.get(email=data['email'])
if user.is_active:
# Check if otp is valid
if data['otp'] == user.opt:
if new_password != '':
# Change Password
user.set_password(data['password'])
user.save() # Here user otp will also be changed on save automatically
return Response('any response or you can add useful information with response as well. ')
else:
message = {
'detail': 'Password cant be empty'}
return Response(message, status=status.HTTP_400_BAD_REQUEST)
else:
message = {
'detail': 'OTP did not matched'}
return Response(message, status=status.HTTP_400_BAD_REQUEST)
else:
message = {
'detail': 'Something went wrong'}
return Response(message, status=status.HTTP_400_BAD_REQUEST)
So you can replicate it with your custom approach as well and can refactor it easily.
I have used Simple API views in these examples you can check the detailed information in DRF DOCS Requests and Response Section as well
So You don't have to use HTML at all, just can work with Response, HttpResponse whatever you prefer.
when I register a new user, his password is encrypted in the database with the sha1 method.
When I want to search if a user is or not already register in the database for my login page, I have to check if the email address exist and then if the password which is send is the good.
This is my code :
*views.py :"
#Login
#api_view(['POST', ])
def log_in(request):
if request.method == 'POST':
data = {}
email = request.POST.get('email')
password = request.POST.get('password')
password = hashlib.sha1(password.encode('utf-8'))
account = memberArea.objects.filter(email = email, password = password)
if account.exists():
for account in account:
data['succes'] = "Successfully connected"
data['id'] = account.id
data['email'] = account.email
else :
data['error'] = "email and password doesn't match !"
return Response(data)
Here, I try to encrypt the password which is send by the user and then to search into the databse for this encrypt password.
After testing it doesn't work.
Thank's by advance for helping me.
There are 3 things to unpack here.
You should never write your own password hashing method. You can be sure it's all there. A small mistake can cost you a lot.
You should absolutely not filter with password. If you want to check if password is matching, then
For setting password, User model has set_password and for checking if it's correct there's check_password
You didn't show how you register a user, but if your user model uses AbstractBaseUser then that's probably what you need to do.
Also, there's this authenticate function that does it all based on how you configured your backends in settings.
You can find it there
from django.contrib.auth import authenticate
In my app I am trying to get the User data using a get request and it works in Postman but the problem occurs when I try to send the data from FrontEnd as we cannot send Body in get Request
URL:
path('user/meta/', UserDetails.as_view()),
Views.py
class UserDetails(APIView):
"""Get basic details of user"""
def get(self, request):
username = request.data["username"]
if User.objects.filter(username = username).exists():
user = User.objects.get(username = username)
print(user)
return Response({
'verified': user.verified,
})
else:
return Response("User doesn't exists", status=status.HTTP_400_BAD_REQUEST)
How should I modify it such that I can get the data from get request?
So, your requesting URL will become /user-info/?username=john
and then, use request.GET
username = request.GET.get("username","default_value")
The good practice is to use query params in such case,
so your url will look like,
/user-info/?username=john
and you modify your code to,
username= self.request.query_params.get('username')
I know this is an old topic and the question is answered, but just to underline that the queried data, you obtained from the user in the API should go through the Serializers (https://www.django-rest-framework.org/tutorial/1-serialization/) to at least somehow protect against unwanted values. This will also avoid you directly querying the database models.
i don't want to use django default user table so i created a user table with username and hashed password. I hashed the password with make_password() when posting to database but when i tried retrieving information from the database using the same method to hash user input password, i get a different hash for same password. below is the view code for saving the user and retrieving the details.
view.py
class HMUser(APIView):
def post(self, request):
request.data['password'] = make_password(request.data['password'])
print(request.data['password'])
serialize = serializers.HMUserSerializer(data=request.data)
if serialize.is_valid():
serialize.save()
return Response(1, status=status.HTTP_201_CREATED)
return Response(serialize.errors,
status=status.HTTP_400_BAD_REQUEST)
class Login(APIView):
def get(self, request, username, password):
print("pass ", password)
userpassword = make_password(password)
print("Hash", userpassword)
user_details = models.HMUser.objects.get(username=username,
password=userpassword)
serialize = serializers.HMUserSerializer(user_details)
return Response(serialize.data)
Mismatch of salt is your problem,
Salt is seed or random number which is used in addition to a password to make it more secure, Since there is no salt specified it generates random at runtime making new value each time for the same password.
Ideal way is to use check_password() which is much more friendly,
Workaround is to specify your own salt, something like make_password(salt='mySalt',password=realPassword)
You need to ensure that salt use while creating a password and verifying are same.
I was working with a legacy database where there was a table 'tbl_personaldetails', from where i ported data to custome user model.
In code to port data from tbl_personaldetails, i use user.set_password(password) which sets password as hash in user table.
Trouble is when i try to authenticate(username=username, password=password) where password and username are plain text, authenticate returns None (Even for superuser account from which i can login in admin section).
The code to login is as follows:
class LoginView(FormView):
form_class = LoginForm
template_name = 'account/login.html'
def get_success_url(self):
return reverse("userHomeAfterLogin")
def form_valid(self, form):
email = form.cleaned_data['email'].lower().strip()
password = form.cleaned_data['password']
user = authenticate(email=email, password=password)
if user:
login(self.request, user)
return redirect(self.get_success_url())
else:
try:
user = User.objects.get(email__iexact=email)
if not check_password(password, user.password):
form._errors['password'] = ErrorList([u'That is not the correct Password.'])
except User.DoesNotExist:
form._errors['email'] = ErrorList([u'This email is not registered with us.'])
context = self.get_context_data(form=form)
return self.render_to_response(context)
As of now it flows like this:
1.authenticate returns none, landing up in else part:
2. can retrieve the user with email and check_password is correct.
3. it renders the form w/o any error message
.
what is it that i am doing wrong, everything looks fine though
As far as I understand from the code snippet, you are using email as your username. With email address, Django's authenticate will never work. It expects username instead. See code below.
def authenticate(**credentials):
"""
If the given credentials are valid, return a User object.
"""
for backend in get_backends():
try:
user = backend.authenticate(**credentials)
except TypeError:
# This backend doesn't accept these credentials as arguments. Try the next one.
continue
if user is None:
continue
# Annotate the user object with the path of the backend.
user.backend = "%s.%s" % (backend.__module__, backend.__class__.__name__)
return user
In order to use email address as the username field, please refer to http://justcramer.com/2008/08/23/logging-in-with-email-addresses-in-django/.
Hope this helps.