React Client Side is Unable to authenticate using passport - node.js

So I'm building a MERN app that is supposed to be authenticating using passport. I've created a login page, a register page and a upload route on the frontend, along with a backend server in node js, mongo db and react. I'm using the passport, passport-local, and passport-local-mongoose npm packages to help with session based authentication.
So when I login using my login page, my server does return the success message and I am redirected correctly. When I try to upload, it turns out that the request can't get past my isLoggedIn middleware. But when I try the same with hoppscotch(which runs using the hoppscotch browser extenxsion), it logs in, and both, the upload requests from my upload page and the upload requests from hoppscotch succeed easily and everything works correctly.
Ohkay so here's my code for my login page:
import axios from "axios";
import React, { useState } from "react";
import "./login.css";
const Login = (props) => {
const [username, setUsername] = useState();
const [password, setPassword] = useState();
const onSubmit = (e) => {
e.preventDefault();
const obj = { username: username, password: password };
axios
.post("http://localhost:4000/api/login", obj)
.then((res) => {
console.log("login response: ", res);
if(res.status === 200) {
alert("Logged in!!!");
// window.location.href = "/";
}
})
.catch((e) => {
alert("error");
console.log(e);
});
};
return (
<div className="cont">
<form className="input" onSubmit={onSubmit}>
<label className="label" htmlFor="username">Username:</label>
<input
className="input"
type="text"
id="username"
onChange={(e) => setUsername(e.target.value)}
/>
<label className="label" htmlFor="password">Password:</label>
<input
className="input"
type="password"
id="password"
onChange={(e) => setPassword(e.target.value)}
/>
<button className="button" type="submit" value="submit">
Submit
</button>
</form>
</div>
);
};
export default Login;
And this would be my upload page:
import React, { useState } from "react";
import axios from "axios";
const Home = () => {
const [file, setFile] = useState();
const onSubmit = (e) => {
e.preventDefault();
const data = new FormData();
data.append("file", file);
axios
.post("http://localhost:4000/api/upload/resume", data, {
withCredentials: true,
headers: { "Content-Type": "multipart/form-data" },
})
.then((file) => {
console.log("worked ", file);
})
.catch((e) => {
console.log(e);
});
};
// post to http://localhost:4000/home
return (
<form onSubmit={onSubmit}>
<p>Submit your resume pdf here ( Resume pdf should be 12 Mb or less )</p>
<input
type="file"
id="pdf-file"
onChange={(e) => setFile(e.target.files[0])}
/>
<button type="submit">Upload</button>
</form>
);
};
export default Home;
this is my login route:
router.post("/login", (req, res, next) => {
passport.authenticate("local", (err, user, info) => {
if (err) throw err;
if (!user) res.send("No User Exists");
else {
req.logIn(user, (err) => {
if (err) throw err;
res.send("Successfully Authenticated");
console.log(req.user);
});
}
})(req, res, next);
});
And finally this is my isLoggedIn Middleware:
const isLoggedIn = (req, res, next) => {
console.log(req);
if (req.isAuthenticated()) {
console.log("authenticated");
return next();
}
console.log("not authenticated");
throw new Error("User not authenticated");
};
module.exports = isLoggedIn;

Related

make login page with post request

