I am trying to use apollo-server-express to maintain a graphql endpoint and other endpoints to perform oAuth with twitter. I wanted to perform the oauth in the client in a way that a browser tab opens and a socket transmits a "MESSAGE" (as an example) from the callback to the server back to the main client window.
Here's my index.js
import { ApolloServer } from "apollo-server-express";
import {
ApolloServerPluginDrainHttpServer,
ApolloServerPluginLandingPageLocalDefault,
} from "apollo-server-core";
import express from "express";
import session, { Session } from "express-session";
import crypto from "crypto";
import cors from "cors";
import http from "http";
import dotenv from "dotenv";
const socketio = require("socket.io");
dotenv.config();
import { twitterAuthClient, TWITTER_STATE } from "./utils/twitter";
import { schema } from "./schema";
import { prisma } from "./graphql/context";
declare module "express-serve-static-core" {
interface Request {
session: Session & {
socketId: string;
};
}
}
(async () => {
const app: express.Express = express();
const httpServer = http.createServer(app);
const io = socketio(httpServer);
app.use(express.json());
app.use(
session({
secret: [crypto.randomBytes(32).toString("hex")],
resave: true,
saveUninitialized: true,
})
);
const plugins = [ApolloServerPluginDrainHttpServer({ httpServer })];
plugins.push(ApolloServerPluginLandingPageLocalDefault());
const server = new ApolloServer({
plugins,
schema,
context: ({ req }) => {
console.log(req.session);
return {
req,
prisma,
};
},
introspection: true, // disable in Prod
});
await server.start();
const corsOptions = {
origin: "http://localhost:3000",
};
server.applyMiddleware({
app,
cors: corsOptions,
});
// This custom middleware picks off the socket id (that was put on req.query)
// and stores it in the session so we can send back the right info to the
// right socket
const addSocketIdToSession = (req: any, res: any, next: any) => {
req.session.socketId = req.query.socketId;
console.log(req.session.socketId);
next();
};
app.get("/", async (_, res) => {
res.send("Example Server");
});
app.get("/login", addSocketIdToSession, async (req, res) => {
const authUrl = twitterAuthClient.generateAuthURL({
state: TWITTER_STATE,
code_challenge_method: "s256",
});
res.redirect(authUrl);
});
app.get("/callback", async (req, res) => {
try {
const { code, state } = req.query;
if (state !== TWITTER_STATE)
return res.status(500).send("State isn't matching");
await twitterAuthClient.requestAccessToken(code as string);
io.in(req.session.socketId).emit("user", "MESSAGE");
res.end();
} catch (error) {
console.log(error);
}
});
const port = process.env.PORT || 4000;
await new Promise<void>((resolve) => httpServer.listen({ port }, resolve));
console.log(`Server ready at port: ${port} and path: ${server.graphqlPath}`);
})();
And on the client side I am using in my App.js in React
import io from "socket.io-client";
const API_URL = "http://127.0.0.1:4000";
const socket = io(API_URL);
But an issue persists, even though I am applying core options with applyMiddleware in apollo-server
Access to XMLHttpRequest at 'http://127.0.0.1:4000/socket.io/?EIO=3&transport=polling&t=O5gzz1z' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
On server side I am using
"apollo-server-express": "^3.9.0",
"express-session": "^1.17.3",
"socket.io": "^4.5.1",
On client side
"socket.io-client": "^4.5.1"
Any clues are appreciated. Thanks
CodePudding user response:
It looks like you need to enable CORS for socket.io in order for it to work when using a different domain (and port).
Take a look at: https://socket.io/docs/v4/handling-cors/ for more information about how to handle Socket.IO CORS