cors error preflight alow origin mismatch for apollo client and sever - authentication

Overview
I am trying to get authentications set up in Apollo but I keep running into this network error: CORS error PreflightAllowOriginMismatch. I have looked and tried so many solutions on the internet but nothing is working.
I have my client running on localhost:3000 and my server on localhost:4000.
Code
//client index.js
import React from 'react';
import { render } from 'react-dom';
import { ApolloProvider } from '#apollo/client';
import App from './App';
import { ApolloClient, createHttpLink, InMemoryCache } from '#apollo/client';
const httpLink = createHttpLink({
uri: 'http://localhost:4000/graphql',
credentials: 'include',
});
const client = new ApolloClient({
link: httpLink,
cache: new InMemoryCache()
});
render((
<ApolloProvider client={client}>
<App />
</ApolloProvider>
), document.getElementById('root')
);
//server index.js
const { ApolloServer } = require('apollo-server-express');
const express = require('express');
const cors = require('cors');
const schema = require('./schema');
const models = require('./sequelize/models');
const server = new ApolloServer({
schema,
context: req => ({
...req,
models,
})
});
const app = express();
var corsOptions = {
origin: 'http://localhost:3000/',
credentials: true // <-- REQUIRED backend setting
};
app.use(cors(corsOptions));
server.applyMiddleware({ app, cors: false });
app.listen({ port: 4000 });
I'm honestly lost at this point. I new to apollo and for the life of me cannot find what i am missing.

Although I am unsure about the security the following changes fixed my issue.
remove
origin: 'http://localhost:3000/',
credentials: true // <-- REQUIRED backend setting
};
app.use(cors(corsOptions));
update
server.applyMiddleware({ app, cors: {credentials: true, origin: true} });
the final file:
//server index.js
const { ApolloServer } = require('apollo-server-express');
const express = require('express');
const schema = require('./schema');
const models = require('./sequelize/models');
const server = new ApolloServer({
schema,
context: req => ({
...req,
models,
})
});
const app = express();
server.applyMiddleware({
app,
cors: {credentials: true, origin: true}
});
app.listen({ port: 4000 });

Related

Can't receive a response message when using proxy-middleware between react ui and express backend