I want to make a login page. But the problem is that even though the data I entered is wrong, it redirects to the page. how can i fix this?
I only have email and password information in my table
const client = require('../../db')
const express = require('express');
const app = express();
const cors = require("cors");
app.use(cors());
app.use(express.json()); //req.body
app.listen(2121, ()=>{
console.log("Sever is now listening at port 5000");
})
client.connect();
app.post("/login", async (req, res) => {
try {
const { email, password } = req.body;
const user = await client.query(
`SELECT * FROM users WHERE email=$1 AND password=$2`,
[email, password]
);
if (user.rows.length === 0) {
res.send("Kullanıcı adı veya şifre yanlış");
} else {
res.send("Kullanıcı adı veya şifre doğru");// Eşleşen kullanıcı bilgileri varsa diğer sayfaya yönlendir
}
} catch (err) {
console.error(err.message);
}
});
this is my database code.when i run my database query in postman it works correctly
import React, { useState } from 'react'
import Navbar from '../../common/Navbar/Navbar';
const User = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [user, setUser] = useState([]);
const [error, setError] = useState('');
const onSubmitForm = async e => {
e.preventDefault();
try {
const response = await fetch(`http://localhost:2120/login`,{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
});
if (response.ok) {
window.location.replace(`/AdminPage.js`);
} else {
setError('Invalid email or password');
}
} catch (err) {
console.error(error);
setError('An error occurred. Please try again later.');
}
};
return (
<>
<Navbar/>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap#4.4.1/dist/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous"></link>
<div className="container text-center">
<h1 className="my-5">Search for the Dormitory You Want</h1>
<form className="d-flex" onSubmit={onSubmitForm}>
<input
type="text"
name="name"
placeholder="Enter email ..."
className="form-control"
value={email}
onChange={e => setEmail(e.target.value)}
/>
<input
type="text"
name="name"
placeholder="Enter password ..."
className="form-control"
value={password}
onChange={e => setPassword(e.target.value)}
/>
<button className="btn btn-success">Submit</button>
</form>
</div>
</>
)
}
export default User
this is my login page code.
The issue is related backend side since when you are sending the wrong login and password, you are still responding as a 200 ok. By default the HTTP status code sent in a response from an Express application is 200 (OK). Instead of that, you can just throw an error message with 404 status. In addition, if there is another issue that is not related correction of credentials you may to response 500 status code as an internal error. I have added inside catch.
app.post("/login", async (req, res) => {
try {
const { email, password } = req.body;
const user = await client.query(
`SELECT * FROM users WHERE email=$1 AND password=$2`,
[email, password]
);
if (user.rows.length === 0) {
res.status(404).send("Kullanıcı adı veya şifre yanlış");
} else {
res.send("Kullanıcı adı veya şifre doğru");// Eşleşen kullanıcı bilgileri varsa diğer sayfaya yönlendir
}
} catch (err) {
response
.status(500)
.json({ message: "Error in invocation of API: /login" })
}
});

getting undefined of an array of errors

