Set cookie with JWT - NodeJs and Javascript - javascript

I'm a bit lost in my own code right now, I need brightness.
I'm trying to set a cookie with JWT when I log in. Here is my code :
My route :
router.post('/signin', user.signIn);
My controller :
const jwtExpire = 3 * 23 * 60 * 60 * 1000
const createToken = (id) => {
return jwt.sign({ id }, process.env.TOKEN_SECRET, {
expiresIn: jwtExpire
})
}
module.exports.signIn = async (req, res) => {
const { email, password } = req.body
try {
const user = await UserModel.login(email, password)
const token = createToken(user._id)
res.cookie('jwt', token, { httpOnly: true, jwtExpire })
res.status(200).json({ user: user._id })
} catch (err) {
const errors = signInErrors(err)
res.status(200).json({ errors })
}
}
My form :
<form id="signin-block" style="display: block;" class="p-4 mx-auto">
<h2 class="text-center my-4 underline">Se connecter</h2>
<div class="input-group">
<input id="email" type="text" class="form-control" name="email" placeholder="E-mail">
</div>
<div class="input-group">
<input id="password" type="password" class="form-control" name="password"
placeholder="Mot de passe">
</div>
<button type="submit" class="btn btn-primary mt-4" id="submit">Me connecter</button>
</form>
My function to log in :
logInBlock.addEventListener('submit', function (e) {
e.preventDefault()
const email = e.target[0].value
const password = e.target[1].value
axios({
method: 'post',
url: 'http://localhost:5000/api/signin',
data: {
email: email,
password: password
}
}).then((res) => console.log(res))
})
With POSTMAN, the cookie is set. So something is missing is my own request, but I don't see what. Any idea ?

Are you doing this in different origins? Cookies will not be in request if origins are different, it is unsafe and can lead to attacks.
If yes, another way to do is to response your jwt from server, when ajax is responsed, store your jwt in cookie by client side, and use axios property to bring it in request header:
axios.defaults.headers.common['Authorization'] = yourJwt
See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite

Related

How can I implement http-only cookie-JWT authentication using React and Node JS?

I am new to programming and just started full-stack project with NodeJS and React. I read somewhere a while ago that saving JWT tokens inside http-only cookies is better in terms of security than just saving them in localstorage, so I decided to implement that, but don't know how.
This is my Login controller in express
const signIn = async (req: Request, res: Response): Promise<any> => {
const userEmail = req.body.email;
const userPassword = req.body.password;
const user: any = await User.findOne({ email: userEmail }).clone();
const isValid = await user.comparePassword(userPassword);
if (isValid) {
const tokenObject = utils.issueJWT(user);
res.cookie("jwt", tokenObject.token, {
httpOnly: true,
maxAge: tokenObject.expiresIn,
});
res.send(tokenObject.token);
} else
res
.status(401)
.json({ success: false, msg: "You entered the wrong password" });
};
But I don't know how to access stored cookie with React and then authenticate user.
This is my Login component in React
import { SyntheticEvent, useState } from "react";
import { Navigate } from "react-router-dom";
import "bootstrap/dist/css/bootstrap.min.css";
function SignIn() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [navigate, setNavigate] = useState(false);
const submit = async (e: SyntheticEvent) => {
e.preventDefault();
await fetch("http://localhost:8080/login", {
method: "POST",
headers: { "Content-type": "application/json" },
body: JSON.stringify({ email, password }),
credentials: "include",
});
setNavigate(true);
};
if(navigate){
return <Navigate to="/users"/>
}
return (
<form action="/login" method="post" onSubmit={submit}>
<div className="form-outline mb-4">
<input
type="email"
id="form2Example1"
className="form-control"
name="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<label className="form-label" htmlFor="form2Example1">
Email address
</label>
</div>
<div className="form-outline mb-4">
<input
type="password"
id="form2Example2"
className="form-control"
name="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<label className="form-label" htmlFor="form2Example2">
Password
</label>
</div>
<button type="submit" className="btn btn-primary btn-block mb-4">
Sign in
</button>
</form>
);
}
export default SignIn;
Token is really stored inside cookie if checked in inspect or POSTMAN:
postman screenshot
While I worked with EJS forms I had my personal implementation of verifying if the user was authenticated or not and it worked:
const verifyJWT = (req: Request, res: Response, next: NextFunction) => {
const signedToken = req.cookies.jwt;
if (signedToken) {
jwt.verify(
signedToken,
config.PRIV_KEY,
{ algorithms: ["RS256"] },
async (err: any, decodedToken: any) => {
if (err) {
// tslint:disable-next-line:no-console
console.log(err);
} else {
// tslint:disable-next-line:no-console
console.log(decodedToken);
next();
}
}
);
} else {
res.redirect("/login");
}
};
Do I need to implement something similar to this?
You cannot access httpOnly cookie in browser it is just send along to browser so it can then be used in the backend . You can access it in your backend app .
I mean if httpOnly cookie is accessed in browser than it is similar to localStorage . Learn to implement the accessToken and refreshToken approach for Auethntication and authorization.
This is one example:
https://www.geeksforgeeks.org/jwt-authentication-with-refresh-tokens/#:~:text=Since%20access%20tokens%20aren't,in%20a%20very%20short%20duration.

