Подписка клиента apollo передает JWT-токен, обрабатываемый DjangoChannelsGraphqlWs на стороне сервера

Я использую подписки Graphql с клиентом Apollo в приложении Vue3, используя пакеты DjangoChannelsGraphqlWs и DjangoGraphqlJWT в моем бэкенд приложении.

Я пытаюсь передать JWT-токен в подписке, но пользователь в приложении Django всегда анонимный (это означает, что он не получает токен). Однако, когда я использую интерфейс GraphiQL, он получает токен. Мне кажется, что я делаю что-то не так на стороне клиента.

Обратите внимание, что при запросах и мутациях JWT-токен передается с ключом "authorization", и работает как положено в приложении Django.

СТОРОНА КЛИЕНТА:

Обратите внимание, что мне удалось зарегистрировать токен в connectionParams.

Я прокомментировал все пары ключ-значение, которые я пробовал до сих пор (путем разнообразных поисков на Stackoverflow и др.).

import { setContext } from "apollo-link-context";
import { Storage } from "@capacitor/storage";

import {
  ApolloClient,
  createHttpLink,
  InMemoryCache,
  split,
} from "@apollo/client/core";
import { getMainDefinition } from "@apollo/client/utilities";
import { WebSocketLink } from "@apollo/client/link/ws";

const authLink = setContext(async (_: any, { headers }: any) => {
  const { value: authStr } = await Storage.get({ key: "auth" });

  let token;
  if (authStr) {
    const auth = JSON.parse(authStr);
    token = auth.token;
  }

  // return the headers to the context so HTTP link can read them
  return {
    headers: {
      ...headers,
      authorization: token ? `JWT ${token}` : null,
    },
  };
});

const httpLink = createHttpLink({
  uri: process.env.VUE_APP_GRAPHQL_URL || "http://0.0.0.0:8000/graphql",
});

const wsLink = new WebSocketLink({
  uri: process.env.VUE_APP_WS_GRAPHQL_URL || "ws://0.0.0.0:8000/ws/graphql/",
  options: {
    reconnect: true,
    connectionParams: async () => {
      const { value: authStr } = await Storage.get({ key: "auth" });
      let token;
      if (authStr) {
        const auth = JSON.parse(authStr);
        token = auth.token;
        console.log(token); // So far so good the token is logged.
        return {
          authorization: `JWT ${token}`,
          // token: `JWT ${token}`,
          // authToken: `JWT ${token}`,
          // authorization: `Bearer ${token}`,
          // token: `Bearer ${token}`,
          // authToken: `Bearer ${token}`,
          // authorization: token,
          // token: token,
          // authToken: token,
        };
      }

      return {};
    },
  },
});

const link = split(
  // split based on operation type
  ({ query }) => {
    const definition = getMainDefinition(query);
    return (
      definition.kind === "OperationDefinition" &&
      definition.operation === "subscription"
    );
  },
  wsLink,
  httpLink
);

const cache = new InMemoryCache();
export default new ApolloClient({
  // @ts-ignore
  link: authLink.concat(link),
  cache,
});

BACKEND:

asgy.py

"""
ASGI config for tinga project.

It exposes the ASGI callable as a module-level variable named ``application``.

For more information on this file, see
https://docs.djangoproject.com/en/3.1/howto/deployment/asgi/
"""

from tinga.routing import MyGraphqlWsConsumer
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
import os

from django.core.asgi import get_asgi_application
from django.conf.urls import url

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'tinga.settings')

application = get_asgi_application()

# import websockets.routing

application = ProtocolTypeRouter({
    "http": get_asgi_application(),
    "websocket": AuthMiddlewareStack(
        URLRouter([
            url(r"^ws/graphql/$", MyGraphqlWsConsumer.as_asgi()),
        ])
    ),
})

subscriptions.py

class NewMessageSubscription(channels_graphql_ws.Subscription):
    """Subscription triggers on a new chat message."""

    message = graphene.Field(MessageType)

    class Arguments:
        """Subscription arguments."""

        chat_uuid = graphene.String()

    def subscribe(self, info, chat_uuid=None):
        """Client subscription handler."""
        print('info.context')
        print(info.context.user) // We have a user with graphiQL but with apollo user is anonymous
        del info
        # Specify the subscription group client subscribes to.
        return [chat_uuid] if chat_uuid is not None else None

Я понял, что это часть полезной нагрузки в потребителе, реализованном с помощью каналов Graphql.

Полезная нагрузка будет содержать любые параметры, переданные в connectionParams.

class MyGraphqlWsConsumer(channels_graphql_ws.GraphqlWsConsumer):
    """Channels WebSocket consumer which provides GraphQL API."""
    schema = schema

    async def on_connect(self, payload):
        print(payload)
        """New client connection handler."""
        # You can `raise` from here to reject the connection.
        print("New client connected!")

Вернуться на верх