I am having an issue with my React Redux and Node app. I am trying to Log in an existing user but in my console i am getting the following error:
Uncaught (in promise) TypeError: error.response.data is undefined
The console points to the following block of code:
export const login = (email, password) => async (dispatch) => {
const body = { email, password };
try {
const res = await axios.post('http://localhost:5000/api/auth', body);
dispatch({
type: LOGIN_SUCCESS,
payload: res.data
});
dispatch(loadUSer());
} catch (err) {
const errors = err.response.data.errors;
if (errors) {
errors.forEach((error) => dispatch(setAlert(error.msg, 'danger')));
}
dispatch({
type: LOGIN_FAIL
});
}
};
This is my server side for user auth auth.js:
const express = require('express');
const router = express.Router();
const User = require('../../models/User');
const auth = require('../../middleware/auth');
const config = require('config');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const { check , validationResult } = require('express-validator/');
//#route GET api/auth
//#desc Test route
//#access public
router.get('/',auth, async(req, res)=> {
try{
const user = await User.findById(req.user.id).select('-password');
res.json(user);
}catch(err){
console.error(err.message);
res.status(500).send('Server Error');
}
});
//#route POST api/auth
//#desc Authenticate user & get token
//#access public
router.post('/', [
check('email', 'Plese include a valid email').isEmail(),
check('password', 'Password is required').exists()
],
async (req, res)=> {
const errors = validationResult(req);
if(!errors.isEmpty()){
return res.status(400).json({ errors:errors.array()}); //400 is for bad requests
}
const { email, password } = req.body;
try{
//See if user exists
let user = await User.findOne({ email });
if(!user){
return res.status(400).json({ errors: [{ msg:'Invalid credentials' }] });
}
//Compare the input password, plane text, to the encrypted password.
const isMatch = await bcrypt.compare(password, user.password);
if(!isMatch){
return res.status(400).json({ errors: [{ msg:'Invalid credentials' }] });
}
//Return jsonwebtoken -> this for users to be logged in right after registration
const payload = {
user:{
id: user.id
}
}
jwt.sign(
payload,
config.get('jwtSecret'),
{expiresIn: 360000}, //change to 3600 for production
(err, token)=>{
if(err) throw err;
res.json({ token });
}
)
}catch(err){
console.error(err.message);
res.status(500).send('Server Error');
}
});
module.exports = router;
And this is my Login component:
import React, { Fragment, useState } from 'react'
/* import axios, { Axios } from 'axios'; */
import { Link, Navigate } from 'react-router-dom'
import { connect } from 'react-redux';
import { PropTypes } from 'prop-types';
import { login } from '../../actions/auth';
const Login = ({ login, isAuthenticated }) => {
const [formData, setFormData] = useState({
email: '',
password: ''
});
const { email, password } = formData;
const onChange = e => setFormData({
...formData, [e.target.name]:e.target.value
});
const onSubmit = async e => {
e.preventDefault();
login(email, password);
}
//Redirect if logged in
if(isAuthenticated){
return <Navigate to="/dashboard"/>;
}
return (
<Fragment>
<section className='container'>
<h1 className="large text-primary">Sign Up</h1>
<p className="lead"><i className="fas fa-user"></i> Sign Into Your Account</p>
<form className="form" action="create-profile.html" onSubmit={e => onSubmit(e)}>
<div className="form-group">
<input type="email"
placeholder="Email Address"
name="email" value={email}
onChange={e => onChange(e)}
required/>
</div>
<div className="form-group">
<input
type="password"
placeholder="Password"
name="password"
minLength="6"
value={password}
onChange={e => onChange(e)}
required
/>
</div>
<input type="submit" className="btn btn-primary" value="Login" />
</form>
<p className="my-1">
Don´t have an account? <Link to="/register">Sign up</Link>
</p>
</section>
</Fragment>
)
}
Login.propTypes = {
login: PropTypes.func.isRequired,
isAuthenticated: PropTypes.bool
}
const mappedStateToProps = state => ({
isAuthenticated: state.auth.isAuthenticated
})
export default connect(mappedStateToProps , { login })(Login)
For some reason there are sometimes i am able to login and sometimes i encounter this issue but i cannot figure out what am i doing wrong.
My redux devtools also show the AUTH_ERROR action type:
This is my auth.js in my actions directory.
export const loadUSer = () => async dispatch => {
if(localStorage.token){
setAuthToken(localStorage.token);
}
try {
const res = await axios.get('http://localhost:5000/api/auth');
dispatch({
type: USER_LOADED,
payload: res.data
})
} catch (error) {
dispatch({
type:AUTH_ERROR
})
}
}
auth.js (reducers directory):
import{
REGISTER_FAIL,
REGISTER_SUCCESS,
USER_LOADED,
AUTH_ERROR,
LOGIN_SUCCESS,
LOGIN_FAIL,
LOGOUT
} from '../actions/types'
const initialState = {
token: localStorage.getItem('token'),
isAuthenticated: null,
loading: true,
user: null
}
function authReducer(state = initialState, action){
const { type, payload } = action
switch (type) {
case USER_LOADED:
return {
...state,
isAuthenticated:true,
loading: false,
user:payload
}
case LOGIN_SUCCESS:
case REGISTER_SUCCESS:
localStorage.setItem('token', payload.token);
return {
...state,
...payload,
isAuthenticated: true,
loading: false
}
case LOGOUT:
case LOGIN_FAIL:
case REGISTER_FAIL:
case AUTH_ERROR:
localStorage.removeItem('token');
return {
...state,
toke: null,
isAuthenticated: false,
loading: false
}
default:
return state;
}
}
export default authReducer
So i first log in with an user everything works fine, i logout and log in with a different user and also works, i now logout and want to login to the first user again and the error shows up. The only difference between both users is that one has a profile and the other one doesn´t.
When i try to log in with the user with no profile my app crashes and my vscode terminal shows a different errors:
Can't set headers after they are sent to the client

How can I perform update method on a mongodb database?