i am making Login/Register System in nodejs and sql and facing a bug

when i enter email and password it and hit register it shows me following error
Cannot destructure property 'email' of 'req.body' as it is undefined.
I want it to be fetched through fetch api and post the data into data base
This is the html form
<form onsubmit = "return false;" id="form">
<div class="mb-3">
<label for="email" class="form-label">Email address</label>
<input type="email" class="form-control" id="email" aria-describedby="emailHelp" autocomplete="off" required>
<div id="emailHelp" class="form-text">We'll never share your email with anyone else.</div>
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" class="form-control" id="password">
</div>
<div class="alert alert-danger" role="alert" id="error" style="display: none;" ></div>
<div class="alert alert-success" role="alert" id="success" style="display: none;" ></div>
<button type="submit" class="btn btn-primary" id="submit">Register</button>
</form>
This is the event listener on submit
form.addEventListener("submit",() => {
const register = {
email: email.value,
password: password.value
}
fetch("/api/register", {
method: "POST",
body: JSON.stringify(register),
headers: {
"Content-Type": "application/json"
}
}).then(res => res.json()).then(
data => {
if(data.status =="error")
{
success.style.display = "none";
error.style.display = "block";
error.innerText = data.error;
}
else
{
success.style.display = "block";
error.style.display = "none";
error.innerText = data.success;
}
}
)
})
This is "/api/register"
const db = require("../routes/db-config");
const bcrypt = require('bcryptjs');
const register = async(req, res) =>{
const email = req.body.email
const Npassword = req.body.password
if(!email || !Npassword) return res.json("Please Enter your email and password");
else {
console.log(email);
db.query('SELECT email FROM data WHERE email = ?',[email], async (err, result)=>{
if (err) throw err;
if(result[0])return res.json({ status: "error", error: "This Email has already Taken"})
else {
const password = bcrypt.hash(Npassword, 8);
console.log(password)
db.query("insert into data SET ?" ,{email:email, password:password},(error, results)=>{
if(error) throw error;
return res.json({status: success, success: "User has been registered"})
})
}
})
}
}
module.exports = register;
The problem was with the:
app.use(express.json())
I had to declare it earlier than the other statements

I created a function that assit to sign up but its return an error that email you enter is wrong?

