1

I am using Djoser to create an authentication backend. I want the following to happen:

  1. User registers at http://localhost:8000/auth/users/ with email, username, password, and re_password
  2. User receives an activation email at the email they entered with a URL to activate their account. The URL is of the form: <web url>/activate/{uid}/token (eg: http://localhost:8000/activate/Mg/apur8c-6100390a061c5ff081235193867f943a)
  3. Clicking on that URL sends a GET request to the server where a view (UserActivationView) receives the extracted uid and token and sends a request of its own to <web url>/users/activation/{uid}/token (http://localhost:8000/users/activation/Mg/apur8c-6100390a061c5ff081235193867f943a). And returns the response that it gets from that request as its response.
  4. User account gets activated.

This is the code that does all of the above:

# Djoser configuration in settings.py
DJOSER = {
    'LOGIN_FIELD': 'email',

    'PASSWORD_RESET_CONFIRM_URL': 'password-reset/{uid}/{token}',
    'USERNAME_RESET_CONFIRM_URL': 'username-reset/{uid}/{token}',

    'SEND_ACTIVATION_EMAIL': True,
    'ACTIVATION_URL': 'activate/{uid}/{token}',

    'SEND_CONFIRMATION_EMAIL': True,
    'PASSWORD_CHANGE_EMAIL_CONFIRMATION': True,
    'USERNAME_CHANGED_EMAIL_CONFIRMATION': True,

    'USER_CREATE_PASSWORD_RETYPE': True,
    'SET_PASSWORD_RETYPE': True,
}
# views.py in user app (users/views.py)
from rest_framework.views import APIView
from rest_framework.response import Response
import requests


class UserActivationView(APIView):
    def get(self, request, uid, token):
        protocol = "https://" if request.is_secure() else "http://"
        web_url = protocol + request.get_host()
        post_url = web_url + '/auth/users/activation/'
        post_data = {'uid': uid, "token": token}
        print(f"Sending request to {post_url} with {post_data}")
        result = requests.post(post_url, data=post_data)     # <<-- causes the server to hang
        return Response(result.text)
# users/urls.py
from django.urls import path, include
from .views import UserActivationView

app_name = "users"

urlpatterns = [
    path('auth/', include('djoser.urls')),
    path('auth/', include('djoser.urls.jwt')),
    path('activate/<str:uid>/<str:token>/',
         UserActivationView.as_view())
]
# MAIN project urls.py
from django.contrib import admin
from django.urls import include, path


urlpatterns = [
    path('admin/', admin.site.urls),
    path('chat/', include('frontend.urls')),
    path("chat/", include('backend.urls')),
    path('api/accounts/', include('backend.urls')),
    path('', include('users.urls'))

]

Now, the problem arises in step 3 (at the result = requests.post(post_url, data=post_data) line). When the view attempts to send a post request the server hangs for quite some time. During this, it doesn't even halt when I do CTRL + C. This is the output that I get (I have added comments):

HTTP POST /auth/users/ 201 [5.87, 127.0.0.1:56239] ## User registration was successful and email was sent
HTTP GET /activate/Mg/apuvq9-3461158f807a1e054738458bc2372486 301 [0.00, 127.0.0.1:56245] # 301 response while trying to GET the URL sent in the email
Sending request to http://localhost:8000/auth/users/activation/ with {'uid': 'Mg', 'token': 'apuvq9-3461158f807a1e054738458bc2372486'} # My print statement
# A long pause here. Server hangs and then this error appears:
Application instance <Task pending coro=<StaticFilesWrapper.__call__() running at D:\pigeon\venv\lib\site-packages\channels\staticfiles.py:44> wait_for=<Future pending cb=[_chain_future.<locals>._call_check_cancel() at C:\ProgramData\Anaconda3\lib\asyncio\futures.py:348, <TaskWakeupMethWrapper object at 0x000001E3EDE96858>()]>> for connection <WebRequest at 0x1e3edd8a788 method=GET uri=/activate/Mg/apuvq9-3461158f807a1e054738458bc2372486/ clientproto=HTTP/1.1> took too long to shut down and was killed.
Application instance <Task pending coro=<StaticFilesWrapper.__call__() running at D:\pigeon\venv\lib\site-packages\channels\staticfiles.py:44> wait_for=<Future pending cb=[_chain_future.<locals>._call_check_cancel() at C:\ProgramData\Anaconda3\lib\asyncio\futures.py:348, <TaskWakeupMethWrapper object at 0x000001E3EE1067C8>()]>> for connection <WebRequest at 0x1e3edd8a788 method=GET uri=/activate/Mg/apuvq9-3461158f807a1e054738458bc2372486/ clientproto=HTTP/1.1> took too long to shut down and was killed.
Internal Server Error: /activate/Mg/apuvq9-3461158f807a1e054738458bc2372486/
Traceback (most recent call last):
  File "D:\pigeon\venv\lib\site-packages\asgiref\sync.py", line 482, in thread_handler
    raise exc_info[1]
  File "D:\pigeon\venv\lib\site-packages\django\core\handlers\exception.py", line 38, in inner
    response = await get_response(request)
  File "D:\pigeon\venv\lib\site-packages\django\core\handlers\base.py", line 238, in _get_response_async
    )(e, request)
  File "D:\pigeon\venv\lib\site-packages\asgiref\sync.py", line 444, in __call__
    ret = await asyncio.wait_for(future, timeout=None)
  File "C:\ProgramData\Anaconda3\lib\asyncio\tasks.py", line 414, in wait_for
    return await fut
concurrent.futures._base.CancelledError

Alternatively, if I manually send an activation request via Postman or a separate Python script then the request works just fine and the user is activated:

enter image description here enter image description here

The problem only seems to arise when the request is made from within the view to the server. I also tried result = requests.get('http://localhost:8000/chat') as an experiment and it too caused the server to hang whereas result = requests.get('http://google.com') causes no such issues.

The view's code was taken from here and someone under the answer also commented:

This hangs my debug django server – Hack_Hut Apr 25 at 11:55

How can I resolve this?

I am using:

  • Python: 3.7.5
  • Django: 3.2.5
  • Djoser: 2.1.0
  • Django rest framework: 3.12.4
  • Requests: 2.26.0
  • What Python | Django | Django Rest Framework are you using? – angardi Jul 15 '21 at 23:11
  • @angardi I have updated my question, please have a look – Sabito stands with Ukraine Jul 15 '21 at 23:15
  • 1
    The user activate URL `auth/users/activation` should be sent from your frontend app, not from your backend. Also take note that activation URL `activate/{uid}/{token}` should be the frontend page URL that your email should direct users to because this page would be sending the POST request to `auth/users/activation` and handling the result from there. – Scratch'N'Purr Jul 16 '21 at 04:15
  • @Scratch'N'Purr yeah, that makes sense. The frontend could extract the uid and token and post it at the activation endpoint and redirect the user to the home page if a proper response is returned else show the error. – Sabito stands with Ukraine Jul 16 '21 at 05:22
  • Did you find a solution for this? I'm facing the same issues. I think this is happening only when we execute requests.post() against localhost. – Arman Avetisyan Jan 03 '22 at 13:13

0 Answers0