I have my update button that performs a get request to fetch the data with the id and fills the slots. Then, using the form below I perform another get request to send a request to update the data.
<form action="//updateBookRequest" method="GET" id="update-book">
<!-- Book Name -->
<div class="form-group">
<label for="name">Name</label>
<input type="hidden" name="id" value="<%= book._id %> ">
<input type="text" name="name" value="<%= book.name %> " placeholder="The Alchemist">
</div>
<!-- Author Name -->
<div class="form-group">
<label for="author">Author</label>
<input type="text" name="author" value="<%= book.author %> " placeholder="Paulo Coelho">
</div>
<!-- Language -->
<div class="form-group">
<label for="language">Language</label>
<input type="text" name="language" value="<%= book.language %> " placeholder="English">
</div>
<!-- Date -->
<div class="form-group">
<label for="date">Date</label>
<input type="date" name="date" value="<%= book.date %> " placeholder="">
</div>
<div class="form-group">
<div class="buttons">
<button class="btn">Cancel</button>
<button class="btn" type="submit">Save</button>
</div>
</div>
</form>
The route looks like this:
route.get('/update-book', services.updateBook)
And the services.updateBook looks like this:
exports.updateBook = (req, res) => {
axios.put(`http://localhost:5000/api/books/${req.query.id}`)
.then(function(response) {
res.redirect('/')
})
.catch(err => {
res.send(err)
})
}
But for some reason, it doesn't update the data.
When I do it in the postman, using the same 'http://localhost:5000/api/books/id', it works just fine and update the data with the postman body as it should.
But, it doesn't when I try to do it in the ejs file with the following section.
I can't seem to find where the problem is. Other functionalities such as adding new data, deleting the data using the id, displaying all the data seem to work fine.
How can I make it to update the data?
This is server.js file:
const express = require('express')
const dotenv = require('dotenv')
const bodyparser = require('body-parser')
const path = require('path')
const connnectDB = require('./server/database/connection')
const app = express()
dotenv.config({path:'config.env'})
const port = process.env.port || 5050
connnectDB()
app.use(bodyparser.urlencoded({extended:true}))
app.set('view engine', 'ejs')
app.use('/css', express.static(path.resolve(__dirname, 'assets/css')))
app.use('/js', express.static(path.resolve(__dirname, 'assets/js')))
app.use('/', require('./server/routes/router'))
app.listen(port, () => {
console.log(`http://localhost:${port}`)
})
This is the server/controller/controller.js
const Userdb = require('../model/model')
exports.create = (req, res) => {
if (!req.body) {
res.status(400).send({message: 'content can not be empty'})
return
}
const user = new Userdb({
name: req.body.name,
author: req.body.author,
language: req.body.language,
date: req.body.date,
description: req.body.description
})
user
.save(user)
.then(data => {
// res.send(data)
res.redirect('/')
})
.catch(err => {
res.status(500).send({
message: err.message || 'some error occured'
})
})
}
exports.find = (req, res) => {
if (req.query.id) {
const id = req.query.id
Userdb.findById(id)
.then(data => {
if (!data) {
res.status(404).send({message: `error ${id}`})
} else {
res.send(data)
}
})
.catch(err => {
res.status(500).send({message: `error ${id}`})
})
} else {
Userdb.find()
.then(user => {
res.send(user)
})
.catch(err => {
res.status(500).send({message:err.message || 'error while finding data'})
})
}
}
exports.update = (req, res) => {
if (!req.body) {
return res.status(400).send({message: 'Data to update is empty'})
}
const id = req.params.id
Userdb.findByIdAndUpdate(id, req.body, {useFindAndModify: false})
.then(data => {
if (!data) {
res.status(404).send({message: `can't update ${id}`})
} else {
res.send(data)
}
})
.catch(err => {
res.status(500).send({message: 'err'})
})
}
exports.delete = (req, res) => {
const id = req.params.id
Userdb.findByIdAndDelete(id)
.then(data => {
if (!data) {
res.status(404).send({message: `${id} is wrong`})
} else {
res.send({message: 'user deleted'})
}
})
.catch(err => {
res.status(500).send({message: `could not delete with ${id}`})
})
}
This is server/routes/router.js
const route = express.Router()
const services = require('../services/render')
const controller = require('../controller/controller')
route.get('/', services.homeRoute)
route.get('/add-book', services.addNewBook)
route.get('/update-book', services.updateBook)
route.get('/updateBookRequest', services.updateBookRequest)
route.get('/delete-book', services.deleteBook)
// API
route.post('/api/books', controller.create)
route.get('/api/books', controller.find)
route.put('/api/books/:id', controller.update)
route.delete('/api/books/:id', controller.delete)
module.exports = route
This is server/services/render.js
const axios = require('axios')
exports.homeRoute = (req, res) => {
axios.get('http://localhost:5000/api/books')
.then(function(response) {
res.render('index', {books: response.data})
})
.catch(err => {
res.send(err)
})
}
exports.addNewBook = (req, res) => {
res.render('add_new_book')
}
exports.updateBook = (req, res) => {
axios.get('http://localhost:5000/api/books', {params:{id:req.query.id}})
.then(function(userdata) {
res.render('update_book', {book:userdata.data})
})
.catch(err => {
res.send(err)
})
}
exports.updateBookRequest = (req, res) => {
console.log(req)
axios.put(`http://localhost:5000/api/books/${req.query.id}`, {book:req.body})
.then(function(response) {
res.redirect('/')
})
.catch(err => {
res.send(err)
})
}
exports.deleteBook = (req, res) => {
axios.delete(`http://localhost:5000/api/books/${req.query.id}`)
.then(function(response) {
console.log('deleted')
res.redirect('/')
})
.catch(err => {
res.send(err)
})
}
This is by far the whole code
You need to use a callback in your update function. Also you can use {new: true} to get the updated document.
Userdb.findByIdAndUpdate(id,
req.body,
{useFindAndModify: false},
{new: true},
function (err, data) {
console.log(data); //check if the document is updated
}
})
Also make sure your req.body is an object. if you are unsure, check req.body in console log

