I'm sending fetch from React to Express to authenticate with Google but I've been getting access blocked by CORS error. I'm redirecting the POST request from React to the Google URL for authentication. I tried using cors in the express app but I'm still getting the same error.
For fetching
const handleClick = (e) => {
fetch('http://localhost:8000/api/mail/login', {
method: 'POST'
})
.then(res => res.text())
}
Using cors in express app.js
app.use(cors())
Trying to redirect to google auth
const oauth2Client = new google.auth.OAuth2(
process.env.CLIENT_ID,
process.env.CLIENT_SECRET,
process.env.REDIRECT_URI
)
const url = oauth2Client.generateAuthUrl({
access_type: 'offline',
scope: process.env.SCOPE
})
const gmail = google.gmail({
version: 'v1',
auth: oauth2Client
})
router.post('/login', (req, res, next) => {
res.redirect(url)
})
The error: Access to fetch at 'https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&scope=https%3A%2F%2Fmail.google.com%2F&response_type=code&client_id=727520060136-ngpfd4ll798v42gfclh7cms9ndqstt32.apps.googleusercontent.com&redirect_uri=http%3A%2F%2Flocalhost%3A8000' (redirected from 'http://localhost:8000/api/mail/login') from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
The authentication flow must happen in a visible browsing context, not with a fetch request. In other words: You must navigate the current tab to (or open a new tab at) http://localhost:8000/api/mail/login, the tab will then be redirected to https://accounts.google.com/o/oauth2/v2/auth?... and this page becomes visible. Now the user must interact with that page to choose/confirm their Google account, after which they will be redirected to a page of your choice (for example, http://localhost:8000/welcome).
When made like this, none of the requests made is cross-origin, so no CORS will be involved at all.
Instead of the handleClick function, you need a login form like
<form action="http://localhost:8000/api/mail/login" method="post">
<input type="submit" value="Press to log in"/>
</form>
Related
I have my express server hosted on Heroku, while my react app is hosted on Netlify.
My server has a login route, and whenever I attempt to log in from the client hosted on netlify, I get the below CORS policy error;
Access to XMLHttpRequest at 'https://u-annon.herokuapp.com/api/users/login' from origin 'https://boisterous-churros-56908e.netlify.app' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
There's no cors error on the GET routes that fetches post, and I don't get this error on localhost, even when I connect the client on the localhost to the server on heroku, the cors setting seems to handle that well, which makes me think this has something to do with netlify hosting. I've been reading about the netlify.toml file, but I can seem to find anything that fixes this problem.
Can someone point me in the right direction pls?
This is my cors setting on the express server;
app.use(cors({
origin: ["https://boisterous-churros-56908e.netlify.app", "http://localhost:3000"],
credentials: true,
methods: "GET, POST, PUT, DELETE",
preflightContinue: true,
allowedHeaders: ['Content-Type', 'Authorization']
}))
This is the login post request from the client
const LoginSubmit = (e) =>{
const data = {
email: email,
password: password
}
const login = axios.post(`${url}/users/login`, encodeURI({...data}))
.then((res) => {
if(res.status === 200){
parseJwt(res.data)
setLoggedIn(true)
setLoading(false)
navigate('/profile')
}
else{
setLoginError("An error occured, pls try again")
return
}
})
.catch((error) => {
console.log(error)
setLoginError("Email or password entered is incorrect, try again or signup")
});
return login
}
Front-End: [Axios]
const formSubmit = async (e) => {
e.preventDefault()
const formData = new FormData(e.target)
const email = formData.get('email')
const password = formData.get('password')
try {
const res = await axios.post('http://172.16.2.19:3001/api/v1/auth/login', {
email,
password,
})
console.log(res.data) // its okay, I can login if email & password are correct.
} catch (error) {
console.log(error)
}
}
Back-End [Nodejs ExpressJs]:
Inside App.js:
const cors = require('cors')
app.use(cors({ credentials: true }))
Inside Login.js (/auth/login endpoint):
// ... code, then... if email & password are correct:
// 3600000ms = 1hour
res.cookie('jwt', token, { httpOnly: true, expires: new Date(Date.now() + 3600000 })
res.status(200).json({
status: 'success'
token,
data: userDoc,
})
Then, when I login in my browser:
I can login successfully, but no cookies will be created, see:
The front-end http service (react app) is running on http://172.16.2.19:3000
The back-end http service (expressjs) is running on http://172.16.2.19:3001
The axios requests I'm sending from the front-end are requesting: http://172.16.2.19:3001
So what's the problem?
The problem that no cookies are getting created in the browser is preventing me from continuing to design the front-end application, because if I wanted to request anything from my API, I have to be authenticated, all the routes on the API I made are protected, so if I wanted to request anything from the API, I will have to send my jwt token along with the request.
edit **:
here's the response from the /auth/login endpoint after successfully logging in:
I am using brave browser, the latest version.
I tried this on firefox, it has the same behavior.
GUYS GUYS GUYS I found it!!!! after 3 hours of researching, let me save your time:
For anyone having the same problem, all you have to do is
change your backend code from:
const cors = require('cors')
app.use(cors({ credentials: true }))
to
app.use(cors({ credentials: true, origin: true }))
and make sure you're using withCredentials: true on the front-end with every request (the login POST method and all the other requests that requires authentication)
why?
setting origin property to true is going to reflect the request origin, the origin property can be a string if you wanted to specify a particular domain, ex: http://localhost:3000. But if you have more than one client, setting this to true is a wise choise.
and for those of you wondering about mobile devices in case of specifying a string for the origin field with one particular domain. This problem of cors only happens in browsers, any other client doesn't use that CORS policy.
I would check by passing {withCredentials: true} as the third argument to the axios method to allow the browser to set the cookie via the request.
I don't think it is correct to use the backend to save cookies, as cookies is a browser feature separate from the database. I might be wrong though. When the post is successful, res will return a token. You save this token in the browser's local storage.
const formSubmit = async (e) => {
e.preventDefault()
const formData = new FormData(e.target)
const email = formData.get('email')
const password = formData.get('password')
try {
const res = await axios.post('http://172.16.2.19:3001/api/v1/auth/login', {
email,
password,
})
//browsers local storage
localStorage.setItem('access_token',res.data.access);
localStorage.setItem('refresh_token',res.data.refresh);
console.log(res.data) // its okay, I can login if email & password are correct.
}
You will then have to create an authorization header as such
headers:{
Authorization: localStorage.getItem('access_token')
? 'JWT '+localStorage.getItem('access_token')
: null
}
I have two servers, frontend (Next.js) and backend (express.js api server).
Frontend server is running without any additions. But I have an nginx proxy for backend.
So far everything is good because they are not connected yet.
Frontend: is working as it should be.
Backend: I can make calls directly from the backend itself (by self origin).
When I make a fetch get call from my frontend server to the backend server, it normally gives a cors error because the origins are different.
For this, I set the backend server with cors:
// /src/middlewares/cors.ts
import cors from 'cors';
const whitelist = new Set(['http://192.168.1.106:3000', 'https://192.168.148.132']);
// frontend: http://192.168.1.106:3000
// backend: https://192.168.148.132
const corsOptions = {
optionsSuccessStatus: 200,
origin: (origin: any, callback: any) => {
console.log('origin: ' + origin);
if (whitelist.has(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
// credentials: true,
};
export default cors(corsOptions);
and
// /app.ts
import cors from './middlewares/system/cors.js';
.
.
// setup cors
app.options('*', cors);
app.use(cors);
.
.
After doing this, I reach my main goal. The frontend server can make call to the backend server.
output:
But this time I notice a problem. I can't send self request to backend anymore (by self origin).
When dealing with this I looked at the origins that came to the /src/middlewares/cors.ts file that I showed above.
for frontend:
for backend:
I am using self signed ssl in nginx for back server.
And there is not any cors relevant headers in conf.
How can i solve this situation?
(If I'm missing something, you can point it out in the comments.)
The Origin header is only set in cross-origin requests. If you call your backend directly, the Javascript value is undefined, and in this case you must not restrict anything. You could, for example, write
if (!origin || whitelist.has(origin)) {
callback(null, true);
}
Having an issue where my ethers provider is giving me a cors issue for just read only functions. I am not even creating any transactions or anything. I have tried changing nodes and different rpcs but it doesn't help. You can find an image of the errors I get below.
Heres my provider code which I import to all the function pages and heres an example of a function below that. Very basic stuff.
This is a basic react application that is displaying some information about a Binance smart chain token.
import { ethers } from "ethers";
const provider = new ethers.providers.JsonRpcProvider(
"https://speedy-nodes-nyc.moralis.io/1c2baaae7c0d11120337ddc1/bsc/mainnet"
);
export default provider;
Example function:
export const getReflectionLive = async () => {
try {
const tokenContract = new ethers.Contract(tokenAddress, tokenAbi, provider);
let rate = await getDeadRate();
tokenContract.on("Transfer", async (from, to, value, event2) => {
console.log("New Dead Balance addition", (value / 10 ** 9) * 0.05 * rate);
});
} catch (error) {
console.log(`error --> ${error}`);
}
};
All my Error Codes:
Error Codes
×
Unhandled Rejection (Error): could not detect network (event="noNetwork", code=NETWORK_ERROR, version=providers/5.4.4)
Access to fetch at 'https://speedy-nodes-nyc.moralis.io/1c2baaae7c0d11120337ddc1/bsc/mainnet' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
POST https://speedy-nodes-nyc.moralis.io/1c2baaae7c0d11120337ddc1/bsc/mainnet net::ERR_FAILED
×
Unhandled Rejection (Error): missing revert data in call exception (error={"reason":"processing response error","code":"SERVER_ERROR","body":"{\"jsonrpc\":\"2.0\",\"id\":52,\"error\":{\"code\":-32000,\"message\":\"missing trie node 264c5265b90a8eb574898445f88321e330c126156794b45cfec08ede5c80f693 (path )\"}}","error":{"code":-32000},"requestBody":"{\"method\":\"eth_call\",\"params\":[{\"to\":\"0xc748673057861a797275cd8a068abb95a902e8de\",\"data\":\"0x70a08231000000000000000000000000000000000000000000000000000000000000dead\"},\"0xbc4921\"],\"id\":52,\"jsonrpc\":\"2.0\"}","requestMethod":"POST","url":"https://bsc-dataseed.binance.org/"}, data="0x", code=CALL_EXCEPTION, version=providers/5.4.4)
Assume you know what you are doing (security!) - and in case you have an Apache server running. You might add the following to your site configuration in Apache:
Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Methods "DELETE, POST, GET, OPTIONS"
Header set Access-Control-Allow-Headers "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With"
Do not forget to enable Apache's header module.
Background:
Ethers.js requires to have in "cross domain/port/server" operation some access rights in order to work in every supported Browser. This forces that the webserver sends all required and allows everything.
My React App takes user input from frontend and send it to backend via Axios where the info will be used to search through MongoDB. My App works fine for a few input, like around 5-6, then it will stop working with no error and I have no idea what is wrong and how to fix it.
Here is an example of my problem, this useEffect will happen when page load and take param from the url and send it to backend (the param change depends on what link the user clicks on). It will then send the information to backend via axios and the Info is used to search through MongoDB and will dynamically generate the page's text. After the user clicks on around 5 different links tho, this will stop working and the page generated will be stuck on the last page it was generated dynamically although the params in the url still change. I added console.log to see if useEffect was activating or not, and everytime I click on a Link, the console.log will work when the page load. Any lead will help, thanks!
frontend runs on port 3000 and backend runs on 3001
useEffect(() => {
const idInfo = {id};
axios.post('http://localhost:3001/info', idInfo);
console.log("testing")
}, [])
useEffect(() => {
fetch("/info").then(res => {
if(res.ok) {
return res.json();
}
}).then(jsonRes => setGetId(jsonRes))}, [])
This is the backend
router.route("/info").post((req, res) => {
console.log(req.body)
const idInfo = req.body.id;
console.log(idInfo);
current_id = idInfo;
})
router.route("/info").get((req,res) => {
Disease.find({_id: current_id})
.then(foundId => res.json(foundId))
})
This way, when you fetch('/api/info') in development, the development server will recognize that it’s not a static asset, and will proxy your request to http://localhost:3001/api/info as a fallback. The development server will only attempt to send requests without text/html in its Accept header to the proxy.
Conveniently, this avoids CORS issues and error messages like this in development:
Fetch API cannot load http://localhost:3001/api/info. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
https://create-react-app.dev/docs/proxying-api-requests-in-development/
src/setupProxy.js
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function(app) {
app.use(
'/api',
createProxyMiddleware({
target: 'http://localhost:3001',
changeOrigin: true,
})
);
};
useEffect(() => {
const idInfo = {id};
axios.post('/api/info', idInfo);
console.log("testing")
}, [])
useEffect(() => {
fetch("/api/info").then(res => {
if(res.ok) {
return res.json();
}
}).then(jsonRes => setGetId(jsonRes))}, [])