I am trying to set a cookie in the browser for authentication. When using POSTMAN the cookie is returned with no problem, as well as any other response I send. However in the react app, it is failing to fetch. It seems to be complaining about CORS, in particular this line of code being set to : res.header("Access-Control-Allow-Origin", "");
I have an app.js file in which I configure CORS and all of my routes, and my basic fetches work perfectly. When attempting to set a cookie in a route file, it fails to fetch. Here is the app.js file
//packages
var bodyParser = require ('body-parser')
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var cors = require('cors');
var mysql = require('mysql');
//routes
const accountRouter = require('./routes/account');
//express app
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(cors());
app.use(bodyParser.json());
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
//use routes
app.use('/account', accountRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// Tells Express to allows data to be sent across different origins
// required by CORS policy
app.use(function (req, res, next) {
res.header("Access-Control-Allow-Origin", "localhost:3001");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
Here is my route file that checks for an existing email, then verifies the password. After verifying the password I generate the token, and send it as a cookie. Again, in POSTMAN this functions fine.
var express = require('express');
var router = express.Router();
var bcryptjs = require('bcryptjs')
var cookieParser = require('cookie-parser');
var jwt = require('jsonwebtoken');
var pool = require('../database/accountsdb');
const saltRounds = 12;
function authenticate(req,res) {
//Check for existing email
//Create DB Connection
pool.getConnection(function(err, connection){
if(err) {
connection.release();
res.json({"code": 100, "status": "Error in database connection"});
return;
}
//Variables and SQL
let emailSQL ='SELECT intAccountID FROM accountdetails where strEmail = ?';
let email = req.body.email;
connection.query(emailSQL, email, function(err, result){
if(!err){
//check if matching row was found
if(result.length>0){
//Get password
let accountID = result[0].intAccountID;
let accountSQL = "SELECT strPassword, intAccountID FROM accounts WHERE intAccountID = ?";
connection.query(accountSQL, accountID, function(err, result){
if(!err){
//get password
let hashed = result[0].strPassword;
//get ID
let ID = result[0].intAccountID;
//compare passwords
bcryptjs.compare(req.body.password, hashed, function(err, result){
if(!err){
if(result== true){
let payload = {
check: true,
ID: ID
};
//generate token
let token = jwt.sign(payload, process.env.TOKEN_SECRET, {
expiresIn: 1440 //24 hours
});
//TESTING EXPIRE
//TESTING EXPIRE
let expiration= 1000;
//return token cookie
//NEED TO SEND COOKIE !!!!!!!!!!!!!!!!!!!!!
res.cookie('token', token, {
expires: new Date(Date.now() + expiration),
httpOnly: true,
});
}else{
//return failed
res.json({
message: 'Invalid Email or Password'
});
}
}else{
res.json({err: err});
}
})
}else{
//return error
res.json({err:err});
}
})
}else{
//return failed
res.json({
message: 'Invalid Email or Password'
});
}
}else{
connection.release();
res.json({err});
return;
}
connection.release();
})
connection.on('error', function(err){
res.json({"code": 100, "status": "Error in database connection"});
})
})
};
router.post('/authenticate', function(req, res){
authenticate(req, res);
});
module.exports = router;
Here is the react component to validate that a valid email was entered, the password is not empty, and the the API call to POST the data to express for authentication.
// Dependencies
import React, {Component} from 'react';
import validator from 'validator';
class Login extends Component{
constructor(props){
super(props);
this.state = {
email: '',
password:''
}
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChangeEmail = (event) => {
this.setState({email: event.target.value});
}
handleChangePass = (event) => {
this.setState({password: event.target.value});
}
verifyInput(data){
//Validate Email
if(validator.isEmail(data.email) == false){
return false;
}
}
handleSubmit(event){
let data = {
email: this.state.email,
password: this.state.password
}
let isValid= this.verifyInput(data)
if(isValid == false){
alert("Please enter a valid email address")
event.preventDefault();
}else{
//api parameters to register user
const url ='http://localhost:3001/account/authenticate'
const options = {
method:'POST',
headers:{
'Accept': 'application/json',
'Content-Type': 'application/json;charset=UTF-8'
},
body: JSON.stringify(data)
}
//call api
fetch(url, options)
.then(response=> {
console.log(response.headers)
}).catch(error=>console.log(error))
}
}
render (){
return(
<div className="register">
<form onSubmit={this.handleSubmit}>
<table>
<tbody>
<tr>
<td>Email:</td>
<td>
<input type="email" value={this.state.email} onChange={this.handleChangeEmail} />
</td>
</tr>
<tr>
<td>Password:</td>
<td>
<input type="text" value={this.state.password} onChange={this.handleChangePass}></input>
</td>
</tr>
<tr>
<td>
<input disabled={false} type="submit" value="Submit" onSubmit={(e) => this.handleSubmit(e)}/>
</td>
</tr>
</tbody>
</table>
</form>
</div>
)
}
}
export default Login;
What do I do in order to fix this issue? The react client is on localhost:3000 and express on localhost:3001
Try installing http-proxy-middleware using npm or Yarn, then create src/setupProxy.js and place the following contents in it:
const proxy = require('http-proxy-middleware');
module.exports = function(app) {
app.use(
'/api',
proxy({
target: 'http://localhost:3001',
changeOrigin: true,
})
);
};
Related
I have written an API in Nodejs which helps in reset password using Mysql database.It is working fine in Postman.I have called this API in Expo IOS App.But I am getting Network request failed error. Below is the Node js program:
app.js:
var express = require('express');
var path = require('path');
var connection = require('./database.js');
var nodemailer = require('nodemailer');
const bcrypt = require("bcryptjs")
var randtoken = require('rand-token');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var flash = require('express-flash');
var session = require('express-session');
var bodyParser = require('body-parser');
const createError = require('http-errors');
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var app = express();
var connection = require('./database.js');
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({extended: false}))
//send email
function sendEmail(email, token) {
var email = email;
var token = token;
var mail = nodemailer.createTransport({
service: 'gmail',
auth: {
user: 'c***********#gmail.com', // Your email id
pass: '**********' // Your password
}
});
var mailOptions = {
from: 'poornima#abccompany.com',
to: email,
subject: 'Reset Password Link - ABCCompany.com',
html: '<p>You requested for reset password, kindly use this link to reset
your password</p>'
};
mail.sendMail(mailOptions, function(error, info) {
if (error) {
console.log(1)
} else {
console.log(0)
}
});
}
app.post('/reset-password-email', function(req, res, next) {
var email = req.body.email;
console.log('email from api '+email);
connection.query('SELECT * FROM JTGDB.UserInfo WHERE email ="' + email + '"',
function(err, result) {
if (err) throw err;
var type = ''
var msg = ''
console.log(result[0]);
if (result[0].Email.length > 0) {
var token = randtoken.generate(20);
var sent = sendEmail(email, token);
if (sent != '0') {
var data = {
token: token
}
connection.query('UPDATE JTGDB.UserInfo SET ? WHERE email ="' + email + '"', data,
function(err, result) {
if(err) throw err
})
type = 'success';
msg = 'The reset password link has been sent to your email address';
} else {
type = 'error';
msg = 'Something goes to wrong. Please try again';
}
} else {
console.log('2');
type = 'error';
msg = 'The Email is not registered with us';
}
res.redirect('/');
});
})
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(session({
secret: '123458cat',
resave: false,
saveUninitialized: true,
cookie: { maxAge: 60000 }
}))
app.use(flash());
app.use('/', usersRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
app.listen(4000, function () {
console.log('Node app is running on port 4000');
});
module.exports = app;
Below is the IOS App ForgetPassword Screen page:
ForgetPasswordScreen.js:
import React, { Component, Fragment, useState } from "react";
import { View, Text,SafeAreaView, Image, StyleSheet } from "react-native";
import { Formik } from "formik";
import * as Yup from "yup";
import FormInput from "../../components/UI/FormInput";
import FormButton from "../../components/UI/FormButton";
import ErrorMessage from "../../components/UI/ErrorMessage";
import * as authActions from '../../store/actions/auth';
import {useDispatch} from "react-redux";
import Toast from 'react-native-root-toast';
const validationSchema = Yup.object().shape({
email: Yup.string()
.label("Email")
.email("Enter a valid email")
.required("Please enter a registered email"),
});
const ForgotPasswordScreen = props => {
const [isLoading,setIsLoading] = React.useState(false);
const [error, setError] = React.useState('');
const dispatch = useDispatch();
const handlePasswordReset = async (values, actions) => {
const { email } = values;
console.log('email is '+email);
let action
action = authActions.resetpassword(
email
);
setError(null);
setIsLoading(true);
try{
await dispatch(action);
props.navigation.navigate("Login");
} catch (error) {
actions.setFieldError("general", error.message);
}
};
return (
<SafeAreaView>
<View style={styles.emailiconview}>
<Image source={require('../../assets/reset_email.png')} />
</View>
<View style ={styles.instrview1}>
<Text>
Please enter your registered email address below to
</Text>
</View>
<View style={styles.instrview2}>
<Text>receive password reset instruction</Text>
</View>
<View style={styles.emailinputview}>
<Formik
initialValues={{ email: "" }}
onSubmit={(values, actions) => {
handlePasswordReset(values, actions);
}}
validationSchema={validationSchema}
>
{({
handleChange,
values,
handleSubmit,
errors,
isValid,
touched,
handleBlur,
isSubmitting,
}) => (
<Fragment>
<FormInput
name="email"
value={values.email}
onChangeText={handleChange("email")}
placeholder="Enter email"
autoCapitalize="none"
iconName="ios-mail"
iconColor="blue"
onBlur={handleBlur("email")}
/>
<ErrorMessage errorValue={touched.email && errors.email} />
<View style={styles.buttonContainer}>
<FormButton
buttonType="outline"
onPress={handleSubmit}
title="Send Email"
buttonColor="blue"
disabled={!isValid || isSubmitting}
/>
</View>
<ErrorMessage errorValue={errors.general} />
</Fragment>
)}
</Formik>
</View>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
marginTop: 150,
},
text: {
color: "#333",
fontSize: 24,
marginLeft: 25,
},
buttonContainer: {
margin: 25,
},
emailiconview:{
justifyContent:'center',
alignItems:'center',
top:"30%"
},
instrview1:{
top:'40%',
justifyContent:'center',
alignSelf:'center'
}, instrview2:{
top:'45%',
justifyContent:'center',
alignSelf:'center'
},
emailinputview:{
top:'50%'
}
});
export default ForgotPasswordScreen;
Below is the store auth where I fetch API:
auth.js:
import Device from '../../model/Device';
import AsyncStorage from '#react-native-async-storage/async-storage'
export const FORGOTPASSWORD = 'FORGOTPASSWORD';
export const resetpassword=(email) =>{
console.log(email);
return async dispatch =>{
const response = await fetch(
'http://my_IPV4_Address:4000/reset-password-email',
{
method: 'POST',
header: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: email
})
}
);
const resData = await response.text();
console.log(resData);
dispatch({type:FORGOTPASSWORD});
};
}
After running this I am getting the below error in Expo:
Network request failed
It is taking email, but not hitting the api.
email is cpoornima.1987#gmail.com
Email id to API cpoornima.1987#gmail.com
I am getting below error in NodeJs:
email from api undefined
undefined
I have added
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
to the Info.plist. I am just checking in IOS Simulator attached to Expo App. How to get through this?
Below is the auth.js fetch API which is working good.As soon as I press Send Email button, an email is going to provided email id for reset password using real Iphone device.
auth.js:
import Device from '../../model/Device';
import AsyncStorage from '#react-native-async-storage/async-storage'
export const FORGOTPASSWORD = 'FORGOTPASSWORD';
export const resetpassword=(email) =>{
const formData = new FormData();
formData.append('email',email);
console.log('Email id to API '+email);
return async dispatch =>{
fetch('http://My_IPV4_Address:4000/reset-password-email',
{
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body:JSON.stringify({
email: email,
}),
})
.then(res => res.text())
.then(
(signupresult) =>{
}).catch(err =>{
console.log(err);
})
dispatch({type:FORGOTPASSWORD});
};
}
This is my first web dev project. I am basically trying to make a simple website to sell light sabers. The goal is to be able to create an account, login, add, edit and delete light sabers. Be able to add them to cart and checkout. I am using node.js, express, and mongoDB in visual studio code. I am also learning github throughout the process. As of now I have the layout of the website mostly set up. Am able to create accounts and login. I am currently stuck on creating items(light sabers) and saving them in the mongoDB database. Specifically my http methods in the routes/items files are not working, they were working previously in the app.js file but I decided to move everything for organizational purposes. Again, this is my first time working with any of this tech and this project is a work in progress, any input is appreciated. Thanks.
Here is my code: There is a lot of code not being used and its kind of a mess. I know..
app.js file:
const express = require("express");
const session = require("express-session");
var mongoose = require("mongoose");
var passport = require("passport");
var bodyParser = require('body-parser');
var localStrategy = require('passport-local');
var passportLocalMongoose = require("passport-local-mongoose");
var router = express.Router();
const app = express();
const indexRouter = require('./routes/index')
const itemsRouter = require('./routes/items')
//npm install ejs body-parser mongoose passport passport
var uri = "mongodb://username:password#cluster0-shard-00-00-
hnxfk.mongodb.net:27017,cluster0-shard-00-01-
hnxfk.mongodb.net:27017,cluster0-shard-00-02-
hnxfk.mongodb.net:27017/test?
ssl=true&replicaSet=Cluster0-shard-
0&authSource=admin&retryWrites=true&w=majority";
var localHost = "mongodb://localhost:27017/Project2SamJoshEricRoy"
mongoose.connect(uri,{useNewUrlParser: true, useUnifiedTopology:
true });
var User = require('./models/user');
var Item = require('./models/item');
// var app = express();
app.set('view engine', 'ejs');
app.use(bodyParser.urlencoded({extended:true}));
app.use(require('express-session')({
secret: "application secret shhhh",
resave: false,
saveUninitialized: false
}));
passport.use(new localStrategy(User.authenticate()))
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
app.use(express.static(__dirname + '/public'));
app.use(passport.initialize());
app.use(passport.session());
app.use('/', indexRouter)
app.use('/items', itemsRouter)
//create account
app.get("/createAccount",(req,res)=> {
res.render("createAccount");
});
app.get('/createAccount', function(req,res) {
res.render('createAccount');
});
app.post("/createAccount",(req,res)=> {
req.body.username;
req.body.password;
User.register(new
User({username:req.body.username}),req.body.password,
(err,user) =>{
if(err) {
console.log(err);
return res.render("createAccount");
}
passport.authenticate("local")(req,res,() => {
res.redirect('/login');
console.log(req.body.username
+ " " + req.body.password);
console.log(user);
});
});
});
//login
app.get("/login",(req,res) => {
res.render("login")
User.find(function(err,users) {
if(err) {
console.log(err);
}
// console.log(users);
})
});
app.post('/login',passport.authenticate("local",{
successRedirect: "/lightsabers",
failureRedirect: "/login"
}),(req,res)=> {
});
function isLoggedIn(req,res,next) {
console.log("CALL")
if(req.isAuthenticated()) {
console.log("logged in ");
return next();
}
res.redirect("/login");
console.log("error logging in");
}
//logout
app.get("/logout",(req,res)=> {
req.logout();
res.redirect('/');
});
//lightsabers
//app.get("/lightsabers",isLoggedIn,(req,res)=> {
//res.render("lightsabers",{user: req.user});
//console.log(req.user);
//});
//shopping cart
app.get("/cart",(req,res)=> {
res.render("cart");
});
app.get('/createLightsaber',(req,res)=> {
res.render("newItem",{user:req.User});
console.log("user is with us");
console.log(req.user)
});
app.get('/updateItem',(req,res)=> {
res.render("updateLightsaber");
});
app.get('/deleteLightsaber',(req,res)=> {
res.render("updateLightsaber");
});
/routes/index.js file:
const express = require('express')
const router = express.Router()
router.get('/', (req, res) => {
res.render('index')
})
module.exports = router
routes/items.js
const express = require('express')
const router = express.Router()
const Item = require('../models/item')
//display page
router.get("/",(req,res)=> {
res.render("newItem");
});
// all items route
router.get('/', async (req, res) =>{
try{
const items = await Item.find({})
res.render('items/newItem', {item: new Item() })
} catch {
res.redirect('/')
}
})
//new item route (display form)
router.get('/newItem', (req, res) => {
res.render('items/newItem', {item: new Item() })
})
// create new item route (actual creation)
router.post('/newItem', async (req,res) => {
const item = new Item({
color:req.body.color,
price:req.body.price,
link:req.body.link
})
try{
const newItem = await item.save()
res.redirect('lightsabers')
} catch {
res.render('items/newItem', {
item: item,
errorMessage:'Error creating item'
})
}
})
/update item
app.get("/updateItem",(req,res)=> {
res.render("updateItem");
});
module.exports = router
newItem.ejs
<!DOCTYPE html>
<html>
<head>
<title> </title>
<link rel="stylesheet" href="/css/styles.css"/>
</head>
<body>
<h1> Adding New Item </h1>
<form action="/items" method="POST">
<h2>
Color: <input type="text" name="color"><br>
Price: <input type="text" name="price"><br>
Image Link:<input type="text" name="link"><br>
<br><br>
<button id="addItem">Add Item</button>
<%= typeof(message) != "undefined"?message:"" %>
</h2>
</form>
<form action="/lightsabers">
<h2><button> Back </button></h2>
</form>
<script type="text/javascript" src="../public/app.js">
</script>
</body>
</html>
If you wish to see any other parts of the code let me know. I may be forgetting something important.
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.
Update:
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8080');
With:
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000');
I encourage you to do app.use(cors()); before those lines
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');
...
Express stacks those functions lets say as a "array" and because of that the order matters
Hope it resolve your problem.
Bit late to the party but just going to leave my answer here incase someone else has the same problem.
The idea is to allow CORS request to your Express server. Go to the directory with your server.js file and run:
npm install cors
Then inside server.js add the following lines:
const cors = require('cors');
app.use(cors());
I trying to understand what is the wrong with my credetrials inside my server code, because when I make Login from the client side, the credetrials does not recorgonizes inside the router.get('/auth/login') method, but when I do the same login action by the Postman with POST method and then reload the page with GET method the credetrials recorgonizes normally and I got the message, that I already loggined.
So, I actually want to say, that I do not have any problem with loginnig from the client side by "POST" method for router.post('/auth/login'), as for logs - user info transmits normally and credentials also creates, but on router.get('/auth/login') it does not callbacks in the res.json({session: req.session.userId, session2: req.session, log: 'You already logined!'});, where I gets just empty string...
My server code:
'use strict'
const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');
const bcrypt = require('bcrypt-nodejs');
const session = require('express-session');
const MongoStore = require('connect-mongo')(session);
const app = express();
const router = express.Router();
const EmployersSchemaDB = require('./SchemaDB/EmployersSchemaDB');
const User = require('./SchemaDB/ShemaAuthtificaion');
mongoose.connect('mongodb://myDB');
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(cookieParser());
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers');
res.setHeader('Cache-Control', 'no-cache');
next();
});
app.use(session({
secret: 'work hard',
resave: true,
saveUninitialized: false,
store: new MongoStore({ mongooseConnection: mongoose.connection })
}));
router.get('/', (req, res) => {
res.json('Hello on Homepage!');
});
router
.get('/auth/login', (req, res) => {
User.find((err, users) => {
if (err) { res.send(err) }
if (req.session.userId !== undefined) { // there is where I always falls...
res.json({session: req.session.userId, session2: req.session, log: 'You already logined!'});
} else {
console.log('User credentials!:', req.session.userId);
res.json({session: req.session.userId, session2: req.session});
console.log('---===--- \n Employers showed!: \n', users + '\n ---===---');
}
});
})
.post('/auth/login', (req, res, next) => {
if (req.body.password !== req.body.passwordConf) {
var err = new Error('Passwords do not match.');
err.status = 400;
res.send("passwords dont match");
return next(err);
}
if (req.body.email &&
req.body.username &&
req.body.password &&
req.body.passwordConf) {
let user = new User();
user.email = req.body.email;
user.username = req.body.username;
user.password = req.body.password;
user.passwordConf = req.body.passwordConf;
user.save((err) => {
if (err) { res.send(err) }
res.json({ message: 'Comment successfully added!', user });
console.log('---===--- \n Employer added: \n', user + '\n ---===---');
});
} else if (req.body.logemail && req.body.logpassword) {
User.authenticate(req.body.logemail, req.body.logpassword, function (error, user) {
if (error || !user) {
var err = new Error('Wrong email or password.');
err.status = 401;
return next(err);
} else { // this is the where credentials are creates. All work good!
req.session.userId = user._id;
console.log('Signed Cookies: ', req.session.userId, req.signedCookies)
console.log('User logined and redirected!');
return res.redirect('/employers');
}
});
} else {
var err = new Error('All fields required.');
err.status = 400;
return next(err);
}
});
app.use('/', router);
const port = process.env.API_PORT || 3016;
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
My client code (just for info) : UPDATED FOR KEVIN
import React, { Component } from 'react';
import { Redirect } from 'react-router-dom';
import axios from 'axios';
class LoginPage extends Component {
constructor(props) {
super(props);
this.state = {
navigate: false
}
}
handleSubmit = (e) => {
e.preventDefault();
let userLogin = {
logemail: e.target.email.value,
logpassword: e.target.password.value
}
axios.post('http://localhost:3016/auth/login', userLogin)
.then(function(){
axios.get('http://localhost:3016/auth/login', {withCredentials: true})
.then(res => console.log(res.data))
.catch(err => console.log(err));
this.setState({
navigate: true
})
})
.catch(err => {
console.error(err);
});
};
render() {
// if (this.state.navigate) {
// return <Redirect to="/employers" />
// }
return (
<form onSubmit={this.handleSubmit}>
<div className="form-group">
<label htmlFor="exampleInputEmail1">Email address</label>
<input name="email" type="email" className="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" placeholder="Enter email" />
<small id="emailHelp" className="form-text text-muted">We'll never share your email with anyone else.</small>
</div>
<div className="form-group">
<label htmlFor="exampleInputPassword1">Password</label>
<input name="password" type="password" className="form-control" id="exampleInputPassword1" placeholder="Password" />
</div>
<div className="form-check">
<input type="checkbox" className="form-check-input" id="exampleCheck1" />
<label className="form-check-label" htmlFor="exampleCheck1">Check me out</label>
</div>
<button type="submit" className="btn btn-primary">Submit</button>
</form>
)
}
}
export default LoginPage;
This may be a cross domain problem as you can login successfully with Postman.
AJAX calls will not send cookies in CORS environment by default. You need to set the withCredentials field of XMLHttpRequest object as true to send cookies even cross domain. Here is the official document.
As you are using axios, hope this setting would be helpful.
axios.defaults.withCredentials = true;
Update:
Please update your client code and make sure GET /auth/login are requested after POST /auth/login are completed, otherwise the Get request might be sent while POST request has not complete authenticate and set session. I recommend you change your code like following (put Get request after POST request are resolved):
axios.post('http://localhost:3016/auth/login', userLogin)
.then(function(){
axios.get('http://localhost:3016/auth/login').then(res => console.log(res.data).catch(e => console.log(e));
this.setState({
navigate: true
})
})
.catch(err => {
console.error(err);
});
```
In my index.js file, each form action called executes properly:
routes/index.js
var express = require('express');
var async = require('async');
var common = require('./common.js');
var log = require('./log.js');
var router = express.Router();
router.post('/premise', function...
router.post('/follow', function...
views/index.jade
extends layout
block js
script(src='/javascripts/countdown.js')
script(src='/javascripts/table_filter.js')
block content
if username...
...div.followers.table_wrapper
h3.table_header.center Following
#followers_tbl.center
h4.center You are not following anyone...
form(action="follow" method="post")
input(type="text" name="follow_username" id="follow_username" placeholder="Add username here...")
button(class="btn btn-primary btn-sml" type="submit" style="margin-bottom: 15px") Follow A User
div#add_premise.textarea_wrapper
form(action="premise" name="premise" method="post")
textarea(name="premise" id="premise" placeholder="Enter your..." required)
p.countdown.black
button(class="btn btn-success" type="submit") Add Premise
But when I call from another page /signup it doesn't work. (See below EDIT file details).
The call returns a 302, so it is found, but the function doesn't execute. But when I remove action='signup/signup/' from the form in the view, then change the router.post('/signup', function... to router.post('/', function..., the function executes, after also returning a 302 in my log.
Can anyone help me? Thanks so much!
EDIT: (Adding app.js and more of routes/signup.js)
app.js
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var session = require('express-session');
var fileStore = require('session-file-store')(session);
var index = require('./routes/index.js');
var signup = require('./routes/signup.js');
var login = require('./routes/login.js');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
var session_options = {
name: 'server-session-cookie-id',
secret: 'secret',
saveUninitialized: true,
resave: true,
store: new fileStore(),
cookie: {
maxAge: 1000 * 60 * 60 * 24 * 365 // 1 year
}
};
// uncomment after placing your favicon in /public
app.use(favicon(path.join(__dirname, 'public', 'images', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(session( session_options ));
app.use( function( req, res, next ) {
try {
if ( ( req.session && req.session.username ) ||
req.originalUrl == "/login" ||
req.originalUrl == "/signup" {
next();
}
else {
res.redirect('/login');
}
}
catch( err ) {
common.send_500( req, res, err );
}
});
app.use('/', index);
app.use('/signup', signup);
app.use('/login', login);
app.get('/logout', function(req, res, next) {
try {
req.session.destroy();
res.clearCookie(session_options.name);
res.redirect('/');
}
catch( err ) {
common.send_500( req, res, err );
}
});
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handlers
// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: err
});
});
}
// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: {}
});
});
module.exports = app;
routes/signup.js
var express = require('express');
var router = express.Router();
var encrypt = require('./encrypt.js');
var common = require('./common.js');
var log = require('./log.js');
/* GET signup page. */
router.get('/', function(req, res, next) {
try {
var signup_error = req.session.signup_error;
req.session.signup_error = null;
res.render('signup', { title: 'Signup', signup_error: signup_error });
}
catch( err ) {
common.send_500( req, res, err );
}
});
router.post('/normal', function(req, res, next) {...});
router.post('/submit', function(req, res, next) {...});
module.exports = router;
views/signup.jade
extends layout
block js
script(src='/javascripts/dob_validate.js')
block content
div#signup_form
form(action="/signup/normal" name="signup" method="post")
input(type="email" name="email" id="email" placeholder="Email" required autofocus)
input(type="text" name="username" id="username" placeholder="Username" required)
input(type="password" name="password1" id="password1" placeholder="Password" required)
input(type="password" name="password2" id="password2" placeholder="Confirm Password" required)
p#dob
| Birthdate
input(type="date" name="birthdate" id="birthdate" style="margin-left=20px;" required).pull-right
label
input(style="margin-right: 5px;" type="checkbox" name="checkbox" required).pull-left
| Check here to indicate that you have read, understand and agree to the
a(href="/terms") Terms of Use,
a(href="/privacy") Privacy Policy
| and
a(href="/cookies") Cookie Policy
| .
div#signup_btns
button(class="btn btn-primary" type="submit") Sign Up
a(href='/login') Login
/signup/normal/ handler:
router.post('/normal', function(req, res, next) {
try {
var email = req.body.email;
var username = req.body.username.toUpperCase();
var password1 = req.body.password1;
var password2 = req.body.password2;
var birthdate = req.body.birthdate;
birthdate = birthdate.substring(5,7) + '/' +
birthdate.substring(8,10) + '/' +
birthdate.substring(0,4);
if (password1 === password2) {
var body = JSON.stringify({
"username" : username,
"password_hash" : encrypt.hash(password1),
"credits" : 100,
"email_address" : email,
"birthdate" : birthdate
});
var path = "/accounts/add/";
common.post( path, body, function( data ) {
try {
if ( data && !data.error ) {
console.log(JSON.parse(data.resp));
res.redirect('/login');
}
else {
req.session.signup_error = data.resp;
req.session.save( function(err) {
res.redirect('/signup');
});
}
}
catch( err ) {
common.send_500( req, res, err );
}
});
}
else {
req.session.signup_error = "Passwords do not match";
req.session.save( function(err) {
res.redirect('/signup');
});
}
}
catch( err ) {
common.send_500( req, res, err );
}
});
This is normal because your are using signup.js router with /signup end point:
const signup = require('./routes/signup.js')
...
app.use('/signup', signup)
Assuming that you define a route handler inside signup.js to /about route:
router.post('/about', function(req, res, next) { ... });
Then the app will be able to handle requests to /signup/about. If you create a route handler for /signup then the end point is /signup/signup
I suggest you to change the /signup route to /user like this: app.use('/user', signup) and also change the form action to action="/user/signup" this makes more sense
You have a mixup here between GET requests and POST requests, and which is on what endpoint.
If you're hitting localhost:3000/signup, then that endpoint should be returning your signup.jade file (by the way this is a server-side html rendering you're using....). You're serving dynamically generated HTML content at that endpoint.
However, By typing in localhost:3000/signup within your browser address bar, you're executing an HTTP GET request, not a POST. Very important difference here!
Thus, you now need a router endpoint of router.post('/signup/signup', function...) to actually catch the form submit (note this is now a POST) from your jade template. Honestly I would want to point out your naming scheme here, because as you can see /signup/signup is quite confusing and doesn't follow a very good convention.