error: SyntaxError: Unexpected token B in JSON at position 0

I am trying to set up a user register form using react and then sending the inputs to the express backend api to register the new user. I keep gettting the error: Register.js:19 POST http://localhost:3000/api/register 400 (Bad Request) and: error: SyntaxError: Unexpected token B in JSON at position 0 on the client side. On the server side I try and console.log(req.body) and only an empty object gets returned.
My react register code
import React from 'react'
class Register extends React.Component {
state = {
name: "",
username: "",
password: ""
}
handleChange = event => {
const { name, value } = event.target
this.setState({[name]:value})
}
handleSubmit = event => {
event.preventDefault();
fetch("/api/register", {
method: "POST",
body: JSON.stringify(this.state),
headers: {
'Content-Type': 'application/json'
}
}).then(res => res.json())
.then(response => console.log("success:", JSON.stringify(response)))
.catch(error => console.log("error:", error));
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<input type="text" name="name" value={this.state.name} placeholder="name" onChange={this.handleChange} />
<input type="text" name="username" value={this.state.username} placeholder="username" onChange={this.handleChange} />
<input type="password" name="password" value={this.state.password} placeholder="password" onChange={this.handleChange} />
<button>Register</button>
</form>
</div>
)
}
}
export default Register
Server side code
const express = require("express"),
passport = require("passport"),
User = require("../models/user");
const router = express.Router({mergeParams: true});
// /api before this
router.post("/register", (req, res)=>{
console.log(req.body);
const newUser = new User({
username: req.body.username,
name: req.body.name
});
User.register(newUser, req.body.password, (err, user)=>{
if(err) {
console.log(err);
}
passport.authenticate("local")(req, res, ()=>{
res.json(user);
});
});
});
module.exports = router;

Pass data from react component to proxy(node server)

I set up a proxy to bypass CORS for the intended api in this react application. I'm having trouble to pass data from react component to proxy(nodeJS server). I've read a few links such as here and here but still have no clues.
/*React component*/
import React, { useState } from "react";
import axios from "axios";
export default function Mail() {
const [emailInput, setEmailInput] = useState('')
const getMail = () => {
axios.get("/list/members").then(json => {
console.log(json.data);
});
};
const subscribe = (email) => {
console.log('inside subscribe')
axios.post("/member", email)
.then(data => console.log('post succeeds'))
.catch(err => console.log(err))
}
const handleSubmit = e => {
e.preventDefault();
const email = {
email_address: `${emailInput}`,
status: "subscribed"
};
subscribe(email)
};
const handleChange = (e) => {
setEmailInput(e.target.value)
}
return (
<form onSubmit={handleSubmit}>
<input type="text" name="email" placeholder="enter your email" value={emailInput} onChange={handleChange}/>
<input type="submit" value="subscribe" />{" "}
</form>
);
}
In node server, I have
app.post("/member", (req, res) => {
const email = {
email_address: "axios1234#gmail.com",
status: "subscribed"
};
axios.post(
"https://<apilink>",
email,
{
withCredentials: true,
auth: {
username: "anystring",
password: "<apikey>"
}
}
).then(json => {
res.send(json.data)
}).catch(err => {
console.log(err);
})
});
I've checked that my conduit between react front end app and proxy server is working. I also examined both req and res in app.post("/member", (req, res) but found thatreq.body is undefined and couldn't find the email object that was passed in from react function component. Did I miss anything here?
Are you using a body-parser? If not, install body-parser and then change your code to this:
const bodyParser = require('body-parser');
app.use(bodyParser.json());
app.post("/member", (req, res) => {
const email = req.body.email_address;
axios.post(
"https://<apilink>",
email,
{
withCredentials: true,
auth: {
username: "anystring",
password: "<apikey>"
}
}
).then(json => {
res.send(json.data)
}).catch(err => {
console.log(err);
})
});

Resources