Подписка клиента 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!")