I see that the request sent from the ui created using React is forwarded to the backend, but I can't get the response from the ui. There may be details that I missed as I am very new to these issues, thanks in advance :)
//react Login.js
function Login() {
const fetch = actions.fetchUser();
async function handleSubmit() {
try {
fetch();
} catch (err) {
console.error('err', err);
}
}
export default Login;
//index.js
import axios from 'axios';
export const fetchUser = () => async () => {
await axios.get('/api/login');
};
//setupProxy.js
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function (app) {
app.use(
['/api'],
createProxyMiddleware({
target: 'http://localhost:5000',
}),
);
};
//express app.js
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const app = express();
const port = 5000;
app.use(cors());
app.use(bodyParser.json());
require('./routes/login')(app);
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
// espress login.js
module.exports = app => {
app.get('/api/login', (req, res) => {
console.error('express login');
res.send('login');
});
First of all, do not mix cjs and mjs import/exports.
second of all, you export your middleware but never register/use it. At least your code does not show that part.
Here is very minimal example how you can proxy your react UI via express.
const express = require('express');
const proxy = require('express-http-proxy');
const app = express();
app.get('/api', (req, res) => {
res.send({my: 'data'});
});
// register other routes here
app.use(proxy('http://127.0.0.1:3000'));
app.listen(5000, '0.0.0.0', () => {
console.log('Server is running at http://127.0.0.1:5000');
});
React app content will be available on http://127.0.0.1:5000 with your routes.
And http://127.0.0.1:5000/api will be your express route.
Note: I assume your react app runs on the port 3000

How to add "directiveResolvers" to apollo-server-express middleware?

I'm working on transitioning from apollo-server to apollo-server-express, so that I can implement Stripe webhooks.
Previously with apollo-server, I was able to add directiveResolvers to the schema, so that I can perform an #auth check on queries or mutations. The server file looked like this:
apollo-server:
import { ApolloServer } from 'apollo-server';
import { ApolloServerPluginLandingPageGraphQLPlayground, ApolloServerPluginLandingPageDisabled } from 'apollo-server-core';
import { CorsOptions } from 'cors';
import { makeExecutableSchema } from '#graphql-tools/schema';
import { applyMiddleware } from 'graphql-middleware';
import { context } from './context';
import { resolvers } from './resolvers';
import typeDefs from './typeDefs';
import directiveResolvers from '#/directives/isAuthorized';
const corsOptions = {
origin: process.env.ORIGIN_URL,
credentials: true,
} as CorsOptions;
const graphqlEndpoint = 'graphql';
const schema = makeExecutableSchema({
typeDefs,
resolvers,
directiveResolvers,
});
const server = new ApolloServer({
schema: applyMiddleware(schema),
context,
cors: corsOptions,
introspection: true,
plugins: [
process.env.NODE_ENV === 'production'
? ApolloServerPluginLandingPageDisabled()
: ApolloServerPluginLandingPageGraphQLPlayground()
],
debug: true,
});
server
.listen({ port: process.env.PORT || 4000 })
.then(({ url }) => console.log(`🚀 Server ready at: ${url}${graphqlEndpoint}`));
With apollo-server-express though, I'm struggling to understand where to add these directiveResolvers in the configuration. Here is what the file currently looks like:
apollo-server-express:
import { ApolloServer } from 'apollo-server-express';
import express from 'express';
import http from 'http';
import {
ApolloServerPluginLandingPageGraphQLPlayground,
ApolloServerPluginLandingPageDisabled,
ApolloServerPluginDrainHttpServer
} from 'apollo-server-core';
import { CorsOptions } from 'cors';
import { makeExecutableSchema } from '#graphql-tools/schema';
import { applyMiddleware } from 'graphql-middleware';
import { context } from './context';
import { resolvers } from './resolvers';
import typeDefs from './typeDefs';
import directiveResolvers from '#/directives/isAuthorized';
const corsOptions: CorsOptions = {
origin: process.env.ORIGIN_URL,
credentials: true,
};
async function startApolloServer () {
const app = express();
const httpServer = http.createServer(app);
const server = new ApolloServer({
typeDefs,
resolvers,
context,
introspection: true,
plugins: [
ApolloServerPluginDrainHttpServer({ httpServer }),
process.env.NODE_ENV === 'production'
? ApolloServerPluginLandingPageDisabled()
: ApolloServerPluginLandingPageGraphQLPlayground()
],
});
await server.start();
server.applyMiddleware({ app, cors: corsOptions });
await new Promise<void>(resolve => httpServer.listen({ port: process.env.PORT || 4000 }, resolve));
console.log(`🚀 Server ready at: http://localhost:4000${server.graphqlPath}`);
}
startApolloServer();
How do I go about adding the directiveResolvers to the server configuration?
In classic fashion, I figured it out pretty soon after posting this. I'd missed the makeExecutableSchema piece that I'd used before. It now looks like this and seems to be functioning correctly:
import { ApolloServer } from 'apollo-server-express';
import express from 'express';
import http from 'http';
import {
ApolloServerPluginLandingPageGraphQLPlayground,
ApolloServerPluginLandingPageDisabled,
ApolloServerPluginDrainHttpServer
} from 'apollo-server-core';
import { CorsOptions } from 'cors';
import { makeExecutableSchema } from '#graphql-tools/schema';
import { context } from './context';
import { resolvers } from './resolvers';
import typeDefs from './typeDefs';
import directiveResolvers from '#/directives/isAuthorized';
const corsOptions: CorsOptions = {
origin: process.env.ORIGIN_URL,
credentials: true,
};
const schema = makeExecutableSchema({
typeDefs,
resolvers,
directiveResolvers,
});
async function startApolloServer () {
const app = express();
const httpServer = http.createServer(app);
const server = new ApolloServer({
schema,
context,
introspection: true,
plugins: [
ApolloServerPluginDrainHttpServer({ httpServer }),
process.env.NODE_ENV === 'production'
? ApolloServerPluginLandingPageDisabled()
: ApolloServerPluginLandingPageGraphQLPlayground()
],
});
await server.start();
server.applyMiddleware({ app, cors: corsOptions });
await new Promise<void>(resolve => httpServer.listen({ port: process.env.PORT || 4000 }, resolve));
console.log(`🚀 Server ready at: http://localhost:4000${server.graphqlPath}`);
}
startApolloServer();

How to set authentications headers with Vue apollo and composition api?

I've build my app with Vite. I read many documents on web about the topic but I'm still very confused. I've a login form that send credentials to a protected view. When post the data I set the headers and store the Bearer token in the local storage.
The problem is that it doesn't work cause the Bearer token result equal to null.
Only when I logout the token is set in the headers.
That's how is the header when I log in
And here how it's set when I log out...
My main.js code is this:
import { createApp, provide, h } from "vue";
import {
ApolloClient,
createHttpLink,
InMemoryCache,
} from "#apollo/client/core";
import { DefaultApolloClient } from "#vue/apollo-composable";
import App from "./App.vue";
import router from "./router";
import { createPinia } from "pinia";
import { provideApolloClient } from "#vue/apollo-composable";
const authToken = localStorage.getItem("auth-token");
const httpLink = createHttpLink({
uri: "http://localhost/graphql",
headers: {
Authorization: "Bearer " + authToken,
},
});
const cache = new InMemoryCache();
const apolloClient = new ApolloClient({
link: httpLink,
cache,
});
provideApolloClient(apolloClient);
const app = createApp({
setup() {
provide(DefaultApolloClient, apolloClient);
},
render: () => h(App),
});
app
.use(router)
.use(createPinia())
.mount("#app");
and this is my routes.js
const router = createRouter({
history: createWebHistory(),
routes
})
router.beforeEach((to, from, next) => {
const requiresAuth = to.matched.some(record => record.meta.requiresAuth);
const isAuthenticated = localStorage.getItem('auth-token');
if(requiresAuth && isAuthenticated===null){
next('/auth/login');
}else {
next();
}
});
I'm surely making some mistakes in my main.js but I cannot understand what's wrong. I'm very confused :-/
Thanks to who'll be able to help me.
Try using a helper function to get the token from local storage; I'm using this method and it's working fine for me. To get your code more organized, create a separate folder to define the apollo client. Here is the code:
// apolloClient.ts
import { ApolloClient, InMemoryCache, HttpLink } from "#apollo/client/core";
function getHeaders() {
const headers: { Authorization?: string; "Content-Type"?: string } = {};
const token = localStorage.getItem("access-token");
if (token) {
headers["Authorization"] = `Bearer ${token}`;
}
headers["Content-Type"] = "application/json";
return headers;
}
// Create an http link:
const httpLink = new HttpLink({
uri: `${import.meta.env.VITE_API_URL}/graphql`,
fetch: (uri: RequestInfo, options: RequestInit) => {
options.headers = getHeaders();
return fetch(uri, options);
},
});
// Create the apollo client
export const apolloClient = new ApolloClient({
cache: new InMemoryCache(),
link: httpLink,
defaultOptions: {
query: {
errorPolicy: "all",
},
mutate: {
errorPolicy: "all",
},
},
});
Then you can use it in your main.ts like this:
// main.ts
import { createApp, h } from "vue";
import { provideApolloClient } from "#vue/apollo-composable";
import App from "./App.vue";
import { apolloClient } from "./apolloClient";
const app = createApp({
setup() {
provideApolloClient(apolloClient);
},
render: () => h(App),
});
app.mount("#app");

Using createProxyMiddleware for the same routes on FE and BE while avoiding CORS

I'm building an app using node.js on the server side and used create-react-app for the client.
When running locally I want to use render Home component for path '/home' like so:
<BrowserRouter>
<div>
<Route exact path="/home" component={Home} />
</div>
</BrowserRouter>
and on the server side I want to to use '/home' to get requests like so:
app.use('/home', require('./routes/home'))
where 'routes/home/' is an express router:
module.exports = router.get('/', (req, res) => {
res.send({ status: 200, data: 'Hello World!' })
})
The problem is that I got CORS error at first, so I added createProxyMiddleware in order to proxy the server responses:
const { createProxyMiddleware } = require("http-proxy-middleware");
module.exports = function (app) {
const portPrefix = 'localhost:3000'
const options = {
target: 'http://localhost:5000',
router: {
'dev.localhost:3000': 'http://localhost:5000'
}
}
app.use([`/home`], createProxyMiddleware(options));
};
But now, when I make a request from the client (port 3000) to '/home' the request is redirected to port 5000 and I get the res.send({...}) immediately (instead of rendering the Home component that is using axios to make the request and handle the response..)
My Home component:
import React, { useState, useEffect } from 'react'
import axios from 'axios'
axios.defaults.baseURL = 'http://localhost:5000'
const Home = () => {
const [loading, setLoading] = useState(true)
const [data, setData] = useState("")
useEffect(() => {
async function makeRequest() {
const res = await axios.get('/home')
setData(res.status === 200 ? res.data.data : "test string")
setLoading(false)
}
makeRequest()
}, [])
return (
<div className="container">
{ !loading && <h1>{data}</h1> }
Home Page
</div>
)
}
export default Home
I saw that there's a solution to avoid createProxyMiddleware and just add headers to the response:
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "http://localhost:3000"); // update to match the domain you will make the request from
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
My question are:
How will adding headers behave on production?
Is the headers solution the better solution for local development, since createProxyMiddleware is there to assist with that I guess.
If I decide to use createProxyMiddleware, how can I use the same routes for both client and server (e.g. '/home' to render Home component on FE and '/home' for get requests on BE)? because now it "skips" the client side and goes straight to the server.
Thanks a lot!

apollo client , adding subscriptions while not breaking http link that uses jwt

I currently have a graphql api that handles HTTP requests, I've migrated to apollo-client and now I want to add subscriptions. The problem is, that I can't figure out how to combine my HTTP link (that has a JWT auth middleware).
I have no code errors to share, BUT, the issue is in the way i use the link split method. why? because when I remove it and just use authLink.concat(httpLink) everything works smoothly (except for that in this case I don't check if the connection is WS or HTTP...).
Here's my code:
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
// import ApolloClient from "apollo-boost"; // migrated to apollo-client
import { ApolloClient } from 'apollo-client';
import { createHttpLink } from 'apollo-link-http';
import { setContext } from 'apollo-link-context';
import { InMemoryCache } from 'apollo-cache-inmemory';
// subscriptions imports
import { split } from 'apollo-link'
import { WebSocketLink } from 'apollo-link-ws';
import { getMainDefinition } from 'apollo-utilities';
import VueApollo from "vue-apollo";
// links composition
const httpLink = createHttpLink({
uri: 'http://localhost:4000/graphql',
});
const wsLink = new WebSocketLink({
uri: `ws://localhost:5000`,
options: {
reconnect: true
}
});
// this is the sketchy section
const link = split(
({ query }) => {
const { kind, operation } = getMainDefinition(query)
return kind === 'OperationDefinition' && operation === 'subscription'
},
wsLink,
httpLink
)
// JWT middleware
const authLink = setContext((_, { headers }) => {
const token = localStorage.getItem('token');
return {
headers: {
...headers,
authorization: token ? token : ''
}
}
});
const wsPlusHttpWithJWT = authLink.concat(link)
export const apolloClient = new ApolloClient({
// link: authLink.concat(httpLink), // this worked
link: wsPlusHttpWithJWT,
cache: new InMemoryCache()
});
Vue.use(VueApollo);
const apolloProvider = new VueApollo({ defaultClient: apolloClient });
Vue.config.productionTip = false;
new Vue({
apolloProvider,
router,
render: h => h(App),
}).$mount("#app");