My app was working fine and I was able to fetch data between my Express and React servers. I reorganised my code and now i cannot get rid of the CORS errors and cannot fetch any data at all. I cannot move on with my project and simply can't figure it out for myself, I have really tried.
The front end works ok until i try to login, and then the authentication fails
I have tried adding headers and have installed CORS to my express app. I have a proxy specified in my react package.json to the express URL.
- This is my Express server.js
const express = require('express');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const path = require('path');
const cors = require('cors');
const methodOverride = require('method-override');
const db = require('./db/index.js')
db.on('error', console.error.bind(console, 'MongoDB connection error:'))
require('dotenv').config();
const app = express();
app.disable('x-powered-by');
app.use(function(req, res, next) {
// Website you wish to allow to connect
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8080');
// Request methods you wish to allow
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
// Request headers you wish to allow
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
// Set to true if you need the website to include cookies in the requests sent
// to the API (e.g. in case you use sessions)
res.setHeader('Access-Control-Allow-Credentials', true);
// Pass to next layer of middleware
next();
});
app.use(methodOverride('_method'));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(cookieParser());
app.use(cors({
origin: 'http://localhost:8080'
}));
app.use(express.static(path.join(__dirname, '../build')));
app.get('/', function (req, res) {
res.sendFile(path.join(__dirname, '../build', 'index.html'));
});
const userRouter = require('./routes/user-routes')
app.use('/api', userRouter)
const fileRouter = require('./routes/file-routes')
app.use('/file', fileRouter)
// mongoose.connection.once('open', run);
app.listen(process.env.PORT || 8080);
console.log('Server is listening on port ' + process.env.PORT);
- This is my user controller
const User = require('../models/Users');
const secretShh = 'mysecretsshhh';
const jwt = require('jsonwebtoken');
const home = (req, res) => {
res.send('Welcome!');
};
const secret = (req, res) => {
res.send('The password is potato');
};
const register = (req, res) => {
const { email, password } = req.body;
const user = new User({ email, password });
user.save(function(err) {
if (err) {
console.log(err);
res.status(500).send("Error registering new user please try again.");
} else {
res.status(200).send("Welcome to the club!");
}
});
};
const authenticate = (req, res) => {
const { email, password } = req.body;
User.findOne({ email }, function(err, user) {
if (err) {
console.error(err);
res.status(500)
.json({
error: 'Internal error please try again'
});
} else if (!user) {
res.status(401)
.json({
error: 'Incorrect email or password'
});
} else {
user.isCorrectPassword(password, function(err, same) {
if (err) {
res.status(500)
.json({
error: 'Internal error please try again'
});
} else if (!same) {
res.status(401)
.json({
error: 'Incorrect email or password'
});
} else {
// Issue token
const payload = { email };
const token = jwt.sign(payload, secretShh, {
expiresIn: '1h'
});
res.cookie('token', token, { httpOnly: true }).sendStatus(200);
}
});
}
});
};
const token = (req, res) => {
res.sendStatus(200);
};
module.exports = {
home,
secret,
register,
authenticate,
token
}
- this is my user routes
const express = require('express')
const UserCtrl = require('../controllers/user-ctrl')
const withAuth = require('../middleware');
const router = express.Router()
router.get('/home', UserCtrl.home)
router.get('/secret', withAuth, UserCtrl.secret)
router.post('/register', UserCtrl.register)
router.post('/authenticate', UserCtrl.authenticate)
router.get('/checktoken', withAuth, UserCtrl.token)
module.exports = router
- this is a middleware function to check tokens, this is where the error seems to point towards, but I'm sure its actually something to do with the proxy and fetch being blocked by CORS.
const jwt = require('jsonwebtoken');
const secret = 'mysecretsshhh';
const withAuth = (req, res, next) => {
const token =
req.body.token ||
req.query.token ||
req.headers['x-access-token'] ||
req.cookies.token;
if (!token) {
res.status(401).send('Unauthorized: No token provided');
} else {
jwt.verify(token, secret, function(err, decoded) {
if (err) {
res.status(401).send('Unauthorized: Invalid token');
} else {
req.email = decoded.email;
next();
}
});
}
}
module.exports = withAuth;
- this is my auth components where the error is also pointing towards
import React, { Component } from 'react';
import { Redirect } from 'react-router-dom';
//withAuth is a high-order component which takes in a component to protect
export default function withAuth(ComponentToProtect) {
return class extends Component {
constructor() {
super();
this.state = {
loading: true,
redirect: false,
};
}
async componentDidMount() {
fetch('http://localhost:8080/api/checktoken', {
credentials: 'include',
mode: 'cors'
})
.then(res => {
if (res.status === 200) {
this.setState({ loading: false });
} else {
const error = new Error(res.error);
throw error;
}
})
.catch(err => {
console.error(err);
this.setState({ loading: false, redirect: true });
});
}
render() {
const { loading, redirect } = this.state;
if (loading) {
return null;
}
if (redirect) {
return <Redirect to="/login" />;
}
return (
<React.Fragment>
<ComponentToProtect {...this.props} />
</React.Fragment>
);
}
}
}
- login component
import React, { Component } from 'react';
export default class Login extends Component { //impplicit vs explicit returns
constructor(props) {
super(props)
this.state = {
email : '',
password: ''
};
}
handleInputChange = (event) => {
const { value, name } = event.target;
this.setState({
[name]: value
});
}
onSubmit = async (event) => {
event.preventDefault();
fetch('/api/authenticate', {
method: 'POST',
body: JSON.stringify(this.state),
headers: {
'Content-Type': 'application/json'
}
})
.then(res => {
if (res.status === 200) {
this.props.history.push('/');
} else {
const error = new Error(res.error);
throw error;
}
})
.catch(err => {
console.error(err);
alert('Error logging in please try again');
});
}
render() {
return (
<form onSubmit={this.onSubmit}>
<h1>Login Below!</h1>
<input
type="email"
name="email"
placeholder="Enter email"
value={this.state.username}
onChange={this.handleInputChange}
required
/>
<input
type="password"
name="password"
placeholder="Enter password"
value={this.state.password}
onChange={this.handleInputChange}
required
/>
<input type="submit" value="Submit"/>
</form>
);
}
}
This is the main error:
Access to fetch at 'http://localhost:8080/api/checktoken' from origin 'http://localhost:3000' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header has a value 'http://localhost:8080' that is not equal to the supplied origin. Have the server send the header with a valid value, or, if an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
It also says:
withAuth.jsx:17 GET http://localhost:8080/api/checktoken net::ERR_ABORTED 401 (Unauthorized)
In my express app the terminal says it cant read the token in the middleware, I presume its also due to cors :
TypeError: Cannot read property 'token' of undefined at withAuth (/Users/nancycollins/virtuload-beta/backend/middleware.js:6:16)
Apologies if this is too much information I've just been stuck on this for too long and really dont know what else to do.