I created a sign up form which takes the input from user in term of name,email,tag ,But it's return an error in cosole which is (Signup.js:12 POST http://localhost:5000/api/auth/createuser 400 (Bad Request)) the error is you enter wrong email but i enter the write which is not my database in case of any previous user the seniorio comes that you enter wrong email but in the case of new user on app its not possible this kind of error but it came
Here is my client side of code of sign up
import React, { useState } from 'react'
import { useNavigate } from 'react-router-dom';
const Signup = () => {
let navigate = useNavigate();
const [credentials, setCredentials] = useState({ name: "", email: "", password: "" })
const handleSubmit = async (e) => {
const { name, email, password } = credentials;
e.preventDefault();
const response = await fetch("http://localhost:5000/api/auth/createuser", {
method: 'POST', // *GET, POST, PUT, DELETE, etc.
headers: {
'Content-Type': 'application/json',
},
/*body: JSON.stringify({name:credentials.name,email:credentials.email,password:credentials.password})*/
body: JSON.stringify({ name, email, password })
});
const json = await response.json()
console.log(json);
if (json.success) {
//redirect
console.log("success")
localStorage.setItem('token', json.authtoken);
navigate('/');
}
else {
console.log("wrong credentials");
alert("Invalid credentials")
}
}
const onChange = (e) => {
setCredentials({ ...credentials, [e.target.name]: e.target.value })
}
return (
<div className='container'>
<form onSubmit={handleSubmit}>
<div className="mb-3">
<label htmlFor="name" className="form-label">Name</label>
<input type="text" className="form-control" id="name" name="name" aria-describedby="emailHelp" onChange={onChange} />
</div>
<div className="mb-3">
<label htmlFor="email" className="form-label">email</label>
<input type="email" className="form-control" id="email" aria-describedby="emailHelp" onChange={onChange} />
</div>
<div className="mb-3">
<label htmlFor="password" className="form-label">Password</label>
<input type="password" name="password" id="password" className="form-control" onChange={onChange} minLength={5} required />
</div>
<button type="submit" className="btn btn-primary">Submit</button>
</form>
</div>
)
}
export default Signup
Here is my server side of code its work on in thunderclient Api but I added it for your ease that you can easily understand it.
router.post('/createuser',[
body('email','you enter wrong email').isEmail(),
body('name').isLength({ min: 5 }),
body('password').isLength({ min: 5 }),
],async (req,res)=>{
let success=false;
//if there are errors ,return bad request and the error
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ success,errors: errors.array() });
}
//check wheater the user with email exist already
try {
let user=await User.findOne({email:req.body.email});
if(user){
return res.status(400).json({success,error:"Sorry a user already exist with this email"})
}
const salt=await bcrypt.genSalt(10);
const secpass= await bcrypt.hash(req.body.password,salt);
//create a new user
user=await User.create({
name: req.body.name,
email: req.body.email,
password: secpass,
});
//.then(user => res.json(user))
//.catch(err=> {console.log(err)
// res.json( {error:'Please enter a unique value for email'})
const data={
user:{
id:user.id
}
}
const authtoken= jwt.sign(data,JWTSECRET);
// res.json(user);
success=true;
res.json({success,authtoken});
} catch (error) {
console.log(error.message);
res.status(500).send("some error occured");
}
})
As I mention earlie the backend code works because its Api tested from thunder client but issue in client side code

How to get req.params from GET route in POST route

