iMerica / dj-rest-auth

Authentication for Django Rest Framework
https://dj-rest-auth.readthedocs.io/en/latest/index.html
MIT License
1.61k stars 302 forks source link

Resend Activation Email #44

Open quank123wip opened 4 years ago

quank123wip commented 4 years ago

Just like the title.

opeodedeyi commented 4 years ago

I had issues with this not too long ago. the problem is I thought Django-rest-auth or Dj-rest-auth handles it for users, but that is not the case. in my case, I had to set up a view for this.

this is what my view looked like for requesting new verification email

from rest_framework.permissions import AllowAny
from django.contrib.auth import get_user_model
from rest_framework.generics import get_object_or_404
from allauth.account.admin import EmailAddress
from allauth.account.utils import send_email_confirmation
from rest_framework.response import Response
from rest_framework import status
from rest_framework.exceptions import APIException

User = get_user_model()

class NewEmailConfirmation(APIView):
    permission_classes = [AllowAny] 

    def post(self, request):
        user = get_object_or_404(User, email=request.data['email'])
        emailAddress = EmailAddress.objects.filter(user=user, verified=True).exists()

        if emailAddress:
            return Response({'message': 'This email is already verified'}, status=status.HTTP_400_BAD_REQUEST)
        else:
            try:
                send_email_confirmation(request, user=user)
                return Response({'message': 'Email confirmation sent'}, status=status.HTTP_201_CREATED)
            except APIException:
                return Response({'message': 'This email does not exist, please create a new account'}, status=status.HTTP_403_FORBIDDEN)

my url.py

path('resend-verification-email/', NewEmailConfirmation.as_view(), name='resend-email-confirmation'),

and when making the request to the API, you just pass the email as shown below, and if the email is already verified, it will not send new verification email, but if not verified, it will send the confirmation email


{
    "email": ""
}
fsboehme commented 4 years ago

If we're trying to play nice with allauth, this should happen automatically when trying to log in with an unverified email.

From docstring for allauth.account.send_email_confirmation:

    E-mail verification mails are sent:
    a) Explicitly: when a user signs up
    b) Implicitly: when a user attempts to log in using an unverified
    e-mail while EMAIL_VERIFICATION is mandatory.
opeodedeyi commented 4 years ago

In my case, the EMAIL_VERIFICATION was mandatory and the email didn't send automatically. I also posted this issue on different platforms wasting months with no reply till today. that is how I got to my conclusion. How did you solve yours? did yours send automatically?

fsboehme commented 4 years ago

No no, I was tracking down why it wasn't sending and landed here. Just added that as a note in case anyone is looking at fixing this. Would be a little more seamless without an extra view. I'll probably use your solution as a starting point for now. Thanks for posting that!

quank123wip commented 4 years ago

I had issues with this not too long ago. the problem is I thought Django-rest-auth or Dj-rest-auth handles it for users, but that is not the case. in my case, I had to set up a view for this.

this is what my view looked like for requesting new verification email

from rest_framework.permissions import AllowAny
from django.contrib.auth import get_user_model
from rest_framework.generics import get_object_or_404
from allauth.account.admin import EmailAddress
from allauth.account.utils import send_email_confirmation
from rest_framework.response import Response
from rest_framework import status
from rest_framework.exceptions import APIException

User = get_user_model()

class NewEmailConfirmation(APIView):
    permission_classes = [AllowAny] 

    def post(self, request):
        user = get_object_or_404(User, email=request.data['email'])
        emailAddress = EmailAddress.objects.filter(user=user, verified=True).exists()

        if emailAddress:
            return Response({'message': 'This email is already verified'}, status=status.HTTP_400_BAD_REQUEST)
        else:
            try:
                send_email_confirmation(request, user=user)
                return Response({'message': 'Email confirmation sent'}, status=status.HTTP_201_CREATED)
            except APIException:
                return Response({'message': 'This email does not exist, please create a new account'}, status=status.HTTP_403_FORBIDDEN)

my url.py

path('resend-verification-email/', NewEmailConfirmation.as_view(), name='resend-email-confirmation'),

and when making the request to the API, you just pass the email as shown below, and if the email is already verified, it will not send new verification email, but if not verified, it will send the confirmation email


{
    "email": ""
}

It's a good workaround, I think we need to write a view inside dj-rest-auth, Maybe I will make a pull request soon.

iMerica commented 4 years ago

Pull requests are welcome ๐Ÿ‘ . Don't forget tests.

sijandh35 commented 3 years ago
from allauth.account.utils import complete_signup

