Skip to content

Trouble saving user along scope #2121

Open
@Alireza-Tabatabaeian

Description

@Alireza-Tabatabaeian

Developing a dashboard using Django channels, two different consumers are implemented, UserConsumer and RequestConsumer.

In UserConsumer methods like login, logout and get_user are implemented. The user Authentication is being done using OTP code sent to user mobile so that a user_send_code method is in charge of sending SMS while the user_login method verifies the code sent by user. UserConsumer is implemented as follow:

class UserConsumer(AsyncJsonWebsocketConsumer):

  async def connect(self):
    await self.accept()

  async def receive_json(self, content=None, **kwargs):
    action = content.get("action", "user.get_user")
    if action == "user.login":
      await self.user_login(content)
    elif action == "user.send_code":
      await self.user_send_code(content)
    elif action == "user.logout":
      await self.user_logout()
    else:
      await self.user_get_user()

  async def user_send_code(self, content):
    await self.send("we will send sms to: {}".format(content['mobile']))
    if check_mobile_pattern(content["mobile"]):
      code = random.randint(1111, 9999)
      self.scope[VERIFY_CODE] = code
      self.scope[MOBILE] = content["mobile"]
      result = await send_sms(content["mobile"], code)
      if result:
        await self.send_json(send_code(200))
      else:
        await self.send_json(send_code(400))
    else:
      await self.send_json(send_code(SystemErrorCodes.InvalidMobilePattern))

  async def user_get_user(self):
    if self.scope['user'].is_authenticated:
      await self.send_json({"id": self.scope['user'].id})
    else:
      await self.send_json(send_code(SystemMessageCodes.AnonymousUserMessage))

  async def user_login(self, content):
    verify_code = self.scope.get("verify_code")
    code = content.get("code")
    mobile = self.scope.get(MOBILE)
    if mobile is not None and verify_code is not None and code == verify_code:
      user = await load_user_by_mobile(mobile)
      del self.scope[VERIFY_CODE]
      # login the user to this session.
      await login(self.scope, user)
      # save the session (if the session backend does not access the db you can use `sync_to_async`)
      await database_sync_to_async(self.scope["session"].save)()
      await self.send_json(send_code(200))
    else:
      await self.send_json(send_code(SystemErrorCodes.InvalidVerifyCode))

after sending the right code to web service, user logins and user_get_user method returns the userID. However when I try to open a new Socket of class RequestConsumer, the user is not set in the scope and socket closed. Here is the code:

class RequestConsumer(AsyncWebsocketConsumer):

  async def connect(self):
    self.user = self.scope['user']
    if self.user.is_authenticated:
      await self.accept()
    else:
      await self.close()
my asgi application is implemented as follow:

application = ProtocolTypeRouter({
  "http": django_asgi_app,
  "websocket": AllowedHostsOriginValidator(
    AuthMiddlewareStack(
      URLRouter(
        [
          path("user/", UserConsumer.as_asgi()),
          path('requests/', RequestConsumer.as_asgi()),
        ]
      )
    )
  ),
})

so both consumer are using same AuthMiddlewareStack middleware. And here is some parts of my settings.py file:

INSTALLED_APPS = [
  'daphne',
  'django.contrib.admin',
  'django.contrib.auth',
  'django.contrib.contenttypes',
  'django.contrib.sessions',
  'django.contrib.messages',
  'django.contrib.staticfiles',
  'file_process.apps.FileProcessConfig',
  'channels',
  'users_app.apps.UsersAppConfig',
  'requests_apps.apps.RequestsAppConfig'
]

MIDDLEWARE = [
  'django.middleware.security.SecurityMiddleware',
  'django.middleware.common.CommonMiddleware',
  'django.middleware.csrf.CsrfViewMiddleware',
  'django.contrib.sessions.middleware.SessionMiddleware',
  'django.contrib.auth.middleware.AuthenticationMiddleware',
  'django.contrib.messages.middleware.MessageMiddleware',
  'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

SESSION_ENGINE = 'django.contrib.sessions.backends.db'  # Default session storage
SESSION_COOKIE_NAME = 'sessionid'  # Default cookie name
SESSION_COOKIE_HTTPONLY = True    # Prevent JavaScript access
SESSION_COOKIE_SECURE = False     # Set to True if using HTTPS
SESSION_EXPIRE_AT_BROWSER_CLOSE = False
SESSION_SAVE_EVERY_REQUEST = True

and finally this is a sample test script I've provided for the client side:

<script>
const socket1 = new WebSocket("ws://localhost:8000/user/");

socket1.onopen = () => {
  console.log("Login WebSocket Connected");
};

socket1.onerror = (error) => {
  console.error("WebSocket Error: ", error);
};

socket1.onclose = () => {
  console.log("Login WebSocket Closed");
};

socket1.onmessage = (event) => {
  const data = JSON.parse(event.data);
  console.log("Message from server: ", data);

  if (data.code === 200) {

    const socket2 = new WebSocket("ws://localhost:8000/requests/");
    
    socket2.onopen = () => {
        console.log("Login WebSocket Connected");
    };

    socket2.onerror = (error) => {
        console.error("WebSocket Error: ", error);
    };

    socket2.onclose = () => {
        console.log("Login WebSocket Closed");
    };
    
  } else {
    alert("Login Failed!");
  }
};

document.getElementById("loginForm").addEventListener("submit", (e) => {
  e.preventDefault();

  const code = document.getElementById("code").value;

  const message = {
    action: "user.login",
    code: code,
  };

  socket1.send(JSON.stringify(message));
});
</script>

I couldn't find any cookie being sent to my browser if there should be one there.

This is my first webservice developed by django-channels so I'm not sure if I have done everything properly.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions