The register and login sections are functioning properly in my django application. When someone registers he receives a confirmation email, but on clicking the email confirmation link the account is not verified. I'm using try and except, it's the except that is being executed each time and try never executes.
models.py
username = models.CharField(max_length=255, unique=True, db_index=True)
email = models.EmailField(max_length=255, unique=True, db_index=True)
is_verified = models.BooleanField(default=False)
views.py
serializer_class = EmailVerificationSerializer
def get(self, request):
token = request.GET.get('token')
try:
key = jwt.decode(token, settings.SECRET_KEY)
user = User.objects.get(id=key['user_id'])
if not user.is_verified:
user.is_verified = True
user.save()
return Response({'email': 'Your email has been activated'}, status=status.HTTP_200_OK)
except jwt.exceptions.DecodeError as identifier:
return Response({'error': 'token not valid'}, status=status.HTTP_400_BAD_REQUEST)
Please I want to know why the the code in the try section never gets executed even when the token is intact and has not expired.
Try to use key = jwt.decode(token, settings.SECRET_KEY, algorithms=['HS256'])
Related
I'm trying to validate an OTP that was sent to the user when registering i have a separate model for otp which has an OneToOne relation with the user
Otp Model
class Verify(models.Model):
user = models.ForeignKey(
User, on_delete=models.CASCADE, related_name="userverify", blank=False, null=True)
otp = IntegerRangeField(
min_value=111111, max_value=999999, blank=True, null=True)
created_at = models.DateTimeField(
_("created at"), auto_now=False, auto_now_add=True, blank=False)
expires_at = models.TimeField(null=True)
the VerifySerializer: I don't specify the model because i only need it to check the validity of the input, although i think this might be the problem
class VerifySerializerBase(serializers.Serializer):
email = serializers.EmailField()
otp = serializers.CharField()
the Verify_Email view takes an email and otp value to validate the email
How I'm trying to verify the otp
class Verify_Email(APIView):
def post(self, request):
try:
data = request.data
serializer = VerifySerializerBase(data=data)
if serializer.is_valid():
email = serializer.data['email']
otp = serializer.data['otp']
verify = Verify.objects.filter(email=email, otp=otp)
user = User.objects.filter(email=email)
if not user[0].exists():
return Response({
'message': "A user with this email was not found"
}, status=status.HTTP_400_BAD_REQUEST)
elif user[0].is_active:
return Response({
'message': "This email has already been verified"
}, status=status.HTTP_400_BAD_REQUEST)
elif verify[0].exists():
if verify[0].expires_at >= datetime.now():
return Response({
'message': "This OTP has expired, please request another one"
}, status=status.HTTP_400_BAD_REQUEST)
elif verify[0].otp != otp:
return Response({
'message': "This OTP is invalid"
}, status=status.HTTP_400_BAD_REQUEST)
else:
verify[0].delete()
user[0].is_active = True
user[0].save()
return Response({
'message': "Email has been verified"
}, status=status.HTTP_200_OK)
return Response({
'message': "Something is wrong"
}, status=status.HTTP_400_BAD_REQUEST)
return Response({
'message': "Something is wrong"
}, status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
return Response(str(e), status=status.HTTP_404_NOT_FOUND, template_name=None, content_type=None)
and when testing i get
"Cannot resolve keyword 'email' into field. Choices are: created_at, expires_at, id, otp, user, user_id"
from what I understand i think it's telling me that I'm checking for fields that are not in the OTP model fields but I don't think I'm doing that??
The error relates to the models, not the serializer. This line implies that a Verify model has a field email, because you're building a query on that field:
verify = Verify.objects.filter(email=email, otp=otp)
Perhaps you meant to query by user instead, which involves swapping with the following line like so:
user = User.objects.get(email=email)
verify = Verify.objects.filter(user=user, otp=otp)
i think Daniel answer is correct but a little bit bugy
instead use
user = User.objects.get(email=email)
can use
user = User.objects.filter(email=email).first()
if user is None:
raise Exception("")
this is better because you can use your custom error handler
another problem is in your model if you have duplicate or more otp code
in this situation your queryset like
verify = Verify.objects.filter(user=user, otp=otp)
returned a list of verify objects,obviously it's not good solution.
change model like below
class Verify(models.Model):
user = models.ForeignKey(
User, on_delete=models.CASCADE,
related_name="userverify",
blank=False,
null=True
)
otp = IntegerRangeField(
min_value=111111, max_value=999999, blank=True, null=True
)
created_at = models.DateTimeField(
_("created at"), auto_now=False, auto_now_add=True, blank=False
)
expires_at = models.TimeField(null=True)
is_active = models.BooleanField(default=True)
now use
verify = Verify.objects.filter(user=user, otp=otp, is_active=True)
I am using pyrebase for calling firebase APIs in python/flask. I am able to sign in with but unable to create a new user. Here is my code for creating new user.
#app.route('/api/signup', methods=['GET', 'POST'])
def signup():
if request.method == 'POST':
email = request.form['email']
password = request.form['password']
print(email)
print(password)
if email is None or password is None:
msg = 'Error missing email or password'
try:
user = auth.create_user(
email=email,
password=password
)
print(user)
msg = f'Successfully created user {user.uid}'
except:
msg = 'There was an error creating user'
return render_template("result.html", msg=msg)
try is not executed and I got except message on screen. I have searched on google, and found nothing helpful. Why auth.create is not creating a new user. There is no user in firebase.
I have a custom User model and a function in views.py to handle the registration. when I register a user with Postman for example all the user data is stored in the database correctly but the password -field remains empty. I think that causes the problem when I try to use a simple login -page - it never accepts my credentials (because there is no password in the database). Any ideas why the password isn't saved and how to fix it?
I have a function like this in views.py when registering a new user:
def register(response):
if response.method == 'POST':
form = RegisterForm(response.POST)
if form.is_valid():
user = form.save()
user.refresh_from_db()
user.id = form.cleaned_data.get('id')
user.save()
username = form.cleaned_data.get('email')
password = form.cleaned_data.get('password')
user = authenticate(username=email, password=password)
return HttpResponse(status=201)
else:
form = RegisterForm()
return HttpResponse(status=400)
And this is my custom user model:
class UserManager(BaseUserManager):
def create_user(self, email, password):
"""
Creates and saves a User with the given email and password.
"""
if not email:
raise ValueError('A user must have a email.')
user = self.model(
email=self.normalize_email(email),
)
user.set_password(password)
user.save(using=self._db)
return user
Edit. Here is my RegisterForm:
User = get_user_model()
class RegisterForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput)
password_2 = forms.CharField(label='Confirm Password', widget=forms.PasswordInput)
class Meta:
model = User
fields = ['email', 'id', 'gender', 'height', 'weight']
def clean_email(self):
'''
Verify email is available.
'''
email = self.cleaned_data.get('email')
qs = User.objects.filter(email=email)
if qs.exists():
raise forms.ValidationError("email is taken")
return email
def clean_id(self):
'''
Verify id is available.
'''
id = self.cleaned_data.get('id')
qs = User.objects.filter(id=id)
if qs.exists():
raise forms.ValidationError("id is taken")
return id
def clean(self):
'''
Verify both passwords match.
'''
cleaned_data = super().clean()
password = cleaned_data.get("password")
password_2 = cleaned_data.get("password_2")
if password is not None and password != password_2:
self.add_error("password_2", "Your passwords must match")
return cleaned_data
And here is the model:
class CustomUser(AbstractBaseUser):
is_active = models.BooleanField(default=True)
staff = models.BooleanField(default=False) # a admin user; non super-user
admin = models.BooleanField(default=False) # a superuser
gender = models.CharField(null=True, blank=True, max_length=20)
height = models.CharField(null=True, blank=True, max_length=3)
id = models.CharField(primary_key=True, unique=True, blank=False, max_length=100)
email= models.EmailField(max_length=100, unique=True, null=False, blank=False)
weight = models.CharField(max_length=3, null=True, blank=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = [] # Email & Password are required by default.
def get_full_name(self):
# The user is identified by their email address
return self.email
def get_short_name(self):
# The user is identified by their email address
return self.email
def __str__(self):
return self.email
def has_perm(self, perm, obj=None):
"Does the user have a specific permission?"
# Simplest possible answer: Yes, always
return True
def has_module_perms(self, app_label):
"Does the user have permissions to view the app `app_label`?"
# Simplest possible answer: Yes, always
return True
#property
def is_staff(self):
"Is the user a member of staff?"
return self.staff
#property
def is_admin(self):
"Is the user a admin member?"
return self.admin
objects = UserManager()
And soon after editing my question I noticed I was missing the save function from the RegisterForm class. Adding this solved the problem:
def save(self, commit=True):
user = super().save(commit=False)
user.set_password(self.cleaned_data["password"])
if commit:
user.save()
return user
i'm trying to generated token after login using drf. i'm using emailbackend for login with email and password but its not working with user created by api and with user created by admin its working
backends.py:
class EmailBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
UserModel = get_user_model()
try:
user = UserModel.objects.get(email=username)
except UserModel.DoesNotExist:
return None
else:
if user.check_password(password):
return user
return None
Token serializers:
class AuthCustomTokenSerializer(serializers.Serializer):
'''
Changing Token auth to use email instead username
'''
email = serializers.EmailField(label=_("Email"))
password = serializers.CharField(
label=_("Password",),
style={'input_type': 'password'},
trim_whitespace=False
)
def validate(self, attrs):
email = attrs.get('email')
password = attrs.get('password')
print(email, password)
if email and password:
user = authenticate(username=email, password=password)
print("this is user", user)
# The authenticate call simply returns None for is_active=False
# users. (Assuming the default ModelBackend authentication
# backend.)
if not user:
msg = _('Unable to log in with provided credentials.')
raise serializers.ValidationError(msg, code='authorization')
else:
msg = _('Must include "username" and "password".')
raise serializers.ValidationError(msg, code='authorization')
attrs['user'] = user
return attrs
login view:
#csrf_exempt
#api_view(["POST"])
#permission_classes((AllowAny,))
def login(request):
serializer = AuthCustomTokenSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
token, _ = Token.objects.get_or_create(user=user)
return Response({token: token.key}, status=status.HTTP_200_OK)
with admin login:
user login create by api:
register api:
Thanks, Great.
This means that authenticate(username=email, password=password) does not return a user.
Do you work with a degugger ? or may be add a
print(email, password) just after the auth call.
print what comes back from auth . print(authenticate(username=email, password=password))
My guess is that username is not email or somthing like that :)
Edit
How to debug:
login with admin user stop just before this line:
authenticate(username=email, password=password)
check and print the email and password
Do the same with API user check and print the email and password
see that values are the same .
login to django admin site check all premissions flag groups etc etc that are different between both users
try to login to admin page with the api user (set up the correct flags is_active etc)
try in the django manage.py shell or from admin user page to create new password for the api user and retest
i am creating a website where a banks loggs in with its username which is a code but i wanted that bank could log in with its first_name.
i am using default user model for registration.
but authenticate() function works only with username so what i wanted to do is that bank fill their name and function finds the value of username with corrosponding name in the database and then use authenticate() function to log the bank in.
my login function in view.py
def login(request):
if request.method == 'POST':
name = request.POST.get('first_name')
password = request.POST.get('password')
username = ????????
user = authenticate(username=username, password=password)
if user:
if user.is_active and has_role(user,Banker):
auth_login(request,user)
return HttpResponseRedirect(reverse('business:dashboard'))
else:
messages.error(request,"Your account is not active")
return render(request,'accounts/bank_login.html')
else:
messages.error(request,"Invalid Username or Password")
return render(request,'accounts/bank_login.html')
else:
if request.user.is_authenticated:
return HttpResponseRedirect(reverse('business:dashboard'))
else:
return render(request,'accounts/bank_login.html')
so please anybody could tell what should i write in that username to get the value of username from database
**my models.py **
from django.db import models
from django.contrib import auth
# Create your models here.
class User(auth.models.User,auth.models.PermissionsMixin):
def __str__(self):
return self.user.username
This is slightly more complex than doing a simple query. Also, you cannot rule our that two users with the same first name will choose the same password. Here the first user found is taken
Something like this:
from django.contrib.auth.hashers import check_password
firstnameusers = User.objects.filter(first_name=name)
for usr in firstnameusers:
if check_password(password, usr.password):
username = usr.username
break
Note that you will need to write some code to handle the case where a user is not found.
I used this in my views.py file and it works perfectly
def login(request):
if request.method == 'POST':
name = (request.POST.get('name')).upper()
username = (get_user_model().objects.all().filter(first_name = name)).values("username")[0]["username"]
password = request.POST.get('password')
user = authenticate(username=username, password=password)