class ResendEmailView(CreateAPIView):
    serializer_class = ResendEmailSerializer
    authentication_classes = [SessionAuthentication, JWTAuthentication, ]
    permission_classes = (IsAuthenticated,)

    def create(self, request, *args, **kwargs):
        serializer = ResendEmailSerializer(data=request.data)
        if serializer.is_valid():
            user = User.objects.get(username=request.data['username'])
            email_address = user.emailaddress_set.get(email=user.email)
            if not email_address.verified:
                complete_signup(self.request._request, user,
                                allauth_settings.EMAIL_VERIFICATION,
                                None)
                return Response("Verification Email Send", status=status.HTTP_201_CREATED)
            else:
                return Response("Email Already Verified", status=status.HTTP_400_BAD_REQUEST)
        else:
            return Response({'Message': serializer.errors}, `status=status.HTTP_400_BAD_REQUEST)```
class ResendEmailSerializer(serializers.Serializer):
    username = serializers.CharField()
escaper01 commented 2 years ago

In my case, the EMAIL_VERIFICATION was mandatory and the email didn't send automatically. I also posted this issue on different platforms wasting months with no reply till today. that is how I got to my conclusion. How did you solve yours? did yours send automatically?

If you set correctly, your email settings : ยดยดยด EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' EMAIL_HOST = 'mail.privateemail.com' EMAIL_USE_TLS = True EMAIL_USE_SSl = False EMAIL_PORT = 587 EMAIL_HOST_USER = getenv('EMAIL_HOST_USER') EMAIL_HOST_PASSWORD = getenv('EMAIL_HOST_PASSWORD') ยดยดยด and set ACCOUNT_EMAIL_VERIFICATION = 'optional' or 'mandatory' it should work automatically

chymdyugah commented 2 years ago

In my case, the EMAIL_VERIFICATION was mandatory and the email didn't send automatically. I also posted this issue on different platforms wasting months with no reply till today. that is how I got to my conclusion. How did you solve yours? did yours send automatically?

If you set correctly, your email settings : ยดยดยด EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' EMAIL_HOST = 'mail.privateemail.com' EMAIL_USE_TLS = True EMAIL_USE_SSl = False EMAIL_PORT = 587 EMAIL_HOST_USER = getenv('EMAIL_HOST_USER') EMAIL_HOST_PASSWORD = getenv('EMAIL_HOST_PASSWORD') ยดยดยด and set ACCOUNT_EMAIL_VERIFICATION = 'optional' or 'mandatory' it should work automatically

It doesn't work automatically for optional email verification, that I'm sure of. I think it's either you write a new view or update the login view for dj-rest-auth. I prefer the former to avoid too many unneccessary emails.

escaper01 commented 2 years ago

@chymdyugah When you register, do get any error on your logs?

chymdyugah commented 2 years ago

@escaper01 errors? No.

I get the email. I was saying when you login, you don't get the email again if you have it set to 'optional'

mgoldenbe commented 2 years ago

@escaper01 errors? No.

I get the email. I was saying when you login, you don't get the email again if you have it set to 'optional'

Neither is it re-sent with 'mandatory' (at least for me). I use console backend, it's for sure not because of some misconfiguration of email...

mgoldenbe commented 2 years ago

Why is this issue still open? I see there is ResendEmailVerificationView in dj_rest_auth.registration.views ...

aladagemre commented 2 years ago

Are we sure this view really works? I get the following error when I POST email to http://127.0.0.1:8000/auth/registration/resend-email/

'ResendEmailVerificationView' should either include a queryset attribute, or override the get_queryset() method.

I see there's no such attribute or method in the view extending CreateAPIView. Maybe we should add that attribute. My temporary solution:

class CustomResendEmailVerificationView(CreateAPIView):
    permission_classes = (AllowAny,)
    serializer_class = ResendEmailVerificationSerializer
    queryset = EmailAddress.objects.all()

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        try:
            email = EmailAddress.objects.get(**serializer.validated_data)
        except allauth.account.models.EmailAddress.DoesNotExist as exc:
            logger.warning(exc)
            raise ValidationError("Account does not exist")
        if email.verified:
            raise ValidationError("Account is already verified")

        email.send_confirmation()
        return Response({"detail": _("ok")}, status=status.HTTP_200_OK)
femiir commented 2 years ago

@aladagemre how were you able to by pass this error I am trying to use your code snippet but can't seem to import logger

aladagemre commented 2 years ago

@aladagemre how were you able to by pass this error I am trying to use your code snippet but can't seem to import logger

@femiir Put the following somewhere at the top:

import logging
logger = logging.getLogger(__name__)
femiir commented 2 years ago

@aladagemre how were you able to by pass this error I am trying to use your code snippet but can't seem to import logger

@femiir Put the following somewhere at the top:

import logging
logger = logging.getLogger(__name__)

thank you so much Apparently the updated version of dj-rest-auth has queryset added but returns ok status for almost every operation I will suggest you make a pull request with your solution...

aladagemre commented 2 years ago

Seems to be fixed by #332

chaulapatrice commented 2 months ago

An endpoint already exists for this on version 5.0.2 http://localhost:8000/auth/registration/resend-email/