I am struggling with one issue for really long time. I can't 'transfer' req.params.token and req.params.id to the POST route. What I mean:
router.get('/passwordReset/:token/:id', isNotLogin, (req, res) => {
console.log('token: ' + req.params.token)
console.log('id: ' + req.params.id)
Token.findOne({userId: req.params.id}, function (err, id) {
if (!id) {
res.redirect('/confirmError');
} else {
res.render('view/password/passwordReset');
};
});
});
Results from console.log are (e.g.):
token: 934f569631026e396da5b9a952bfsnx72ba1d2187ecd734b47b3aca89640faf3
id: 60e5560119fcb9627sgar810
Example link:
https://localhost:3000/passwordReset/934f569631026e396da5b9a952bfsnx72ba1d2187ecd734b47b3aca89640faf3/60e5560119fcb9627sgar810
But when it comes to the POST route:
router.post('/passwordReset/:token/:id', isNotLogin, async (req, res) => {
console.log('token: ' + req.params.token)
console.log('id: ' + req.params.id)
await resetPassword(req.params.id, req.params.token, req.body.password);
});
The results of console.log are:
token: :token
id: :id
And that's what I can't solve. I've tried many things, but I just can't come to the right solution.
I've already tried to replace router.post('/passwordReset/:token/:id' to router.post('/passwordReset' but it didn't help.
Form for the password reset:
<form action="/passwordReset/:token/:id" method="POST">
<label for="password">Password</label>
<input type="password" name="password" id="password">
<button type="submit">Zmień hasło</button>
</form>
And the resetPassword function:
async function resetPassword(userId, token, password) {
const passwordResetToken = await Token.findOne({userId: userId});
if (!passwordResetToken) {throw new Error("Invalid password reset token");}
const isValid = await bcrypt.compare(token, passwordResetToken.token);
if (!isValid) throw new Error("Invalid password reset token");
const salt = crypto.randomBytes(32).toString('hex');
const hash = crypto.pbkdf2Sync(password, salt, 100000, 64, 'sha512').toString('hex');
await User.updateOne(
{_id: userId},
{$set: {hash: hash}},
{$set: {salt: salt}},
{new: true}
);
const user = await User.findById({_id: userId});
sendEmail(user.username, "Password Reset Successfully", {email}, "./email/template/resetPassword.handlebars");
await passwordResetToken.deleteOne();
return true;
};
The error is:
(node:1811) UnhandledPromiseRejectionWarning: CastError: Cast to ObjectId failed for value ":id" (type string) at path "userId" for model "Token"
Whole code: https://github.com/kbilak/ToDoApp
Send the actual value for token and id to the html page. So your form action should be something like this when you inspect the code on your browser
/passwordReset/934f569631026e396da5b9a952bfsnx72ba1d2187ecd734b47b3aca89640faf3/60e5560119fcb9627sgar810
Using the ejs template format
<form action="/passwordReset/<%= token %>/<%= id %>" method="POST">
<label for="password">Password</label>
<input type="password" name="password" id="password">
<button type="submit">Zmień hasło</button>
</form>
In order to send the value to your html page, you need to add those value when you are rendering the page. Just like you have it here
router.get('/passwordReset/:token/:id', isNotLogin, (req, res) => {
token = req.params.token
id =req.params.id
Token.findOne({userId: req.params.id}, function (err, id) {
if (!id) {
res.redirect('/confirmError');
} else {
res.render('view/password/passwordReset', {
token: token
id: id
} );
};
});
});

After adding a user document to mongo collection; How to show that name on their profile page?

I want a user to be able to create a profile;
then be directed to a page which shows their username.
The user registration part is working correctly:
app.post('/users',
function (req, res) {
var hashedPassword = Users.hashPassword(req.body.Password);
Users.findOne({ Ainm: req.body.Name })
.then(function (user) {
if (user) {
return res.status(400).send(req.body.Name + "Name already in use.");
} else {
Users
.create({
Name: req.body.Name,
Password: hashedPassword,
Email: req.body.Email
})
.then(function (user) {
res.status(201).sendFile(path.join(__dirname, "public", "profile.html"));
}).catch(function (error) {
console.error(error);
res.status(500).send("Error: " + error);
})
}
}).catch(function (error) {
console.error(error);
res.status(500).send("Error: " + error);
});
});
but can anybody suggest how to send their data from the server to the client, in order to show user's name on the profile page to which they are being redirected?
the client side html is like this:
<div id="registration-menu">
<form action="/users" method="post">
<br />
<input type="text" name="Ainm" placeholder="Ainm">
<br />
<input type="password" name="Password" placeholder="Passfhocal" >
<br />
<input type="email" name="Email" id="email" placeholder="seoladh r-post">
<br />
<br />
<input class="form-check-input" type="checkbox" value="" id="defaultCheck1">
<label class="form-check-label" for="defaultCheck1">
Nuachtlitir
</label>
<br />
<input type="submit" class="btn btn-success btn-block" id="register" value="cláraigh">
</form>
You can use ,
res.redirect('/users/${req.body.Name}')
and setup a new route to display user details , using their names as id to figure out other details (duplicate names will cause conflict).
Use jquery's .ajax() to send the data from clientside to server side, as previously:
$('#btnSendMsg').click(function () {
$.ajax('/addMsg', {
type: 'POST', // http method
data: { myData: usrMsg }, // data to submit
success: function () {
usrMsg = $('#output').html()
// alert(usrMsg)
$('#output').fadeOut();
setTimeout(function () {
$('#output').empty();
$('#output').fadeIn();
}, 500)
}
});
});
The server requires a mongoose schema to liase with db. Components should have a few run-throughs implementing data storage.
Respond from server with .json representing eventual db.
>//client-side.js
> let gotIt = [];
> $.ajax('urlOfData', { dataType: 'json' }).then(function
> (response) { gotIt = response; //
> alert(JSON.stringify(response)) })

Categories